Index: test/tools/llvm-symbolizer/errors.test =================================================================== --- /dev/null +++ test/tools/llvm-symbolizer/errors.test @@ -0,0 +1,8 @@ +RUN: echo "nonexisting 0x100" > %t.input +RUN: echo "%s 0x100" >> %t.input + +RUN: llvm-symbolizer < %t.input 2>&1 | FileCheck %s + +CHECK: LLVMSymbolizer: error reading file: No such file or directory. +CHECK: LLVMSymbolizer: error reading file: The file was not recognized as a valid object file. + Index: tools/llvm-symbolizer/LLVMSymbolize.h =================================================================== --- tools/llvm-symbolizer/LLVMSymbolize.h +++ tools/llvm-symbolizer/LLVMSymbolize.h @@ -50,48 +50,39 @@ }; LLVMSymbolizer(const Options &Opts = Options()) : Opts(Opts) {} - ~LLVMSymbolizer() { - flush(); - } // Returns the result of symbolization for module name/offset as // a string (possibly containing newlines). - std::string - symbolizeCode(const std::string &ModuleName, uint64_t ModuleOffset); - std::string - symbolizeData(const std::string &ModuleName, uint64_t ModuleOffset); + std::string symbolizeCode(const std::string &ModuleName, + uint64_t ModuleOffset); + std::string symbolizeData(const std::string &ModuleName, + uint64_t ModuleOffset); void flush(); static std::string DemangleName(const std::string &Name); -private: - typedef std::pair ObjectPair; +private: ModuleInfo *getOrCreateModuleInfo(const std::string &ModuleName); - ObjectFile *lookUpDsymFile(const std::string &Path, const MachOObjectFile *ExeObj, - const std::string &ArchName); - - /// \brief Returns pair of pointers to object and debug object. - ObjectPair getOrCreateObjects(const std::string &Path, - const std::string &ArchName); - /// \brief Returns a parsed object file for a given architecture in a - /// universal binary (or the binary itself if it is an object file). - ObjectFile *getObjectFileFromBinary(Binary *Bin, const std::string &ArchName); std::string printDILineInfo(DILineInfo LineInfo) const; - // Owns all the parsed binaries and object files. - SmallVector, 4> ParsedBinariesAndObjects; - SmallVector, 4> MemoryBuffers; - void addOwningBinary(OwningBinary Bin) { - ParsedBinariesAndObjects.push_back(std::move(Bin.getBinary())); - MemoryBuffers.push_back(std::move(Bin.getBuffer())); - } - - // Owns module info objects. - std::map Modules; - std::map, ObjectFile *> - ObjectFileForArch; - std::map, ObjectPair> - ObjectPairForPathArch; + ErrorOr loadBinary(const std::string &Path); + ErrorOr loadObjectFile(const std::string &Path, + const std::string &ArchName); + + const ObjectFile *findDbgObj(const ObjectFile *Obj, const std::string &Path, + const std::string &ArchName); + const ObjectFile *findDsymFile(const MachOObjectFile *ExeObj, + const std::string &Path, + const std::string &ArchName); + + // Stores parsed binaries for each filepath. + std::map>> LoadedBinaries; + // MachO object files, which are contained inside parsed MachO universal + // binaries. + SmallVector, 4> ParsedMachOObjects; + // Stores ModuleInfo objects corresponding to filepath/architecture pair. + std::map, std::unique_ptr> + ModuleForPathArch; Options Opts; static const char kBadString[]; @@ -99,7 +90,7 @@ class ModuleInfo { public: - ModuleInfo(ObjectFile *Obj, DIContext *DICtx); + ModuleInfo(const ObjectFile *Obj, const ObjectFile *DbgObj); DILineInfo symbolizeCode(uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const; @@ -113,7 +104,7 @@ std::string &Name, uint64_t &Addr, uint64_t &Size) const; void addSymbol(const SymbolRef &Symbol); - ObjectFile *Module; + const ObjectFile *Module; std::unique_ptr DebugInfoContext; struct SymbolDesc { Index: tools/llvm-symbolizer/LLVMSymbolize.cpp =================================================================== --- tools/llvm-symbolizer/LLVMSymbolize.cpp +++ tools/llvm-symbolizer/LLVMSymbolize.cpp @@ -43,8 +43,9 @@ Opts.PrintFunctions); } -ModuleInfo::ModuleInfo(ObjectFile *Obj, DIContext *DICtx) - : Module(Obj), DebugInfoContext(DICtx) { +ModuleInfo::ModuleInfo(const ObjectFile *Obj, const ObjectFile *DbgObj) + : Module(Obj), DebugInfoContext(DIContext::getDWARFContext(*DbgObj)) { + assert(DebugInfoContext); for (const SymbolRef &Symbol : Module->symbols()) { addSymbol(Symbol); } @@ -112,11 +113,8 @@ DILineInfo ModuleInfo::symbolizeCode( uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const { - DILineInfo LineInfo; - if (DebugInfoContext) { - LineInfo = DebugInfoContext->getLineInfoForAddress( - ModuleOffset, getDILineInfoSpecifier(Opts)); - } + DILineInfo LineInfo = DebugInfoContext->getLineInfoForAddress( + ModuleOffset, getDILineInfoSpecifier(Opts)); // Override function name from symbol table if necessary. if (Opts.PrintFunctions != FunctionNameKind::None && Opts.UseSymbolTable) { std::string FunctionName; @@ -131,11 +129,8 @@ DIInliningInfo ModuleInfo::symbolizeInlinedCode( uint64_t ModuleOffset, const LLVMSymbolizer::Options &Opts) const { - DIInliningInfo InlinedContext; - if (DebugInfoContext) { - InlinedContext = DebugInfoContext->getInliningInfoForAddress( - ModuleOffset, getDILineInfoSpecifier(Opts)); - } + DIInliningInfo InlinedContext = DebugInfoContext->getInliningInfoForAddress( + ModuleOffset, getDILineInfoSpecifier(Opts)); // Make sure there is at least one frame in context. if (InlinedContext.getNumberOfFrames() == 0) { InlinedContext.addFrame(DILineInfo()); @@ -206,9 +201,9 @@ } void LLVMSymbolizer::flush() { - DeleteContainerSeconds(Modules); - ObjectPairForPathArch.clear(); - ObjectFileForArch.clear(); + ModuleForPathArch.clear(); + ParsedMachOObjects.clear(); + LoadedBinaries.clear(); } // For Path="/path/to/foo" and Basename="foo" assume that debug info is in @@ -274,8 +269,6 @@ static bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName, uint32_t &CRCHash) { - if (!Obj) - return false; for (const SectionRef &Section : Obj->sections()) { StringRef Name; Section.getName(Name); @@ -310,8 +303,10 @@ return !memcmp(dbg_uuid.data(), bin_uuid.data(), dbg_uuid.size()); } -ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath, - const MachOObjectFile *MachExeObj, const std::string &ArchName) { +const ObjectFile * +LLVMSymbolizer::findDsymFile(const MachOObjectFile *MachExeObj, + const std::string &ExePath, + const std::string &ArchName) { // On Darwin we may find DWARF in separate object file in // resource directory. std::vector DsymPaths; @@ -320,120 +315,119 @@ for (const auto &Path : Opts.DsymHints) { DsymPaths.push_back(getDarwinDWARFResourceForPath(Path, Filename)); } - for (const auto &path : DsymPaths) { - ErrorOr> BinaryOrErr = createBinary(path); - std::error_code EC = BinaryOrErr.getError(); - if (EC != errc::no_such_file_or_directory && !error(EC)) { - OwningBinary B = std::move(BinaryOrErr.get()); - ObjectFile *DbgObj = - getObjectFileFromBinary(B.getBinary().get(), ArchName); - const MachOObjectFile *MachDbgObj = - dyn_cast(DbgObj); - if (!MachDbgObj) continue; - if (darwinDsymMatchesBinary(MachDbgObj, MachExeObj)) { - addOwningBinary(std::move(B)); - return DbgObj; - } - } + for (const auto &Path : DsymPaths) { + auto DbgObjOrErr = loadObjectFile(Path, ArchName); + if (!DbgObjOrErr) + continue; + const MachOObjectFile *MachDbgObj = + dyn_cast(DbgObjOrErr.get()); + if (MachDbgObj && darwinDsymMatchesBinary(MachDbgObj, MachExeObj)) + return MachDbgObj; } return nullptr; } -LLVMSymbolizer::ObjectPair -LLVMSymbolizer::getOrCreateObjects(const std::string &Path, - const std::string &ArchName) { - const auto &I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName)); - if (I != ObjectPairForPathArch.end()) - return I->second; - ObjectFile *Obj = nullptr; - ObjectFile *DbgObj = nullptr; - ErrorOr> BinaryOrErr = createBinary(Path); - if (!error(BinaryOrErr.getError())) { - OwningBinary &B = BinaryOrErr.get(); - Obj = getObjectFileFromBinary(B.getBinary().get(), ArchName); - if (!Obj) { - ObjectPair Res = std::make_pair(nullptr, nullptr); - ObjectPairForPathArch[std::make_pair(Path, ArchName)] = Res; - return Res; - } - addOwningBinary(std::move(B)); - if (auto MachObj = dyn_cast(Obj)) - DbgObj = lookUpDsymFile(Path, MachObj, ArchName); - // Try to locate the debug binary using .gnu_debuglink section. - if (!DbgObj) { - std::string DebuglinkName; - uint32_t CRCHash; - std::string DebugBinaryPath; - if (getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash) && - findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath)) { - BinaryOrErr = createBinary(DebugBinaryPath); - if (!error(BinaryOrErr.getError())) { - OwningBinary B = std::move(BinaryOrErr.get()); - DbgObj = getObjectFileFromBinary(B.getBinary().get(), ArchName); - addOwningBinary(std::move(B)); - } - } +const ObjectFile *LLVMSymbolizer::findDbgObj(const ObjectFile *Obj, + const std::string &Path, + const std::string &ArchName) { + assert(Obj); + const ObjectFile *DbgObj = nullptr; + // Try to find .dSYM bundle for the file. + if (auto MachObj = dyn_cast(Obj)) + DbgObj = findDsymFile(MachObj, Path, ArchName); + // Try to find debug binary from .gnu_debuglink section. + if (!DbgObj) { + std::string DebuglinkName; + uint32_t CRCHash; + std::string DebugBinaryPath; + if (getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash) && + findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath)) { + if (auto DbgObjOrErr = loadObjectFile(DebugBinaryPath, ArchName)) + DbgObj = DbgObjOrErr.get(); } } + // If no external object is found, return the original object itself. if (!DbgObj) DbgObj = Obj; - ObjectPair Res = std::make_pair(Obj, DbgObj); - ObjectPairForPathArch[std::make_pair(Path, ArchName)] = Res; - return Res; + return DbgObj; } -ObjectFile * -LLVMSymbolizer::getObjectFileFromBinary(Binary *Bin, - const std::string &ArchName) { - if (!Bin) - return nullptr; - ObjectFile *Res = nullptr; - if (MachOUniversalBinary *UB = dyn_cast(Bin)) { - const auto &I = ObjectFileForArch.find( - std::make_pair(UB, ArchName)); - if (I != ObjectFileForArch.end()) - return I->second; - ErrorOr> ParsedObj = - UB->getObjectForArch(Triple(ArchName).getArch()); - if (ParsedObj) { - Res = ParsedObj.get().get(); - ParsedBinariesAndObjects.push_back(std::move(ParsedObj.get())); - } - ObjectFileForArch[std::make_pair(UB, ArchName)] = Res; +// Extracts Binary* from ErrorOr>, or propagates error. +static ErrorOr +getPointer(const ErrorOr> &BinaryOrErr) { + if (BinaryOrErr) + return BinaryOrErr.get().getBinary().get(); + else + return BinaryOrErr.getError(); +} + +ErrorOr LLVMSymbolizer::loadBinary(const std::string &Path) { + const auto &I = LoadedBinaries.find(Path); + if (I != LoadedBinaries.end()) { + return getPointer(I->second); + } + const auto &InsertPt = + LoadedBinaries.insert(std::make_pair(Path, createBinary(Path))); + assert(InsertPt.second); + return getPointer(InsertPt.first->second); +} + +ErrorOr +LLVMSymbolizer::loadObjectFile(const std::string &Path, + const std::string &ArchName) { + ErrorOr BinaryOrErr = loadBinary(Path); + if (!BinaryOrErr) + return BinaryOrErr.getError(); + const Binary *Bin = BinaryOrErr.get(); + + if (const auto *UB = dyn_cast(Bin)) { + auto Res = UB->getObjectForArch(Triple(ArchName).getArch()); + if (!Res) + return Res.getError(); + ParsedMachOObjects.push_back(std::move(Res.get())); + return ParsedMachOObjects.back().get(); } else if (Bin->isObject()) { - Res = cast(Bin); + return cast(Bin); } - return Res; + return object_error::invalid_file_type; } -ModuleInfo * -LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { - const auto &I = Modules.find(ModuleName); - if (I != Modules.end()) - return I->second; - std::string BinaryName = ModuleName; - std::string ArchName = Opts.DefaultArch; - size_t ColonPos = ModuleName.find_last_of(':'); - // Verify that substring after colon form a valid arch name. +static std::pair +getPathArchPair(const std::string &Path, const std::string &DefaultArch) { + size_t ColonPos = Path.find_last_of(':'); if (ColonPos != std::string::npos) { - std::string ArchStr = ModuleName.substr(ColonPos + 1); - if (Triple(ArchStr).getArch() != Triple::UnknownArch) { - BinaryName = ModuleName.substr(0, ColonPos); - ArchName = ArchStr; - } + std::string ArchStr = Path.substr(ColonPos + 1); + // Verify that substring after colon form a valid arch name. + if (Triple(ArchStr).getArch() != Triple::UnknownArch) + return std::make_pair(Path.substr(0, ColonPos), ArchStr); } - ObjectPair Objects = getOrCreateObjects(BinaryName, ArchName); + return std::make_pair(Path, DefaultArch); +} - if (!Objects.first) { - // Failed to find valid object file. - Modules.insert(make_pair(ModuleName, (ModuleInfo *)nullptr)); +ModuleInfo * +LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { + auto PathArchPair = getPathArchPair(ModuleName, Opts.DefaultArch); + const auto &I = ModuleForPathArch.find(PathArchPair); + if (I != ModuleForPathArch.end()) + return I->second.get(); + + // Get obj/debug_obj pair. + auto ObjOrError = loadObjectFile(PathArchPair.first, PathArchPair.second); + if (!ObjOrError) { + error(ObjOrError.getError()); + ModuleForPathArch.insert( + std::make_pair(PathArchPair, std::unique_ptr())); return nullptr; } - DIContext *Context = DIContext::getDWARFContext(*Objects.second); - assert(Context); - ModuleInfo *Info = new ModuleInfo(Objects.first, Context); - Modules.insert(make_pair(ModuleName, Info)); - return Info; + const ObjectFile *Obj = ObjOrError.get(); + const ObjectFile *DbgObj = + findDbgObj(Obj, PathArchPair.first, PathArchPair.second); + assert(DbgObj); + + const auto &II = ModuleForPathArch.insert( + std::make_pair(PathArchPair, make_unique(Obj, DbgObj))); + assert(II.second); + return II.first->second.get(); } std::string LLVMSymbolizer::printDILineInfo(DILineInfo LineInfo) const {