//===----------------------------------------------------------------------===//
#include "LLVMSymbolize.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Config/config.h"
+#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/MachO.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compression.h"
+#include "llvm/Support/DataExtractor.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
-
#include <sstream>
+#include <stdlib.h>
namespace llvm {
+using std::error_code;
namespace symbolize {
static bool error(error_code ec) {
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());
+static DILineInfoSpecifier
+getDILineInfoSpecifier(const LLVMSymbolizer::Options &Opts) {
+ return DILineInfoSpecifier(
+ DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath,
+ Opts.PrintFunctions);
}
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;
- 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, SymbolAddress + SymbolSize };
- M.insert(std::make_pair(SD, SymbolName));
+ for (const SymbolRef &Symbol : Module->symbols()) {
+ addSymbol(Symbol);
+ }
+ bool NoSymbolTable = (Module->symbol_begin() == Module->symbol_end());
+ if (NoSymbolTable && Module->isELF()) {
+ // Fallback to dynamic symbol table, if regular symbol table is stripped.
+ std::pair<symbol_iterator, symbol_iterator> IDyn =
+ getELFDynamicSymbolIterators(Module);
+ for (symbol_iterator si = IDyn.first, se = IDyn.second; si != se; ++si) {
+ addSymbol(*si);
+ }
}
}
+void ModuleInfo::addSymbol(const SymbolRef &Symbol) {
+ SymbolRef::Type SymbolType;
+ if (error(Symbol.getType(SymbolType)))
+ return;
+ if (SymbolType != SymbolRef::ST_Function && SymbolType != SymbolRef::ST_Data)
+ return;
+ uint64_t SymbolAddress;
+ if (error(Symbol.getAddress(SymbolAddress)) ||
+ SymbolAddress == UnknownAddressOrSize)
+ return;
+ 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<MachOObjectFile>(Module))
+ SymbolSize = 0;
+ else if (error(Symbol.getSize(SymbolSize)) ||
+ SymbolSize == UnknownAddressOrSize)
+ return;
+ StringRef SymbolName;
+ if (error(Symbol.getName(SymbolName)))
+ return;
+ // Mach-O symbol table names have leading underscore, skip it.
+ if (Module->isMachO() && SymbolName.size() > 0 && SymbolName[0] == '_')
+ SymbolName = SymbolName.drop_front();
+ // 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;
- SymbolDesc SD = { Address, Address + 1 };
- SymbolMapTy::const_iterator it = M.find(SD);
- if (it == M.end())
+ if (M.empty())
+ return false;
+ SymbolDesc SD = { Address, Address };
+ SymbolMapTy::const_iterator it = M.upper_bound(SD);
+ if (it == M.begin())
return false;
- if (Address < it->first.Addr || Address >= it->first.AddrEnd)
+ --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.AddrEnd - it->first.Addr;
+ Size = it->first.Size;
return true;
}
DILineInfo LineInfo;
if (DebugInfoContext) {
LineInfo = DebugInfoContext->getLineInfoForAddress(
- ModuleOffset, getDILineInfoSpecifierFlags(Opts));
+ ModuleOffset, getDILineInfoSpecifier(Opts));
}
// Override function name from symbol table if necessary.
- if (Opts.PrintFunctions && Opts.UseSymbolTable) {
+ if (Opts.PrintFunctions != FunctionNameKind::None && Opts.UseSymbolTable) {
std::string FunctionName;
uint64_t Start, Size;
if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
FunctionName, Start, Size)) {
- patchFunctionNameInDILineInfo(FunctionName, LineInfo);
+ LineInfo.FunctionName = FunctionName;
}
}
return LineInfo;
DIInliningInfo InlinedContext;
if (DebugInfoContext) {
InlinedContext = DebugInfoContext->getInliningInfoForAddress(
- ModuleOffset, getDILineInfoSpecifierFlags(Opts));
+ ModuleOffset, getDILineInfoSpecifier(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) {
+ if (Opts.PrintFunctions != FunctionNameKind::None && Opts.UseSymbolTable) {
DIInliningInfo PatchedInlinedContext;
for (uint32_t i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) {
DILineInfo LineInfo = InlinedContext.getFrame(i);
uint64_t Start, Size;
if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
FunctionName, Start, Size)) {
- patchFunctionNameInDILineInfo(FunctionName, LineInfo);
+ LineInfo.FunctionName = FunctionName;
}
}
PatchedInlinedContext.addFrame(LineInfo);
std::string LLVMSymbolizer::symbolizeCode(const std::string &ModuleName,
uint64_t ModuleOffset) {
ModuleInfo *Info = getOrCreateModuleInfo(ModuleName);
- if (Info == 0)
+ if (!Info)
return printDILineInfo(DILineInfo());
if (Opts.PrintInlining) {
DIInliningInfo InlinedContext =
uint64_t Size = 0;
if (Opts.UseSymbolTable) {
if (ModuleInfo *Info = getOrCreateModuleInfo(ModuleName)) {
- if (Info->symbolizeData(ModuleOffset, Name, Start, Size))
- DemangleName(Name);
+ if (Info->symbolizeData(ModuleOffset, Name, Start, Size) && Opts.Demangle)
+ Name = DemangleName(Name);
}
}
std::stringstream ss;
return ss.str();
}
-// 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<MemoryBuffer> Buff;
- if (error_code ec = MemoryBuffer::getFile(Path, Buff))
- error(ec);
- return ObjectFile::createObjectFile(Buff.take());
+void LLVMSymbolizer::flush() {
+ DeleteContainerSeconds(Modules);
+ BinaryForPath.clear();
+ ObjectFileForArch.clear();
}
-static std::string getDarwinDWARFResourceForModule(const std::string &Path) {
+static std::string getDarwinDWARFResourceForPath(const std::string &Path) {
StringRef Basename = sys::path::filename(Path);
const std::string &DSymDirectory = Path + ".dSYM";
SmallString<16> ResourceName = StringRef(DSymDirectory);
return ResourceName.str();
}
+static bool checkFileCRC(StringRef Path, uint32_t CRCHash) {
+ std::unique_ptr<MemoryBuffer> MB;
+ if (MemoryBuffer::getFileOrSTDIN(Path, MB))
+ return false;
+ return !zlib::isAvailable() || CRCHash == zlib::crc32(MB->getBuffer());
+}
+
+static bool findDebugBinary(const std::string &OrigPath,
+ const std::string &DebuglinkName, uint32_t CRCHash,
+ std::string &Result) {
+ std::string OrigRealPath = OrigPath;
+#if defined(HAVE_REALPATH)
+ if (char *RP = realpath(OrigPath.c_str(), nullptr)) {
+ OrigRealPath = RP;
+ free(RP);
+ }
+#endif
+ SmallString<16> OrigDir(OrigRealPath);
+ llvm::sys::path::remove_filename(OrigDir);
+ SmallString<16> DebugPath = OrigDir;
+ // Try /path/to/original_binary/debuglink_name
+ llvm::sys::path::append(DebugPath, DebuglinkName);
+ if (checkFileCRC(DebugPath, CRCHash)) {
+ Result = DebugPath.str();
+ return true;
+ }
+ // Try /path/to/original_binary/.debug/debuglink_name
+ DebugPath = OrigRealPath;
+ llvm::sys::path::append(DebugPath, ".debug", DebuglinkName);
+ if (checkFileCRC(DebugPath, CRCHash)) {
+ Result = DebugPath.str();
+ return true;
+ }
+ // Try /usr/lib/debug/path/to/original_binary/debuglink_name
+ DebugPath = "/usr/lib/debug";
+ llvm::sys::path::append(DebugPath, llvm::sys::path::relative_path(OrigDir),
+ DebuglinkName);
+ if (checkFileCRC(DebugPath, CRCHash)) {
+ Result = DebugPath.str();
+ return true;
+ }
+ return false;
+}
+
+static bool getGNUDebuglinkContents(const Binary *Bin, std::string &DebugName,
+ uint32_t &CRCHash) {
+ const ObjectFile *Obj = dyn_cast<ObjectFile>(Bin);
+ if (!Obj)
+ return false;
+ for (const SectionRef &Section : Obj->sections()) {
+ StringRef Name;
+ Section.getName(Name);
+ Name = Name.substr(Name.find_first_not_of("._"));
+ if (Name == "gnu_debuglink") {
+ StringRef Data;
+ Section.getContents(Data);
+ DataExtractor DE(Data, Obj->isLittleEndian(), 0);
+ uint32_t Offset = 0;
+ if (const char *DebugNameStr = DE.getCStr(&Offset)) {
+ // 4-byte align the offset.
+ Offset = (Offset + 3) & ~0x3;
+ if (DE.isValidOffsetForDataOfSize(Offset, 4)) {
+ DebugName = DebugNameStr;
+ CRCHash = DE.getU32(&Offset);
+ return true;
+ }
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+LLVMSymbolizer::BinaryPair
+LLVMSymbolizer::getOrCreateBinary(const std::string &Path) {
+ BinaryMapTy::iterator I = BinaryForPath.find(Path);
+ if (I != BinaryForPath.end())
+ return I->second;
+ Binary *Bin = nullptr;
+ Binary *DbgBin = nullptr;
+ ErrorOr<Binary *> BinaryOrErr = createBinary(Path);
+ if (!error(BinaryOrErr.getError())) {
+ std::unique_ptr<Binary> ParsedBinary(BinaryOrErr.get());
+ // Check if it's a universal binary.
+ Bin = ParsedBinary.get();
+ ParsedBinariesAndObjects.push_back(std::move(ParsedBinary));
+ if (Bin->isMachO() || Bin->isMachOUniversalBinary()) {
+ // On Darwin we may find DWARF in separate object file in
+ // resource directory.
+ const std::string &ResourcePath =
+ getDarwinDWARFResourceForPath(Path);
+ BinaryOrErr = createBinary(ResourcePath);
+ error_code EC = BinaryOrErr.getError();
+ if (EC != std::errc::no_such_file_or_directory && !error(EC)) {
+ DbgBin = BinaryOrErr.get();
+ ParsedBinariesAndObjects.push_back(std::unique_ptr<Binary>(DbgBin));
+ }
+ }
+ // Try to locate the debug binary using .gnu_debuglink section.
+ if (!DbgBin) {
+ std::string DebuglinkName;
+ uint32_t CRCHash;
+ std::string DebugBinaryPath;
+ if (getGNUDebuglinkContents(Bin, DebuglinkName, CRCHash) &&
+ findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath)) {
+ BinaryOrErr = createBinary(DebugBinaryPath);
+ if (!error(BinaryOrErr.getError())) {
+ DbgBin = BinaryOrErr.get();
+ ParsedBinariesAndObjects.push_back(std::unique_ptr<Binary>(DbgBin));
+ }
+ }
+ }
+ }
+ if (!DbgBin)
+ DbgBin = Bin;
+ BinaryPair Res = std::make_pair(Bin, DbgBin);
+ BinaryForPath[Path] = Res;
+ return Res;
+}
+
+ObjectFile *
+LLVMSymbolizer::getObjectFileFromBinary(Binary *Bin, const std::string &ArchName) {
+ if (!Bin)
+ return nullptr;
+ ObjectFile *Res = nullptr;
+ if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(Bin)) {
+ ObjectFileForArchMapTy::iterator I = ObjectFileForArch.find(
+ std::make_pair(UB, ArchName));
+ if (I != ObjectFileForArch.end())
+ return I->second;
+ std::unique_ptr<ObjectFile> ParsedObj;
+ if (!UB->getObjectForArch(Triple(ArchName).getArch(), ParsedObj)) {
+ Res = ParsedObj.get();
+ ParsedBinariesAndObjects.push_back(std::move(ParsedObj));
+ }
+ ObjectFileForArch[std::make_pair(UB, ArchName)] = Res;
+ } else if (Bin->isObject()) {
+ Res = cast<ObjectFile>(Bin);
+ }
+ return Res;
+}
+
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<MachOObjectFile>(Obj)) {
- const std::string &ResourceName =
- getDarwinDWARFResourceForModule(ModuleName);
- ObjectFile *ResourceObj = getObjectFile(ResourceName);
- if (ResourceObj != 0)
- DbgObj = ResourceObj;
+ 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.
+ 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;
}
- Context = DIContext::getDWARFContext(DbgObj);
- assert(Context);
}
+ BinaryPair Binaries = getOrCreateBinary(BinaryName);
+ ObjectFile *Obj = getObjectFileFromBinary(Binaries.first, ArchName);
+ ObjectFile *DbgObj = getObjectFileFromBinary(Binaries.second, ArchName);
+ if (!Obj) {
+ // Failed to find valid object file.
+ Modules.insert(make_pair(ModuleName, (ModuleInfo *)nullptr));
+ return nullptr;
+ }
+ DIContext *Context = DIContext::getDWARFContext(DbgObj);
+ assert(Context);
ModuleInfo *Info = new ModuleInfo(Obj, Context);
Modules.insert(make_pair(ModuleName, Info));
return Info;
// cannot fetch. We replace it to "??" to make our output closer to addr2line.
static const std::string kDILineInfoBadString = "<invalid>";
std::stringstream Result;
- if (Opts.PrintFunctions) {
- std::string FunctionName = LineInfo.getFunctionName();
+ if (Opts.PrintFunctions != FunctionNameKind::None) {
+ std::string FunctionName = LineInfo.FunctionName;
if (FunctionName == kDILineInfoBadString)
FunctionName = kBadString;
- DemangleName(FunctionName);
+ else if (Opts.Demangle)
+ FunctionName = DemangleName(FunctionName);
Result << FunctionName << "\n";
}
- std::string Filename = LineInfo.getFileName();
+ std::string Filename = LineInfo.FileName;
if (Filename == kDILineInfoBadString)
Filename = kBadString;
- Result << Filename << ":" << LineInfo.getLine() << ":" << LineInfo.getColumn()
- << "\n";
+ Result << Filename << ":" << LineInfo.Line << ":" << LineInfo.Column << "\n";
return Result.str();
}
size_t *length, int *status);
#endif
-void LLVMSymbolizer::DemangleName(std::string &Name) const {
+std::string LLVMSymbolizer::DemangleName(const std::string &Name) {
#if !defined(_MSC_VER)
- if (!Opts.Demangle)
- return;
+ // We can spoil names of symbols with C linkage, so use an heuristic
+ // approach to check if the name should be demangled.
+ if (Name.substr(0, 2) != "_Z")
+ return Name;
int status = 0;
- char *DemangledName = __cxa_demangle(Name.c_str(), 0, 0, &status);
+ char *DemangledName = __cxa_demangle(Name.c_str(), nullptr, nullptr, &status);
if (status != 0)
- return;
- Name = DemangledName;
+ return Name;
+ std::string Result = DemangledName;
free(DemangledName);
+ return Result;
+#else
+ return Name;
#endif
}