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 
22 static cl::opt<bool> ShowDisassembly("show-disassembly", cl::ReallyHidden,
23                                      cl::init(false), cl::ZeroOrMore,
24                                      cl::desc("Print disassembled code."));
25 
26 static cl::opt<bool> ShowSourceLocations("show-source-locations",
27                                          cl::ReallyHidden, cl::init(false),
28                                          cl::ZeroOrMore,
29                                          cl::desc("Print source locations."));
30 
31 namespace llvm {
32 namespace sampleprof {
33 
34 static const Target *getTarget(const ObjectFile *Obj) {
35   Triple TheTriple = Obj->makeTriple();
36   std::string Error;
37   std::string ArchName;
38   const Target *TheTarget =
39       TargetRegistry::lookupTarget(ArchName, TheTriple, Error);
40   if (!TheTarget)
41     exitWithError(Error, Obj->getFileName());
42   return TheTarget;
43 }
44 
45 template <class ELFT>
46 static uint64_t getELFImageLMAForSec(const ELFFile<ELFT> &Obj,
47                                      const object::ELFSectionRef &Sec,
48                                      StringRef FileName) {
49   // Search for a PT_LOAD segment containing the requested section. Return this
50   // segment's p_addr as the image load address for the section.
51   const auto &PhdrRange = unwrapOrError(Obj.program_headers(), FileName);
52   for (const typename ELFT::Phdr &Phdr : PhdrRange)
53     if ((Phdr.p_type == ELF::PT_LOAD) && (Phdr.p_vaddr <= Sec.getAddress()) &&
54         (Phdr.p_vaddr + Phdr.p_memsz > Sec.getAddress()))
55       // Segments will always be loaded at a page boundary.
56       return Phdr.p_paddr & ~(Phdr.p_align - 1U);
57   return 0;
58 }
59 
60 // Get the image load address for a specific section. Note that an image is
61 // loaded by segments (a group of sections) and segments may not be consecutive
62 // in memory.
63 static uint64_t getELFImageLMAForSec(const object::ELFSectionRef &Sec) {
64   if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Sec.getObject()))
65     return getELFImageLMAForSec(ELFObj->getELFFile(), Sec,
66                                 ELFObj->getFileName());
67   else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Sec.getObject()))
68     return getELFImageLMAForSec(ELFObj->getELFFile(), Sec,
69                                 ELFObj->getFileName());
70   else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Sec.getObject()))
71     return getELFImageLMAForSec(ELFObj->getELFFile(), Sec,
72                                 ELFObj->getFileName());
73   const auto *ELFObj = cast<ELF64BEObjectFile>(Sec.getObject());
74   return getELFImageLMAForSec(ELFObj->getELFFile(), Sec, ELFObj->getFileName());
75 }
76 
77 void ProfiledBinary::load() {
78   // Attempt to open the binary.
79   OwningBinary<Binary> OBinary = unwrapOrError(createBinary(Path), Path);
80   Binary &Binary = *OBinary.getBinary();
81 
82   auto *Obj = dyn_cast<ELFObjectFileBase>(&Binary);
83   if (!Obj)
84     exitWithError("not a valid Elf image", Path);
85 
86   TheTriple = Obj->makeTriple();
87   // Current only support X86
88   if (!TheTriple.isX86())
89     exitWithError("unsupported target", TheTriple.getTriple());
90   LLVM_DEBUG(dbgs() << "Loading " << Path << "\n");
91 
92   // Find the preferred base address for text sections.
93   setPreferredBaseAddress(Obj);
94 
95   // Disassemble the text sections.
96   disassemble(Obj);
97 
98   // TODO: decode other sections.
99 
100   return;
101 }
102 
103 void ProfiledBinary::setPreferredBaseAddress(const ELFObjectFileBase *Obj) {
104   for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
105        SI != SE; ++SI) {
106     const SectionRef &Section = *SI;
107     if (Section.isText()) {
108       PreferredBaseAddress = getELFImageLMAForSec(Section);
109       return;
110     }
111   }
112   exitWithError("no text section found", Obj->getFileName());
113 }
114 
115 bool ProfiledBinary::dissassembleSymbol(std::size_t SI, ArrayRef<uint8_t> Bytes,
116                                         SectionSymbolsTy &Symbols,
117                                         const SectionRef &Section) {
118 
119   std::size_t SE = Symbols.size();
120   uint64_t SectionOffset = Section.getAddress() - PreferredBaseAddress;
121   uint64_t SectSize = Section.getSize();
122   uint64_t StartOffset = Symbols[SI].Addr - PreferredBaseAddress;
123   uint64_t EndOffset = (SI + 1 < SE)
124                            ? Symbols[SI + 1].Addr - PreferredBaseAddress
125                            : SectionOffset + SectSize;
126   if (StartOffset >= EndOffset)
127     return true;
128 
129   std::string &&SymbolName = Symbols[SI].Name.str();
130   if (ShowDisassembly)
131     outs() << '<' << SymbolName << ">:\n";
132 
133   uint64_t Offset = StartOffset;
134   while (Offset < EndOffset) {
135     MCInst Inst;
136     uint64_t Size;
137     // Disassemble an instruction.
138     if (!DisAsm->getInstruction(Inst, Size, Bytes.slice(Offset - SectionOffset),
139                                 Offset + PreferredBaseAddress, nulls()))
140       return false;
141 
142     if (ShowDisassembly) {
143       outs() << format("%8" PRIx64 ":", Offset);
144       size_t Start = outs().tell();
145       IP->printInst(&Inst, Offset + Size, "", *STI.get(), outs());
146       if (ShowSourceLocations) {
147         unsigned Cur = outs().tell() - Start;
148         if (Cur < 40)
149           outs().indent(40 - Cur);
150         InstructionPointer Inst(this, Offset);
151         outs() << getReversedLocWithContext(symbolize(Inst));
152       }
153       outs() << "\n";
154     }
155 
156     const MCInstrDesc &MCDesc = MII->get(Inst.getOpcode());
157 
158     // Populate address maps.
159     CodeAddrs.push_back(Offset);
160     if (MCDesc.isCall())
161       CallAddrs.insert(Offset);
162     else if (MCDesc.isReturn())
163       RetAddrs.insert(Offset);
164 
165     Offset += Size;
166   }
167 
168   if (ShowDisassembly)
169     outs() << "\n";
170 
171   FuncStartAddrMap[StartOffset] = Symbols[SI].Name.str();
172   return true;
173 }
174 
175 void ProfiledBinary::setUpDisassembler(const ELFObjectFileBase *Obj) {
176   const Target *TheTarget = getTarget(Obj);
177   std::string TripleName = TheTriple.getTriple();
178   StringRef FileName = Obj->getFileName();
179 
180   MRI.reset(TheTarget->createMCRegInfo(TripleName));
181   if (!MRI)
182     exitWithError("no register info for target " + TripleName, FileName);
183 
184   MCTargetOptions MCOptions;
185   AsmInfo.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions));
186   if (!AsmInfo)
187     exitWithError("no assembly info for target " + TripleName, FileName);
188 
189   SubtargetFeatures Features = Obj->getFeatures();
190   STI.reset(
191       TheTarget->createMCSubtargetInfo(TripleName, "", Features.getString()));
192   if (!STI)
193     exitWithError("no subtarget info for target " + TripleName, FileName);
194 
195   MII.reset(TheTarget->createMCInstrInfo());
196   if (!MII)
197     exitWithError("no instruction info for target " + TripleName, FileName);
198 
199   MCObjectFileInfo MOFI;
200   MCContext Ctx(AsmInfo.get(), MRI.get(), &MOFI);
201   MOFI.InitMCObjectFileInfo(Triple(TripleName), false, Ctx);
202   DisAsm.reset(TheTarget->createMCDisassembler(*STI, Ctx));
203   if (!DisAsm)
204     exitWithError("no disassembler for target " + TripleName, FileName);
205 
206   MIA.reset(TheTarget->createMCInstrAnalysis(MII.get()));
207 
208   int AsmPrinterVariant = AsmInfo->getAssemblerDialect();
209   IP.reset(TheTarget->createMCInstPrinter(Triple(TripleName), AsmPrinterVariant,
210                                           *AsmInfo, *MII, *MRI));
211   IP->setPrintBranchImmAsAddress(true);
212 }
213 
214 void ProfiledBinary::disassemble(const ELFObjectFileBase *Obj) {
215   // Set up disassembler and related components.
216   setUpDisassembler(Obj);
217 
218   // Create a mapping from virtual address to symbol name. The symbols in text
219   // sections are the candidates to dissassemble.
220   std::map<SectionRef, SectionSymbolsTy> AllSymbols;
221   StringRef FileName = Obj->getFileName();
222   for (const SymbolRef &Symbol : Obj->symbols()) {
223     const uint64_t Addr = unwrapOrError(Symbol.getAddress(), FileName);
224     const StringRef Name = unwrapOrError(Symbol.getName(), FileName);
225     section_iterator SecI = unwrapOrError(Symbol.getSection(), FileName);
226     if (SecI != Obj->section_end())
227       AllSymbols[*SecI].push_back(SymbolInfoTy(Addr, Name, ELF::STT_NOTYPE));
228   }
229 
230   // Sort all the symbols. Use a stable sort to stabilize the output.
231   for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols)
232     stable_sort(SecSyms.second);
233 
234   if (ShowDisassembly)
235     outs() << "\nDisassembly of " << FileName << ":\n";
236 
237   // Dissassemble a text section.
238   for (section_iterator SI = Obj->section_begin(), SE = Obj->section_end();
239        SI != SE; ++SI) {
240     const SectionRef &Section = *SI;
241     if (!Section.isText())
242       continue;
243 
244     uint64_t ImageLoadAddr = PreferredBaseAddress;
245     uint64_t SectionOffset = Section.getAddress() - ImageLoadAddr;
246     uint64_t SectSize = Section.getSize();
247     if (!SectSize)
248       continue;
249 
250     // Register the text section.
251     TextSections.insert({SectionOffset, SectSize});
252 
253     if (ShowDisassembly) {
254       StringRef SectionName = unwrapOrError(Section.getName(), FileName);
255       outs() << "\nDisassembly of section " << SectionName;
256       outs() << " [" << format("0x%" PRIx64, SectionOffset) << ", "
257              << format("0x%" PRIx64, SectionOffset + SectSize) << "]:\n\n";
258     }
259 
260     // Get the section data.
261     ArrayRef<uint8_t> Bytes =
262         arrayRefFromStringRef(unwrapOrError(Section.getContents(), FileName));
263 
264     // Get the list of all the symbols in this section.
265     SectionSymbolsTy &Symbols = AllSymbols[Section];
266 
267     // Disassemble symbol by symbol.
268     for (std::size_t SI = 0, SE = Symbols.size(); SI != SE; ++SI) {
269       if (!dissassembleSymbol(SI, Bytes, Symbols, Section))
270         exitWithError("disassembling error", FileName);
271     }
272   }
273 }
274 
275 void ProfiledBinary::setupSymbolizer() {
276   symbolize::LLVMSymbolizer::Options SymbolizerOpts;
277   SymbolizerOpts.PrintFunctions =
278       DILineInfoSpecifier::FunctionNameKind::LinkageName;
279   SymbolizerOpts.Demangle = false;
280   SymbolizerOpts.DefaultArch = TheTriple.getArchName().str();
281   SymbolizerOpts.UseSymbolTable = false;
282   SymbolizerOpts.RelativeAddresses = false;
283   Symbolizer = std::make_unique<symbolize::LLVMSymbolizer>(SymbolizerOpts);
284 }
285 
286 FrameLocationStack ProfiledBinary::symbolize(const InstructionPointer &IP) {
287   assert(this == IP.Binary &&
288          "Binary should only symbolize its own instruction");
289   auto Addr = object::SectionedAddress{IP.Offset + PreferredBaseAddress,
290                                        object::SectionedAddress::UndefSection};
291   DIInliningInfo InlineStack =
292       unwrapOrError(Symbolizer->symbolizeInlinedCode(Path, Addr), getName());
293 
294   FrameLocationStack CallStack;
295 
296   for (int32_t I = InlineStack.getNumberOfFrames() - 1; I >= 0; I--) {
297     const auto &CallerFrame = InlineStack.getFrame(I);
298     if (CallerFrame.FunctionName == "<invalid>")
299       break;
300     LineLocation Line(CallerFrame.Line - CallerFrame.StartLine,
301                       CallerFrame.Discriminator);
302     FrameLocation Callsite(CallerFrame.FunctionName, Line);
303     CallStack.push_back(Callsite);
304   }
305 
306   return CallStack;
307 }
308 
309 } // end namespace sampleprof
310 } // end namespace llvm
311