//===-- LLVMSymbolize.cpp -------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Implementation for LLVM symbolization library. // //===----------------------------------------------------------------------===// #include "LLVMSymbolize.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Object/MachO.h" #include "llvm/Support/Casting.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include namespace llvm { namespace symbolize { static bool error(error_code ec) { if (!ec) return false; errs() << "LLVMSymbolizer: error reading file: " << ec.message() << ".\n"; return true; } static uint32_t getDILineInfoSpecifierFlags(const LLVMSymbolizer::Options &Opts) { uint32_t Flags = llvm::DILineInfoSpecifier::FileLineInfo | llvm::DILineInfoSpecifier::AbsoluteFilePath; if (Opts.PrintFunctions) Flags |= llvm::DILineInfoSpecifier::FunctionName; return Flags; } static void patchFunctionNameInDILineInfo(const std::string &NewFunctionName, DILineInfo &LineInfo) { std::string FileName = LineInfo.getFileName(); LineInfo = DILineInfo(StringRef(FileName), StringRef(NewFunctionName), LineInfo.getLine(), LineInfo.getColumn()); } ModuleInfo::ModuleInfo(ObjectFile *Obj, DIContext *DICtx) : Module(Obj), DebugInfoContext(DICtx) { error_code ec; for (symbol_iterator si = Module->begin_symbols(), se = Module->end_symbols(); si != se; si.increment(ec)) { if (error(ec)) return; SymbolRef::Type SymbolType; if (error(si->getType(SymbolType))) continue; if (SymbolType != SymbolRef::ST_Function && SymbolType != SymbolRef::ST_Data) continue; uint64_t SymbolAddress; if (error(si->getAddress(SymbolAddress)) || SymbolAddress == UnknownAddressOrSize) continue; uint64_t SymbolSize; // Getting symbol size is linear for Mach-O files, so assume that symbol // occupies the memory range up to the following symbol. if (isa(Obj)) SymbolSize = 0; else if (error(si->getSize(SymbolSize)) || SymbolSize == UnknownAddressOrSize) continue; StringRef SymbolName; if (error(si->getName(SymbolName))) continue; // FIXME: If a function has alias, there are two entries in symbol table // with same address size. Make sure we choose the correct one. SymbolMapTy &M = SymbolType == SymbolRef::ST_Function ? Functions : Objects; SymbolDesc SD = { SymbolAddress, SymbolSize }; M.insert(std::make_pair(SD, SymbolName)); } } bool ModuleInfo::getNameFromSymbolTable(SymbolRef::Type Type, uint64_t Address, std::string &Name, uint64_t &Addr, uint64_t &Size) const { const SymbolMapTy &M = Type == SymbolRef::ST_Function ? Functions : Objects; if (M.empty()) return false; SymbolDesc SD = { Address, Address }; SymbolMapTy::const_iterator it = M.upper_bound(SD); if (it == M.begin()) return false; --it; if (it->first.Size != 0 && it->first.Addr + it->first.Size <= Address) return false; Name = it->second.str(); Addr = it->first.Addr; Size = it->first.Size; return true; } DILineInfo ModuleInfo::symbolizeCode( uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const { DILineInfo LineInfo; if (DebugInfoContext) { LineInfo = DebugInfoContext->getLineInfoForAddress( ModuleOffset, getDILineInfoSpecifierFlags(Opts)); } // Override function name from symbol table if necessary. if (Opts.PrintFunctions && Opts.UseSymbolTable) { std::string FunctionName; uint64_t Start, Size; if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset, FunctionName, Start, Size)) { patchFunctionNameInDILineInfo(FunctionName, LineInfo); } } return LineInfo; } DIInliningInfo ModuleInfo::symbolizeInlinedCode( uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const { DIInliningInfo InlinedContext; if (DebugInfoContext) { InlinedContext = DebugInfoContext->getInliningInfoForAddress( ModuleOffset, getDILineInfoSpecifierFlags(Opts)); } // Make sure there is at least one frame in context. if (InlinedContext.getNumberOfFrames() == 0) { InlinedContext.addFrame(DILineInfo()); } // Override the function name in lower frame with name from symbol table. if (Opts.PrintFunctions && Opts.UseSymbolTable) { DIInliningInfo PatchedInlinedContext; for (uint32_t i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) { DILineInfo LineInfo = InlinedContext.getFrame(i); if (i == n - 1) { std::string FunctionName; uint64_t Start, Size; if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset, FunctionName, Start, Size)) { patchFunctionNameInDILineInfo(FunctionName, LineInfo); } } PatchedInlinedContext.addFrame(LineInfo); } InlinedContext = PatchedInlinedContext; } return InlinedContext; } bool ModuleInfo::symbolizeData(uint64_t ModuleOffset, std::string &Name, uint64_t &Start, uint64_t &Size) const { return getNameFromSymbolTable(SymbolRef::ST_Data, ModuleOffset, Name, Start, Size); } const char LLVMSymbolizer::kBadString[] = "??"; std::string LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, uint64_t ModuleOffset) { ModuleInfo *Info = getOrCreateModuleInfo(ModuleName); if (Info == 0) return printDILineInfo(DILineInfo()); if (Opts.PrintInlining) { DIInliningInfo InlinedContext = Info->symbolizeInlinedCode(ModuleOffset, Opts); uint32_t FramesNum = InlinedContext.getNumberOfFrames(); assert(FramesNum > 0); std::string Result; for (uint32_t i = 0; i < FramesNum; i++) { DILineInfo LineInfo = InlinedContext.getFrame(i); Result += printDILineInfo(LineInfo); } return Result; } DILineInfo LineInfo = Info->symbolizeCode(ModuleOffset, Opts); return printDILineInfo(LineInfo); } std::string LLVMSymbolizer::symbolizeData(const std::string &ModuleName, uint64_t ModuleOffset) { std::string Name = kBadString; uint64_t Start = 0; uint64_t Size = 0; if (Opts.UseSymbolTable) { if (ModuleInfo *Info = getOrCreateModuleInfo(ModuleName)) { if (Info->symbolizeData(ModuleOffset, Name, Start, Size)) DemangleName(Name); } } std::stringstream ss; ss << Name << "\n" << Start << " " << Size << "\n"; return ss.str(); } void LLVMSymbolizer::flush() { DeleteContainerSeconds(Modules); } // Returns true if the object endianness is known. static bool getObjectEndianness(const ObjectFile *Obj, bool &IsLittleEndian) { // FIXME: Implement this when libLLVMObject allows to do it easily. IsLittleEndian = true; return true; } static ObjectFile *getObjectFile(const std::string &Path) { OwningPtr Buff; if (error(MemoryBuffer::getFile(Path, Buff))) return 0; return ObjectFile::createObjectFile(Buff.take()); } static std::string getDarwinDWARFResourceForModule(const std::string &Path) { StringRef Basename = sys::path::filename(Path); const std::string &DSymDirectory = Path + ".dSYM"; SmallString<16> ResourceName = StringRef(DSymDirectory); sys::path::append(ResourceName, "Contents", "Resources", "DWARF"); sys::path::append(ResourceName, Basename); return ResourceName.str(); } ModuleInfo * LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { ModuleMapTy::iterator I = Modules.find(ModuleName); if (I != Modules.end()) return I->second; ObjectFile *Obj = getObjectFile(ModuleName); if (Obj == 0) { // Module name doesn't point to a valid object file. Modules.insert(make_pair(ModuleName, (ModuleInfo *)0)); return 0; } DIContext *Context = 0; bool IsLittleEndian; if (getObjectEndianness(Obj, IsLittleEndian)) { // On Darwin we may find DWARF in separate object file in // resource directory. ObjectFile *DbgObj = Obj; if (isa(Obj)) { const std::string &ResourceName = getDarwinDWARFResourceForModule(ModuleName); bool ResourceFileExists = false; if (!sys::fs::exists(ResourceName, ResourceFileExists) && ResourceFileExists) { if (ObjectFile *ResourceObj = getObjectFile(ResourceName)) DbgObj = ResourceObj; } } Context = DIContext::getDWARFContext(DbgObj); assert(Context); } ModuleInfo *Info = new ModuleInfo(Obj, Context); Modules.insert(make_pair(ModuleName, Info)); return Info; } std::string LLVMSymbolizer::printDILineInfo(DILineInfo LineInfo) const { // By default, DILineInfo contains "" for function/filename it // cannot fetch. We replace it to "??" to make our output closer to addr2line. static const std::string kDILineInfoBadString = ""; std::stringstream Result; if (Opts.PrintFunctions) { std::string FunctionName = LineInfo.getFunctionName(); if (FunctionName == kDILineInfoBadString) FunctionName = kBadString; DemangleName(FunctionName); Result << FunctionName << "\n"; } std::string Filename = LineInfo.getFileName(); if (Filename == kDILineInfoBadString) Filename = kBadString; Result << Filename << ":" << LineInfo.getLine() << ":" << LineInfo.getColumn() << "\n"; return Result.str(); } #if !defined(_MSC_VER) // Assume that __cxa_demangle is provided by libcxxabi (except for Windows). extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer, size_t *length, int *status); #endif void LLVMSymbolizer::DemangleName(std::string &Name) const { #if !defined(_MSC_VER) if (!Opts.Demangle) return; int status = 0; char *DemangledName = __cxa_demangle(Name.c_str(), 0, 0, &status); if (status != 0) return; Name = DemangledName; free(DemangledName); #endif } } // namespace symbolize } // namespace llvm