394 lines · cpp
1//===- SymbolizableObjectFile.cpp -----------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// Implementation of SymbolizableObjectFile class.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h"14#include "llvm/ADT/STLExtras.h"15#include "llvm/BinaryFormat/COFF.h"16#include "llvm/DebugInfo/DWARF/DWARFContext.h"17#include "llvm/Object/COFF.h"18#include "llvm/Object/ELFObjectFile.h"19#include "llvm/Object/ObjectFile.h"20#include "llvm/Object/SymbolSize.h"21#include "llvm/Support/Casting.h"22#include "llvm/Support/DataExtractor.h"23#include "llvm/TargetParser/Triple.h"24 25using namespace llvm;26using namespace object;27using namespace symbolize;28 29Expected<std::unique_ptr<SymbolizableObjectFile>>30SymbolizableObjectFile::create(const object::ObjectFile *Obj,31 std::unique_ptr<DIContext> DICtx,32 bool UntagAddresses) {33 assert(DICtx);34 std::unique_ptr<SymbolizableObjectFile> res(35 new SymbolizableObjectFile(Obj, std::move(DICtx), UntagAddresses));36 std::unique_ptr<DataExtractor> OpdExtractor;37 uint64_t OpdAddress = 0;38 // Find the .opd (function descriptor) section if any, for big-endian39 // PowerPC64 ELF.40 if (Obj->getArch() == Triple::ppc64) {41 for (section_iterator Section : Obj->sections()) {42 Expected<StringRef> NameOrErr = Section->getName();43 if (!NameOrErr)44 return NameOrErr.takeError();45 46 if (*NameOrErr == ".opd") {47 Expected<StringRef> E = Section->getContents();48 if (!E)49 return E.takeError();50 OpdExtractor.reset(new DataExtractor(*E, Obj->isLittleEndian(),51 Obj->getBytesInAddress()));52 OpdAddress = Section->getAddress();53 break;54 }55 }56 }57 std::vector<std::pair<SymbolRef, uint64_t>> Symbols =58 computeSymbolSizes(*Obj);59 for (auto &P : Symbols)60 if (Error E =61 res->addSymbol(P.first, P.second, OpdExtractor.get(), OpdAddress))62 return std::move(E);63 64 // If this is a COFF object and we didn't find any symbols, try the export65 // table.66 if (Symbols.empty()) {67 if (auto *CoffObj = dyn_cast<COFFObjectFile>(Obj))68 if (Error E = res->addCoffExportSymbols(CoffObj))69 return std::move(E);70 }71 72 std::vector<SymbolDesc> &SS = res->Symbols;73 // Sort by (Addr,Size,Name). If several SymbolDescs share the same Addr,74 // pick the one with the largest Size. This helps us avoid symbols with no75 // size information (Size=0).76 llvm::stable_sort(SS);77 auto I = SS.begin(), E = SS.end(), J = SS.begin();78 while (I != E) {79 auto OI = I;80 while (++I != E && OI->Addr == I->Addr) {81 }82 *J++ = I[-1];83 }84 SS.erase(J, SS.end());85 86 return std::move(res);87}88 89SymbolizableObjectFile::SymbolizableObjectFile(const ObjectFile *Obj,90 std::unique_ptr<DIContext> DICtx,91 bool UntagAddresses)92 : Module(Obj), DebugInfoContext(std::move(DICtx)),93 UntagAddresses(UntagAddresses) {}94 95namespace {96 97struct OffsetNamePair {98 uint32_t Offset;99 StringRef Name;100 101 bool operator<(const OffsetNamePair &R) const {102 return Offset < R.Offset;103 }104};105 106} // end anonymous namespace107 108Error SymbolizableObjectFile::addCoffExportSymbols(109 const COFFObjectFile *CoffObj) {110 // Get all export names and offsets.111 std::vector<OffsetNamePair> ExportSyms;112 for (const ExportDirectoryEntryRef &Ref : CoffObj->export_directories()) {113 StringRef Name;114 uint32_t Offset;115 if (auto EC = Ref.getSymbolName(Name))116 return EC;117 if (auto EC = Ref.getExportRVA(Offset))118 return EC;119 ExportSyms.push_back(OffsetNamePair{Offset, Name});120 }121 if (ExportSyms.empty())122 return Error::success();123 124 // Sort by ascending offset.125 array_pod_sort(ExportSyms.begin(), ExportSyms.end());126 127 // Approximate the symbol sizes by assuming they run to the next symbol.128 // FIXME: This assumes all exports are functions.129 uint64_t ImageBase = CoffObj->getImageBase();130 for (auto I = ExportSyms.begin(), E = ExportSyms.end(); I != E; ++I) {131 OffsetNamePair &Export = *I;132 // FIXME: The last export has a one byte size now.133 uint32_t NextOffset = I != E ? I->Offset : Export.Offset + 1;134 uint64_t SymbolStart = ImageBase + Export.Offset;135 uint64_t SymbolSize = NextOffset - Export.Offset;136 Symbols.push_back({SymbolStart, SymbolSize, Export.Name, 0});137 }138 return Error::success();139}140 141Error SymbolizableObjectFile::addSymbol(const SymbolRef &Symbol,142 uint64_t SymbolSize,143 DataExtractor *OpdExtractor,144 uint64_t OpdAddress) {145 // Avoid adding symbols from an unknown/undefined section.146 const ObjectFile &Obj = *Symbol.getObject();147 Expected<StringRef> SymbolNameOrErr = Symbol.getName();148 if (!SymbolNameOrErr)149 return SymbolNameOrErr.takeError();150 StringRef SymbolName = *SymbolNameOrErr;151 152 uint32_t ELFSymIdx =153 Obj.isELF() ? ELFSymbolRef(Symbol).getRawDataRefImpl().d.b : 0;154 Expected<section_iterator> Sec = Symbol.getSection();155 if (!Sec || Obj.section_end() == *Sec) {156 if (Obj.isELF()) {157 // Store the (index, filename) pair for a file symbol.158 ELFSymbolRef ESym(Symbol);159 if (ESym.getELFType() == ELF::STT_FILE)160 FileSymbols.emplace_back(ELFSymIdx, SymbolName);161 }162 return Error::success();163 }164 165 Expected<SymbolRef::Type> SymbolTypeOrErr = Symbol.getType();166 if (!SymbolTypeOrErr)167 return SymbolTypeOrErr.takeError();168 SymbolRef::Type SymbolType = *SymbolTypeOrErr;169 if (Obj.isELF()) {170 // Ignore any symbols coming from sections that don't have runtime171 // allocated memory.172 if ((elf_section_iterator(*Sec)->getFlags() & ELF::SHF_ALLOC) == 0)173 return Error::success();174 175 // Allow function and data symbols. Additionally allow STT_NONE, which are176 // common for functions defined in assembly.177 uint8_t Type = ELFSymbolRef(Symbol).getELFType();178 if (Type != ELF::STT_NOTYPE && Type != ELF::STT_FUNC &&179 Type != ELF::STT_OBJECT && Type != ELF::STT_GNU_IFUNC)180 return Error::success();181 // Some STT_NOTYPE symbols are not desired. This excludes STT_SECTION and182 // ARM mapping symbols.183 uint32_t Flags = cantFail(Symbol.getFlags());184 if (Flags & SymbolRef::SF_FormatSpecific)185 return Error::success();186 } else if (SymbolType != SymbolRef::ST_Function &&187 SymbolType != SymbolRef::ST_Data) {188 return Error::success();189 }190 191 Expected<uint64_t> SymbolAddressOrErr = Symbol.getAddress();192 if (!SymbolAddressOrErr)193 return SymbolAddressOrErr.takeError();194 uint64_t SymbolAddress = *SymbolAddressOrErr;195 if (UntagAddresses) {196 // For kernel addresses, bits 56-63 need to be set, so we sign extend bit 55197 // into bits 56-63 instead of masking them out.198 SymbolAddress &= (1ull << 56) - 1;199 SymbolAddress = (int64_t(SymbolAddress) << 8) >> 8;200 }201 if (OpdExtractor) {202 // For big-endian PowerPC64 ELF, symbols in the .opd section refer to203 // function descriptors. The first word of the descriptor is a pointer to204 // the function's code.205 // For the purposes of symbolization, pretend the symbol's address is that206 // of the function's code, not the descriptor.207 uint64_t OpdOffset = SymbolAddress - OpdAddress;208 if (OpdExtractor->isValidOffsetForAddress(OpdOffset))209 SymbolAddress = OpdExtractor->getAddress(&OpdOffset);210 }211 // Mach-O symbol table names have leading underscore, skip it.212 if (Module->isMachO())213 SymbolName.consume_front("_");214 215 if (Obj.isELF() && ELFSymbolRef(Symbol).getBinding() != ELF::STB_LOCAL)216 ELFSymIdx = 0;217 Symbols.push_back({SymbolAddress, SymbolSize, SymbolName, ELFSymIdx});218 return Error::success();219}220 221// Return true if this is a 32-bit x86 PE COFF module.222bool SymbolizableObjectFile::isWin32Module() const {223 auto *CoffObject = dyn_cast<COFFObjectFile>(Module);224 return CoffObject && CoffObject->getMachine() == COFF::IMAGE_FILE_MACHINE_I386;225}226 227uint64_t SymbolizableObjectFile::getModulePreferredBase() const {228 if (auto *CoffObject = dyn_cast<COFFObjectFile>(Module))229 return CoffObject->getImageBase();230 return 0;231}232 233bool SymbolizableObjectFile::getNameFromSymbolTable(234 uint64_t Address, std::string &Name, uint64_t &Addr, uint64_t &Size,235 std::string &FileName) const {236 SymbolDesc SD{Address, UINT64_C(-1), StringRef(), 0};237 auto SymbolIterator = llvm::upper_bound(Symbols, SD);238 if (SymbolIterator == Symbols.begin())239 return false;240 --SymbolIterator;241 if (SymbolIterator->Size != 0 &&242 SymbolIterator->Addr + SymbolIterator->Size <= Address)243 return false;244 Name = SymbolIterator->Name.str();245 Addr = SymbolIterator->Addr;246 Size = SymbolIterator->Size;247 248 if (SymbolIterator->ELFLocalSymIdx != 0) {249 // If this is an ELF local symbol, find the STT_FILE symbol preceding250 // SymbolIterator to get the filename. The ELF spec requires the STT_FILE251 // symbol (if present) precedes the other STB_LOCAL symbols for the file.252 assert(Module->isELF());253 auto It = llvm::upper_bound(254 FileSymbols,255 std::make_pair(SymbolIterator->ELFLocalSymIdx, StringRef()));256 if (It != FileSymbols.begin())257 FileName = It[-1].second.str();258 }259 return true;260}261 262bool SymbolizableObjectFile::shouldOverrideWithSymbolTable(263 FunctionNameKind FNKind, bool UseSymbolTable) const {264 // When DWARF is used with -gline-tables-only / -gmlt, the symbol table gives265 // better answers for linkage names than the DIContext. Otherwise, we are266 // probably using PEs and PDBs, and we shouldn't do the override. PE files267 // generally only contain the names of exported symbols.268 return FNKind == FunctionNameKind::LinkageName && UseSymbolTable &&269 isa<DWARFContext>(DebugInfoContext.get());270}271 272DILineInfo273SymbolizableObjectFile::symbolizeCode(object::SectionedAddress ModuleOffset,274 DILineInfoSpecifier LineInfoSpecifier,275 bool UseSymbolTable) const {276 if (ModuleOffset.SectionIndex == object::SectionedAddress::UndefSection)277 ModuleOffset.SectionIndex =278 getModuleSectionIndexForAddress(ModuleOffset.Address);279 DILineInfo LineInfo;280 std::optional<DILineInfo> DBGLineInfo =281 DebugInfoContext->getLineInfoForAddress(ModuleOffset, LineInfoSpecifier);282 if (DBGLineInfo)283 LineInfo = *DBGLineInfo;284 285 // Override function name from symbol table if necessary.286 if (shouldOverrideWithSymbolTable(LineInfoSpecifier.FNKind, UseSymbolTable)) {287 std::string FunctionName, FileName;288 uint64_t Start, Size;289 if (getNameFromSymbolTable(ModuleOffset.Address, FunctionName, Start, Size,290 FileName)) {291 LineInfo.FunctionName = FunctionName;292 LineInfo.StartAddress = Start;293 // Only use the filename from symbol table if the debug info for the294 // address is missing.295 if (!DBGLineInfo && !FileName.empty())296 LineInfo.FileName = FileName;297 }298 }299 return LineInfo;300}301 302DIInliningInfo SymbolizableObjectFile::symbolizeInlinedCode(303 object::SectionedAddress ModuleOffset,304 DILineInfoSpecifier LineInfoSpecifier, bool UseSymbolTable) const {305 if (ModuleOffset.SectionIndex == object::SectionedAddress::UndefSection)306 ModuleOffset.SectionIndex =307 getModuleSectionIndexForAddress(ModuleOffset.Address);308 DIInliningInfo InlinedContext = DebugInfoContext->getInliningInfoForAddress(309 ModuleOffset, LineInfoSpecifier);310 311 // Make sure there is at least one frame in context.312 bool EmptyFrameAdded = false;313 if (InlinedContext.getNumberOfFrames() == 0) {314 EmptyFrameAdded = true;315 InlinedContext.addFrame(DILineInfo());316 }317 318 // Override the function name in lower frame with name from symbol table.319 if (shouldOverrideWithSymbolTable(LineInfoSpecifier.FNKind, UseSymbolTable)) {320 std::string FunctionName, FileName;321 uint64_t Start, Size;322 if (getNameFromSymbolTable(ModuleOffset.Address, FunctionName, Start, Size,323 FileName)) {324 DILineInfo *LI = InlinedContext.getMutableFrame(325 InlinedContext.getNumberOfFrames() - 1);326 LI->FunctionName = FunctionName;327 LI->StartAddress = Start;328 // Only use the filename from symbol table if the debug info for the329 // address is missing.330 if (EmptyFrameAdded && !FileName.empty())331 LI->FileName = FileName;332 }333 }334 335 return InlinedContext;336}337 338DIGlobal SymbolizableObjectFile::symbolizeData(339 object::SectionedAddress ModuleOffset) const {340 DIGlobal Res;341 std::string FileName;342 getNameFromSymbolTable(ModuleOffset.Address, Res.Name, Res.Start, Res.Size,343 FileName);344 Res.DeclFile = FileName;345 346 // Try and get a better filename:lineno pair from the debuginfo, if present.347 std::optional<DILineInfo> DL =348 DebugInfoContext->getLineInfoForDataAddress(ModuleOffset);349 if (DL && DL->Line != 0) {350 Res.DeclFile = DL->FileName;351 Res.DeclLine = DL->Line;352 }353 return Res;354}355 356std::vector<DILocal> SymbolizableObjectFile::symbolizeFrame(357 object::SectionedAddress ModuleOffset) const {358 if (ModuleOffset.SectionIndex == object::SectionedAddress::UndefSection)359 ModuleOffset.SectionIndex =360 getModuleSectionIndexForAddress(ModuleOffset.Address);361 return DebugInfoContext->getLocalsForAddress(ModuleOffset);362}363 364std::vector<object::SectionedAddress>365SymbolizableObjectFile::findSymbol(StringRef Symbol, uint64_t Offset) const {366 std::vector<object::SectionedAddress> Result;367 for (const SymbolDesc &Sym : Symbols) {368 if (Sym.Name == Symbol) {369 uint64_t Addr = Sym.Addr;370 if (Offset < Sym.Size)371 Addr += Offset;372 object::SectionedAddress A{Addr, getModuleSectionIndexForAddress(Addr)};373 Result.push_back(A);374 }375 }376 return Result;377}378 379/// Search for the first occurence of specified Address in ObjectFile.380uint64_t SymbolizableObjectFile::getModuleSectionIndexForAddress(381 uint64_t Address) const {382 383 for (SectionRef Sec : Module->sections()) {384 if (!Sec.isText() || Sec.isVirtual())385 continue;386 387 if (Address >= Sec.getAddress() &&388 Address < Sec.getAddress() + Sec.getSize())389 return Sec.getIndex();390 }391 392 return object::SectionedAddress::UndefSection;393}394