Update the MemoryBuffer API to use ErrorOr.
[oota-llvm.git] / tools / llvm-symbolizer / LLVMSymbolize.cpp
index 4f6e0cf6dc7925f25805dfef1f8bb2c7d4436af0..c1d39efc1b81bada0fe776e3f2eac846718fbcea 100644 (file)
 
 #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/Errc.h"
 #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/Path.h"
-
 #include <sstream>
+#include <stdlib.h>
 
 namespace llvm {
 namespace symbolize {
 
-static bool error(error_code ec) {
+static bool error(std::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());
+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;
-    // 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>(Obj))
-      SymbolSize = 0;
-    else if (error(si->getSize(SymbolSize)) ||
-             SymbolSize == UnknownAddressOrSize)
-      continue;
-    StringRef SymbolName;
-    if (error(si->getName(SymbolName)))
-      continue;
-    // 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));
+  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,
@@ -109,15 +114,15 @@ DILineInfo ModuleInfo::symbolizeCode(
   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;
@@ -128,14 +133,14 @@ DIInliningInfo ModuleInfo::symbolizeInlinedCode(
   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);
@@ -144,7 +149,7 @@ DIInliningInfo ModuleInfo::symbolizeInlinedCode(
         uint64_t Start, Size;
         if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
                                    FunctionName, Start, Size)) {
-          patchFunctionNameInDILineInfo(FunctionName, LineInfo);
+          LineInfo.FunctionName = FunctionName;
         }
       }
       PatchedInlinedContext.addFrame(LineInfo);
@@ -165,7 +170,7 @@ const char LLVMSymbolizer::kBadString[] = "??";
 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 =
@@ -201,7 +206,6 @@ std::string LLVMSymbolizer::symbolizeData(const std::string &ModuleName,
 
 void LLVMSymbolizer::flush() {
   DeleteContainerSeconds(Modules);
-  DeleteContainerPointers(ParsedBinariesAndObjects);
   BinaryForPath.clear();
   ObjectFileForArch.clear();
 }
@@ -215,34 +219,121 @@ static std::string getDarwinDWARFResourceForPath(const std::string &Path) {
   return ResourceName.str();
 }
 
+static bool checkFileCRC(StringRef Path, uint32_t CRCHash) {
+  ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
+      MemoryBuffer::getFileOrSTDIN(Path);
+  if (!MB)
+    return false;
+  return !zlib::isAvailable() || CRCHash == zlib::crc32(MB.get()->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 = 0;
-  Binary *DbgBin = 0;
-  OwningPtr<Binary> ParsedBinary;
-  OwningPtr<Binary> ParsedDbgBinary;
-  if (!error(createBinary(Path, ParsedBinary))) {
+  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.take();
-    ParsedBinariesAndObjects.push_back(Bin);
+    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);
-      bool ResourceFileExists = false;
-      if (!sys::fs::exists(ResourcePath, ResourceFileExists) &&
-          ResourceFileExists &&
-          !error(createBinary(ResourcePath, ParsedDbgBinary))) {
-        DbgBin = ParsedDbgBinary.take();
-        ParsedBinariesAndObjects.push_back(DbgBin);
+      BinaryOrErr = createBinary(ResourcePath);
+      std::error_code EC = BinaryOrErr.getError();
+      if (EC != 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 == 0)
+  if (!DbgBin)
     DbgBin = Bin;
   BinaryPair Res = std::make_pair(Bin, DbgBin);
   BinaryForPath[Path] = Res;
@@ -251,18 +342,19 @@ LLVMSymbolizer::getOrCreateBinary(const std::string &Path) {
 
 ObjectFile *
 LLVMSymbolizer::getObjectFileFromBinary(Binary *Bin, const std::string &ArchName) {
-  if (Bin == 0)
-    return 0;
-  ObjectFile *Res = 0;
+  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;
-    OwningPtr<ObjectFile> ParsedObj;
-    if (!UB->getObjectForArch(Triple(ArchName).getArch(), ParsedObj)) {
-      Res = ParsedObj.take();
-      ParsedBinariesAndObjects.push_back(Res);
+    ErrorOr<std::unique_ptr<ObjectFile>> 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;
   } else if (Bin->isObject()) {
@@ -282,7 +374,7 @@ LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) {
   // 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::ArchType::UnknownArch) {
+    if (Triple(ArchStr).getArch() != Triple::UnknownArch) {
       BinaryName = ModuleName.substr(0, ColonPos);
       ArchName = ArchStr;
     }
@@ -291,10 +383,10 @@ LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) {
   ObjectFile *Obj = getObjectFileFromBinary(Binaries.first, ArchName);
   ObjectFile *DbgObj = getObjectFileFromBinary(Binaries.second, ArchName);
 
-  if (Obj == 0) {
+  if (!Obj) {
     // Failed to find valid object file.
-    Modules.insert(make_pair(ModuleName, (ModuleInfo *)0));
-    return 0;
+    Modules.insert(make_pair(ModuleName, (ModuleInfo *)nullptr));
+    return nullptr;
   }
   DIContext *Context = DIContext::getDWARFContext(DbgObj);
   assert(Context);
@@ -308,19 +400,18 @@ std::string LLVMSymbolizer::printDILineInfo(DILineInfo LineInfo) const {
   // 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;
     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();
 }
 
@@ -332,8 +423,12 @@ extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer,
 
 std::string LLVMSymbolizer::DemangleName(const std::string &Name) {
 #if !defined(_MSC_VER)
+  // 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;
   std::string Result = DemangledName;