1 //===-- ProfiledBinary.cpp - Binary decoder ---------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "ProfiledBinary.h"
10 #include "ErrorHandling.h"
11 #include "llvm/ADT/Triple.h"
12 #include "llvm/Demangle/Demangle.h"
13 #include "llvm/Support/CommandLine.h"
14 #include "llvm/Support/Format.h"
15 #include "llvm/Support/TargetRegistry.h"
16 #include "llvm/Support/TargetSelect.h"
17 
18 #define DEBUG_TYPE "load-binary"
19 
20 using namespace llvm;
21 using namespace sampleprof;
22 
23 static cl::opt<bool> ShowDisassembly("show-disassembly", cl::ReallyHidden,
24                                      cl::init(false), cl::ZeroOrMore,
25                                      cl::desc("Print disassembled code."));
26 
27 static cl::opt<bool> ShowSourceLocations("show-source-locations",
28                                          cl::ReallyHidden, cl::init(false),
29                                          cl::ZeroOrMore,
30                                          cl::desc("Print source locations."));
31 
32 static cl::opt<bool> ShowPseudoProbe(
33     "show-pseudo-probe", cl::ReallyHidden, cl::init(false), cl::ZeroOrMore,
34     cl::desc("Print pseudo probe section and disassembled info."));
35 
36 namespace llvm {
37 namespace sampleprof {
38 
39 static const Target *getTarget(const ObjectFile *Obj) {
40   Triple TheTriple = Obj->makeTriple();
41   std::string Error;
42   std::string ArchName;
43   const Target *TheTarget =
44       TargetRegistry::lookupTarget(ArchName, TheTriple, Error);
45   if (!TheTarget)
46     exitWithError(Error, Obj->getFileName());
47   return TheTarget;
48 }
49 
50 template <class ELFT>
51 static uint64_t getELFImageLMAForSec(const ELFFile<ELFT> &Obj,
52                                      const object::ELFSectionRef &Sec,
53                                      StringRef FileName) {
54   // Search for a PT_LOAD segment containing the requested section. Return this
55   // segment's p_addr as the image load address for the section.
56   const auto &PhdrRange = unwrapOrError(Obj.program_headers(), FileName);
57   for (const typename ELFT::Phdr &Phdr : PhdrRange)
58     if ((Phdr.p_type == ELF::PT_LOAD) && (Phdr.p_vaddr <= Sec.getAddress()) &&
59         (Phdr.p_vaddr + Phdr.p_memsz > Sec.getAddress()))
60       // Segments will always be loaded at a page boundary.
61       return Phdr.p_paddr & ~(Phdr.p_align - 1U);
62   return 0;
63 }
64 
65 // Get the image load address for a specific section. Note that an image is
66 // loaded by segments (a group of sections) and segments may not be consecutive
67 // in memory.
68 static uint64_t getELFImageLMAForSec(const object::ELFSectionRef &Sec) {
69   if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Sec.getObject()))
70     return getELFImageLMAForSec(ELFObj->getELFFile(), Sec,
71                                 ELFObj->getFileName());
72   else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Sec.getObject()))
73     return getELFImageLMAForSec(ELFObj->getELFFile(), Sec,
74                                 ELFObj->getFileName());
75   else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Sec.getObject()))
76     return getELFImageLMAForSec(ELFObj->getELFFile(), Sec,
77                                 ELFObj->getFileName());
78   const auto *ELFObj = cast<ELF64BEObjectFile>(Sec.getObject());
79   return getELFImageLMAForSec(ELFObj->getELFFile(), Sec, ELFObj->getFileName());
80 }
81 
82 void ProfiledBinary::load() {
83   // Attempt to open the binary.
84   OwningBinary<Binary> OBinary = unwrapOrError(createBinary(Path), Path);
85   Binary &Binary = *OBinary.getBinary();
86 
87   auto *Obj = dyn_cast<ELFObjectFileBase>(&Binary);
88   if (!Obj)
89     exitWithError("not a valid Elf image", Path);
90 
91   TheTriple = Obj->makeTriple();
92   // Current only support X86
93   if (!TheTriple.isX86())
94     exitWithError("unsupported target", TheTriple.getTriple());
95   LLVM_DEBUG(dbgs() << "Loading " << Path << "\n");
96 
97   // Find the preferred base address for text sections.
98   setPreferredBaseAddress(Obj);
99 
100   // Decode pseudo probe related section
101   decodePseudoProbe(Obj);
102 
103   // Disassemble the text sections.
104   disassemble(Obj);
105 
106   // Use function start and return address to infer prolog and epilog
107   ProEpilogTracker.inferPrologOffsets(FuncStartAddrMap);
108   ProEpilogTracker.inferEpilogOffsets(RetAddrs);
109 
110   // TODO: decode other sections.
111 }
112 
113 bool ProfiledBinary::inlineContextEqual(uint64_t Address1,
114                                         uint64_t Address2) const {
115   uint64_t Offset1 = virtualAddrToOffset(Address1);
116   uint64_t Offset2 = virtualAddrToOffset(Address2);
117   const FrameLocationStack &Context1 = getFrameLocationStack(Offset1);
118   const FrameLocationStack &Context2 = getFrameLocationStack(Offset2);
119   if (Context1.size() != Context2.size())
120     return false;
121 
122   // The leaf frame contains location within the leaf, and it
123   // needs to be remove that as it's not part of the calling context
124   return std::equal(Context1.begin(), Context1.begin() + Context1.size() - 1,
125                     Context2.begin(), Context2.begin() + Context2.size() - 1);
126 }
127 
128 std::string
129 ProfiledBinary::getExpandedContextStr(const std::list<uint64_t> &Stack) const {
130   std::string ContextStr;
131   SmallVector<std::string, 8> ContextVec;
132   // Process from frame root to leaf
133   for (auto Iter = Stack.rbegin(); Iter != Stack.rend(); Iter++) {
134     uint64_t Offset = virtualAddrToOffset(*Iter);
135     const FrameLocationStack &ExpandedContext = getFrameLocationStack(Offset);
136     for (const auto &Loc : ExpandedContext) {
137       ContextVec.push_back(getCallSite(Loc));
138     }
139   }
140 
141   assert(ContextVec.size() && "Context length should be at least 1");
142 
143   std::ostringstream OContextStr;
144   for (uint32_t I = 0; I < (uint32_t)ContextVec.size(); I++) {
145     if (OContextStr.str().size()) {
146       OContextStr << " @ ";
147     }
148 
149     if (I == ContextVec.size() - 1) {
150       // Only keep the function name for the leaf frame
151       StringRef Ref(ContextVec[I]);
152       OContextStr << Ref.split(":").first.str();
153     } else {
154       OContextStr << ContextVec[I];
155     }
156   }
157   return OContextStr.str();
158 }
159 
160 void ProfiledBinary::setPreferredBaseAddress(const ELFObjectFileBase *Obj) {
161   for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
162        SI != SE; ++SI) {
163     const SectionRef &Section = *SI;
164     if (Section.isText()) {
165       PreferredBaseAddress = getELFImageLMAForSec(Section);
166       return;
167     }
168   }
169   exitWithError("no text section found", Obj->getFileName());
170 }
171 
172 void ProfiledBinary::decodePseudoProbe(const ELFObjectFileBase *Obj) {
173   StringRef FileName = Obj->getFileName();
174   for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
175        SI != SE; ++SI) {
176     const SectionRef &Section = *SI;
177     StringRef SectionName = unwrapOrError(Section.getName(), FileName);
178 
179     if (SectionName == ".pseudo_probe_desc") {
180       StringRef Contents = unwrapOrError(Section.getContents(), FileName);
181       ProbeDecoder.buildGUID2FuncDescMap(
182           reinterpret_cast<const uint8_t *>(Contents.data()), Contents.size());
183     } else if (SectionName == ".pseudo_probe") {
184       StringRef Contents = unwrapOrError(Section.getContents(), FileName);
185       ProbeDecoder.buildAddress2ProbeMap(
186           reinterpret_cast<const uint8_t *>(Contents.data()), Contents.size());
187       // set UsePseudoProbes flag, used for PerfReader
188       UsePseudoProbes = true;
189     }
190   }
191 
192   if (ShowPseudoProbe)
193     ProbeDecoder.printGUID2FuncDescMap(outs());
194 }
195 
196 bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
197                                         SectionSymbolsTy &Symbols,
198                                         const SectionRef &Section) {
199 
200   std::size_t SE = Symbols.size();
201   uint64_t SectionOffset = Section.getAddress() - PreferredBaseAddress;
202   uint64_t SectSize = Section.getSize();
203   uint64_t StartOffset = Symbols[SI].Addr - PreferredBaseAddress;
204   uint64_t EndOffset = (SI + 1 < SE)
205                            ? Symbols[SI + 1].Addr - PreferredBaseAddress
206                            : SectionOffset + SectSize;
207   if (StartOffset >= EndOffset)
208     return true;
209 
210   std::string &&SymbolName = Symbols[SI].Name.str();
211   if (ShowDisassembly)
212     outs() << '<' << SymbolName << ">:\n";
213 
214   uint64_t Offset = StartOffset;
215   while (Offset < EndOffset) {
216     MCInst Inst;
217     uint64_t Size;
218     // Disassemble an instruction.
219     if (!DisAsm->getInstruction(Inst, Size, Bytes.slice(Offset - SectionOffset),
220                                 Offset + PreferredBaseAddress, nulls()))
221       return false;
222 
223     if (ShowDisassembly) {
224       if (ShowPseudoProbe) {
225         ProbeDecoder.printProbeForAddress(outs(),
226                                           Offset + PreferredBaseAddress);
227       }
228       outs() << format("%8" PRIx64 ":", Offset);
229       size_t Start = outs().tell();
230       IPrinter->printInst(&Inst, Offset + Size, "", *STI.get(), outs());
231       if (ShowSourceLocations) {
232         unsigned Cur = outs().tell() - Start;
233         if (Cur < 40)
234           outs().indent(40 - Cur);
235         InstructionPointer Inst(this, Offset);
236         outs() << getReversedLocWithContext(symbolize(Inst));
237       }
238       outs() << "\n";
239     }
240 
241     const MCInstrDesc &MCDesc = MII->get(Inst.getOpcode());
242 
243     // Populate a vector of the symbolized callsite at this location
244     InstructionPointer IP(this, Offset);
245     Offset2LocStackMap[Offset] = symbolize(IP, true);
246 
247     // Populate address maps.
248     CodeAddrs.push_back(Offset);
249     if (MCDesc.isCall())
250       CallAddrs.insert(Offset);
251     else if (MCDesc.isReturn())
252       RetAddrs.insert(Offset);
253 
254     Offset += Size;
255   }
256 
257   if (ShowDisassembly)
258     outs() << "\n";
259 
260   FuncStartAddrMap[StartOffset] = Symbols[SI].Name.str();
261   return true;
262 }
263 
264 void ProfiledBinary::setUpDisassembler(const ELFObjectFileBase *Obj) {
265   const Target *TheTarget = getTarget(Obj);
266   std::string TripleName = TheTriple.getTriple();
267   StringRef FileName = Obj->getFileName();
268 
269   MRI.reset(TheTarget->createMCRegInfo(TripleName));
270   if (!MRI)
271     exitWithError("no register info for target " + TripleName, FileName);
272 
273   MCTargetOptions MCOptions;
274   AsmInfo.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
275   if (!AsmInfo)
276     exitWithError("no assembly info for target " + TripleName, FileName);
277 
278   SubtargetFeatures Features = Obj->getFeatures();
279   STI.reset(
280       TheTarget->createMCSubtargetInfo(TripleName, "", Features.getString()));
281   if (!STI)
282     exitWithError("no subtarget info for target " + TripleName, FileName);
283 
284   MII.reset(TheTarget->createMCInstrInfo());
285   if (!MII)
286     exitWithError("no instruction info for target " + TripleName, FileName);
287 
288   MCObjectFileInfo MOFI;
289   MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI);
290   MOFI.InitMCObjectFileInfo(Triple(TripleName), false, Ctx);
291   DisAsm.reset(TheTarget->createMCDisassembler(*STI, Ctx));
292   if (!DisAsm)
293     exitWithError("no disassembler for target " + TripleName, FileName);
294 
295   MIA.reset(TheTarget->createMCInstrAnalysis(MII.get()));
296 
297   int AsmPrinterVariant = AsmInfo->getAssemblerDialect();
298   IPrinter.reset(TheTarget->createMCInstPrinter(
299       Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI));
300   IPrinter->setPrintBranchImmAsAddress(true);
301 }
302 
303 void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) {
304   // Set up disassembler and related components.
305   setUpDisassembler(Obj);
306 
307   // Create a mapping from virtual address to symbol name. The symbols in text
308   // sections are the candidates to dissassemble.
309   std::map<SectionRef, SectionSymbolsTy> AllSymbols;
310   StringRef FileName = Obj->getFileName();
311   for (const SymbolRef &Symbol : Obj->symbols()) {
312     const uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName);
313     const StringRef Name = unwrapOrError(Symbol.getName(), FileName);
314     section_iterator SecI = unwrapOrError(Symbol.getSection(), FileName);
315     if (SecI != Obj->section_end())
316       AllSymbols[*SecI].push_back(SymbolInfoTy(Addr, Name, ELF::STT_NOTYPE));
317   }
318 
319   // Sort all the symbols. Use a stable sort to stabilize the output.
320   for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols)
321     stable_sort(SecSyms.second);
322 
323   if (ShowDisassembly)
324     outs() << "\nDisassembly of " << FileName << ":\n";
325 
326   // Dissassemble a text section.
327   for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
328        SI != SE; ++SI) {
329     const SectionRef &Section = *SI;
330     if (!Section.isText())
331       continue;
332 
333     uint64_t ImageLoadAddr = PreferredBaseAddress;
334     uint64_t SectionOffset = Section.getAddress() - ImageLoadAddr;
335     uint64_t SectSize = Section.getSize();
336     if (!SectSize)
337       continue;
338 
339     // Register the text section.
340     TextSections.insert({SectionOffset, SectSize});
341 
342     if (ShowDisassembly) {
343       StringRef SectionName = unwrapOrError(Section.getName(), FileName);
344       outs() << "\nDisassembly of section " << SectionName;
345       outs() << " [" << format("0x%" PRIx64, SectionOffset) << ", "
346              << format("0x%" PRIx64, SectionOffset + SectSize) << "]:\n\n";
347     }
348 
349     // Get the section data.
350     ArrayRef<uint8_t> Bytes =
351         arrayRefFromStringRef(unwrapOrError(Section.getContents(), FileName));
352 
353     // Get the list of all the symbols in this section.
354     SectionSymbolsTy &Symbols = AllSymbols[Section];
355 
356     // Disassemble symbol by symbol.
357     for (std::size_t SI = 0, SE = Symbols.size(); SI != SE; ++SI) {
358       if (!dissassembleSymbol(SI, Bytes, Symbols, Section))
359         exitWithError("disassembling error", FileName);
360     }
361   }
362 }
363 
364 void ProfiledBinary::setupSymbolizer() {
365   symbolize::LLVMSymbolizer::Options SymbolizerOpts;
366   SymbolizerOpts.PrintFunctions =
367       DILineInfoSpecifier::FunctionNameKind::LinkageName;
368   SymbolizerOpts.Demangle = false;
369   SymbolizerOpts.DefaultArch = TheTriple.getArchName().str();
370   SymbolizerOpts.UseSymbolTable = false;
371   SymbolizerOpts.RelativeAddresses = false;
372   Symbolizer = std::make_unique<symbolize::LLVMSymbolizer>(SymbolizerOpts);
373 }
374 
375 FrameLocationStack ProfiledBinary::symbolize(const InstructionPointer &IP,
376                                              bool UseCanonicalFnName) {
377   assert(this == IP.Binary &&
378          "Binary should only symbolize its own instruction");
379   auto Addr = object::SectionedAddress{IP.Offset + PreferredBaseAddress,
380                                        object::SectionedAddress::UndefSection};
381   DIInliningInfo InlineStack =
382       unwrapOrError(Symbolizer->symbolizeInlinedCode(Path, Addr), getName());
383 
384   FrameLocationStack CallStack;
385 
386   for (int32_t I = InlineStack.getNumberOfFrames() - 1; I >= 0; I--) {
387     const auto &CallerFrame = InlineStack.getFrame(I);
388     if (CallerFrame.FunctionName == "<invalid>")
389       break;
390     StringRef FunctionName(CallerFrame.FunctionName);
391     if (UseCanonicalFnName)
392       FunctionName = FunctionSamples::getCanonicalFnName(FunctionName);
393     LineLocation Line(CallerFrame.Line - CallerFrame.StartLine,
394                       CallerFrame.Discriminator);
395     FrameLocation Callsite(FunctionName.str(), Line);
396     CallStack.push_back(Callsite);
397   }
398 
399   return CallStack;
400 }
401 
402 InstructionPointer::InstructionPointer(ProfiledBinary *Binary, uint64_t Address,
403                                        bool RoundToNext)
404     : Binary(Binary), Address(Address) {
405   Index = Binary->getIndexForAddr(Address);
406   if (RoundToNext) {
407     // we might get address which is not the code
408     // it should round to the next valid address
409     this->Address = Binary->getAddressforIndex(Index);
410   }
411 }
412 
413 void InstructionPointer::advance() {
414   Index++;
415   Address = Binary->getAddressforIndex(Index);
416 }
417 
418 void InstructionPointer::backward() {
419   Index--;
420   Address = Binary->getAddressforIndex(Index);
421 }
422 
423 void InstructionPointer::update(uint64_t Addr) {
424   Address = Addr;
425   Index = Binary->getIndexForAddr(Address);
426 }
427 
428 } // end namespace sampleprof
429 } // end namespace llvm
430