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   return;
113 }
114 
115 bool ProfiledBinary::inlineContextEqual(uint64_t Address1,
116                                         uint64_t Address2) const {
117   uint64_t Offset1 = virtualAddrToOffset(Address1);
118   uint64_t Offset2 = virtualAddrToOffset(Address2);
119   const FrameLocationStack &Context1 = getFrameLocationStack(Offset1);
120   const FrameLocationStack &Context2 = getFrameLocationStack(Offset2);
121   if (Context1.size() != Context2.size())
122     return false;
123 
124   // The leaf frame contains location within the leaf, and it
125   // needs to be remove that as it's not part of the calling context
126   return std::equal(Context1.begin(), Context1.begin() + Context1.size() - 1,
127                     Context2.begin(), Context2.begin() + Context2.size() - 1);
128 }
129 
130 std::string
131 ProfiledBinary::getExpandedContextStr(const std::list<uint64_t> &Stack) const {
132   std::string ContextStr;
133   SmallVector<std::string, 8> ContextVec;
134   // Process from frame root to leaf
135   for (auto Iter = Stack.rbegin(); Iter != Stack.rend(); Iter++) {
136     uint64_t Offset = virtualAddrToOffset(*Iter);
137     const FrameLocationStack &ExpandedContext = getFrameLocationStack(Offset);
138     for (const auto &Loc : ExpandedContext) {
139       ContextVec.push_back(getCallSite(Loc));
140     }
141   }
142 
143   assert(ContextVec.size() && "Context length should be at least 1");
144 
145   std::ostringstream OContextStr;
146   for (uint32_t I = 0; I < (uint32_t)ContextVec.size(); I++) {
147     if (OContextStr.str().size()) {
148       OContextStr << " @ ";
149     }
150 
151     if (I == ContextVec.size() - 1) {
152       // Only keep the function name for the leaf frame
153       StringRef Ref(ContextVec[I]);
154       OContextStr << Ref.split(":").first.str();
155     } else {
156       OContextStr << ContextVec[I];
157     }
158   }
159   return OContextStr.str();
160 }
161 
162 void ProfiledBinary::setPreferredBaseAddress(const ELFObjectFileBase *Obj) {
163   for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
164        SI != SE; ++SI) {
165     const SectionRef &Section = *SI;
166     if (Section.isText()) {
167       PreferredBaseAddress = getELFImageLMAForSec(Section);
168       return;
169     }
170   }
171   exitWithError("no text section found", Obj->getFileName());
172 }
173 
174 void ProfiledBinary::decodePseudoProbe(const ELFObjectFileBase *Obj) {
175   StringRef FileName = Obj->getFileName();
176   for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
177        SI != SE; ++SI) {
178     const SectionRef &Section = *SI;
179     StringRef SectionName = unwrapOrError(Section.getName(), FileName);
180 
181     if (SectionName == ".pseudo_probe_desc") {
182       StringRef Contents = unwrapOrError(Section.getContents(), FileName);
183       ProbeDecoder.buildGUID2FuncDescMap(
184           reinterpret_cast<const uint8_t *>(Contents.data()), Contents.size());
185     } else if (SectionName == ".pseudo_probe") {
186       StringRef Contents = unwrapOrError(Section.getContents(), FileName);
187       ProbeDecoder.buildAddress2ProbeMap(
188           reinterpret_cast<const uint8_t *>(Contents.data()), Contents.size());
189       // set UsePseudoProbes flag, used for PerfReader
190       UsePseudoProbes = true;
191     }
192   }
193 
194   if (ShowPseudoProbe)
195     ProbeDecoder.printGUID2FuncDescMap(outs());
196 }
197 
198 bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
199                                         SectionSymbolsTy &Symbols,
200                                         const SectionRef &Section) {
201 
202   std::size_t SE = Symbols.size();
203   uint64_t SectionOffset = Section.getAddress() - PreferredBaseAddress;
204   uint64_t SectSize = Section.getSize();
205   uint64_t StartOffset = Symbols[SI].Addr - PreferredBaseAddress;
206   uint64_t EndOffset = (SI + 1 < SE)
207                            ? Symbols[SI + 1].Addr - PreferredBaseAddress
208                            : SectionOffset + SectSize;
209   if (StartOffset >= EndOffset)
210     return true;
211 
212   std::string &&SymbolName = Symbols[SI].Name.str();
213   if (ShowDisassembly)
214     outs() << '<' << SymbolName << ">:\n";
215 
216   uint64_t Offset = StartOffset;
217   while (Offset < EndOffset) {
218     MCInst Inst;
219     uint64_t Size;
220     // Disassemble an instruction.
221     if (!DisAsm->getInstruction(Inst, Size, Bytes.slice(Offset - SectionOffset),
222                                 Offset + PreferredBaseAddress, nulls()))
223       return false;
224 
225     if (ShowDisassembly) {
226       if (ShowPseudoProbe) {
227         ProbeDecoder.printProbeForAddress(outs(),
228                                           Offset + PreferredBaseAddress);
229       }
230       outs() << format("%8" PRIx64 ":", Offset);
231       size_t Start = outs().tell();
232       IPrinter->printInst(&Inst, Offset + Size, "", *STI.get(), outs());
233       if (ShowSourceLocations) {
234         unsigned Cur = outs().tell() - Start;
235         if (Cur < 40)
236           outs().indent(40 - Cur);
237         InstructionPointer Inst(this, Offset);
238         outs() << getReversedLocWithContext(symbolize(Inst));
239       }
240       outs() << "\n";
241     }
242 
243     const MCInstrDesc &MCDesc = MII->get(Inst.getOpcode());
244 
245     // Populate a vector of the symbolized callsite at this location
246     InstructionPointer IP(this, Offset);
247     Offset2LocStackMap[Offset] = symbolize(IP, true);
248 
249     // Populate address maps.
250     CodeAddrs.push_back(Offset);
251     if (MCDesc.isCall())
252       CallAddrs.insert(Offset);
253     else if (MCDesc.isReturn())
254       RetAddrs.insert(Offset);
255 
256     Offset += Size;
257   }
258 
259   if (ShowDisassembly)
260     outs() << "\n";
261 
262   FuncStartAddrMap[StartOffset] = Symbols[SI].Name.str();
263   return true;
264 }
265 
266 void ProfiledBinary::setUpDisassembler(const ELFObjectFileBase *Obj) {
267   const Target *TheTarget = getTarget(Obj);
268   std::string TripleName = TheTriple.getTriple();
269   StringRef FileName = Obj->getFileName();
270 
271   MRI.reset(TheTarget->createMCRegInfo(TripleName));
272   if (!MRI)
273     exitWithError("no register info for target " + TripleName, FileName);
274 
275   MCTargetOptions MCOptions;
276   AsmInfo.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
277   if (!AsmInfo)
278     exitWithError("no assembly info for target " + TripleName, FileName);
279 
280   SubtargetFeatures Features = Obj->getFeatures();
281   STI.reset(
282       TheTarget->createMCSubtargetInfo(TripleName, "", Features.getString()));
283   if (!STI)
284     exitWithError("no subtarget info for target " + TripleName, FileName);
285 
286   MII.reset(TheTarget->createMCInstrInfo());
287   if (!MII)
288     exitWithError("no instruction info for target " + TripleName, FileName);
289 
290   MCObjectFileInfo MOFI;
291   MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI);
292   MOFI.InitMCObjectFileInfo(Triple(TripleName), false, Ctx);
293   DisAsm.reset(TheTarget->createMCDisassembler(*STI, Ctx));
294   if (!DisAsm)
295     exitWithError("no disassembler for target " + TripleName, FileName);
296 
297   MIA.reset(TheTarget->createMCInstrAnalysis(MII.get()));
298 
299   int AsmPrinterVariant = AsmInfo->getAssemblerDialect();
300   IPrinter.reset(TheTarget->createMCInstPrinter(
301       Triple(TripleName), AsmPrinterVariant, *AsmInfo, *MII, *MRI));
302   IPrinter->setPrintBranchImmAsAddress(true);
303 }
304 
305 void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) {
306   // Set up disassembler and related components.
307   setUpDisassembler(Obj);
308 
309   // Create a mapping from virtual address to symbol name. The symbols in text
310   // sections are the candidates to dissassemble.
311   std::map<SectionRef, SectionSymbolsTy> AllSymbols;
312   StringRef FileName = Obj->getFileName();
313   for (const SymbolRef &Symbol : Obj->symbols()) {
314     const uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName);
315     const StringRef Name = unwrapOrError(Symbol.getName(), FileName);
316     section_iterator SecI = unwrapOrError(Symbol.getSection(), FileName);
317     if (SecI != Obj->section_end())
318       AllSymbols[*SecI].push_back(SymbolInfoTy(Addr, Name, ELF::STT_NOTYPE));
319   }
320 
321   // Sort all the symbols. Use a stable sort to stabilize the output.
322   for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols)
323     stable_sort(SecSyms.second);
324 
325   if (ShowDisassembly)
326     outs() << "\nDisassembly of " << FileName << ":\n";
327 
328   // Dissassemble a text section.
329   for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
330        SI != SE; ++SI) {
331     const SectionRef &Section = *SI;
332     if (!Section.isText())
333       continue;
334 
335     uint64_t ImageLoadAddr = PreferredBaseAddress;
336     uint64_t SectionOffset = Section.getAddress() - ImageLoadAddr;
337     uint64_t SectSize = Section.getSize();
338     if (!SectSize)
339       continue;
340 
341     // Register the text section.
342     TextSections.insert({SectionOffset, SectSize});
343 
344     if (ShowDisassembly) {
345       StringRef SectionName = unwrapOrError(Section.getName(), FileName);
346       outs() << "\nDisassembly of section " << SectionName;
347       outs() << " [" << format("0x%" PRIx64, SectionOffset) << ", "
348              << format("0x%" PRIx64, SectionOffset + SectSize) << "]:\n\n";
349     }
350 
351     // Get the section data.
352     ArrayRef<uint8_t> Bytes =
353         arrayRefFromStringRef(unwrapOrError(Section.getContents(), FileName));
354 
355     // Get the list of all the symbols in this section.
356     SectionSymbolsTy &Symbols = AllSymbols[Section];
357 
358     // Disassemble symbol by symbol.
359     for (std::size_t SI = 0, SE = Symbols.size(); SI != SE; ++SI) {
360       if (!dissassembleSymbol(SI, Bytes, Symbols, Section))
361         exitWithError("disassembling error", FileName);
362     }
363   }
364 }
365 
366 void ProfiledBinary::setupSymbolizer() {
367   symbolize::LLVMSymbolizer::Options SymbolizerOpts;
368   SymbolizerOpts.PrintFunctions =
369       DILineInfoSpecifier::FunctionNameKind::LinkageName;
370   SymbolizerOpts.Demangle = false;
371   SymbolizerOpts.DefaultArch = TheTriple.getArchName().str();
372   SymbolizerOpts.UseSymbolTable = false;
373   SymbolizerOpts.RelativeAddresses = false;
374   Symbolizer = std::make_unique<symbolize::LLVMSymbolizer>(SymbolizerOpts);
375 }
376 
377 FrameLocationStack ProfiledBinary::symbolize(const InstructionPointer &IP,
378                                              bool UseCanonicalFnName) {
379   assert(this == IP.Binary &&
380          "Binary should only symbolize its own instruction");
381   auto Addr = object::SectionedAddress{IP.Offset + PreferredBaseAddress,
382                                        object::SectionedAddress::UndefSection};
383   DIInliningInfo InlineStack =
384       unwrapOrError(Symbolizer->symbolizeInlinedCode(Path, Addr), getName());
385 
386   FrameLocationStack CallStack;
387 
388   for (int32_t I = InlineStack.getNumberOfFrames() - 1; I >= 0; I--) {
389     const auto &CallerFrame = InlineStack.getFrame(I);
390     if (CallerFrame.FunctionName == "<invalid>")
391       break;
392     StringRef FunctionName(CallerFrame.FunctionName);
393     if (UseCanonicalFnName)
394       FunctionName = FunctionSamples::getCanonicalFnName(FunctionName);
395     LineLocation Line(CallerFrame.Line - CallerFrame.StartLine,
396                       CallerFrame.Discriminator);
397     FrameLocation Callsite(FunctionName.str(), Line);
398     CallStack.push_back(Callsite);
399   }
400 
401   return CallStack;
402 }
403 
404 InstructionPointer::InstructionPointer(ProfiledBinary *Binary, uint64_t Address,
405                                        bool RoundToNext)
406     : Binary(Binary), Address(Address) {
407   Index = Binary->getIndexForAddr(Address);
408   if (RoundToNext) {
409     // we might get address which is not the code
410     // it should round to the next valid address
411     this->Address = Binary->getAddressforIndex(Index);
412   }
413 }
414 
415 void InstructionPointer::advance() {
416   Index++;
417   Address = Binary->getAddressforIndex(Index);
418 }
419 
420 void InstructionPointer::backward() {
421   Index--;
422   Address = Binary->getAddressforIndex(Index);
423 }
424 
425 void InstructionPointer::update(uint64_t Addr) {
426   Address = Addr;
427   Index = Binary->getIndexForAddr(Address);
428 }
429 
430 } // end namespace sampleprof
431 } // end namespace llvm
432