llvm-project/llvm/lib/DebugInfo/Symbolize/SymbolizableObjectFile.cpp

272 lines
10 KiB
C++
Raw Normal View History

//===- SymbolizableObjectFile.cpp -----------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementation of SymbolizableObjectFile class.
//
//===----------------------------------------------------------------------===//
#include "SymbolizableObjectFile.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Object/SymbolSize.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Error.h"
#include <algorithm>
#include <cstdint>
#include <memory>
#include <string>
#include <system_error>
#include <utility>
#include <vector>
using namespace llvm;
using namespace object;
using namespace symbolize;
static DILineInfoSpecifier
getDILineInfoSpecifier(FunctionNameKind FNKind) {
return DILineInfoSpecifier(
DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, FNKind);
}
ErrorOr<std::unique_ptr<SymbolizableObjectFile>>
SymbolizableObjectFile::create(object::ObjectFile *Obj,
std::unique_ptr<DIContext> DICtx) {
std::unique_ptr<SymbolizableObjectFile> res(
new SymbolizableObjectFile(Obj, std::move(DICtx)));
std::unique_ptr<DataExtractor> OpdExtractor;
uint64_t OpdAddress = 0;
// Find the .opd (function descriptor) section if any, for big-endian
// PowerPC64 ELF.
if (Obj->getArch() == Triple::ppc64) {
for (section_iterator Section : Obj->sections()) {
StringRef Name;
StringRef Data;
if (auto EC = Section->getName(Name))
return EC;
if (Name == ".opd") {
if (auto EC = Section->getContents(Data))
return EC;
OpdExtractor.reset(new DataExtractor(Data, Obj->isLittleEndian(),
Obj->getBytesInAddress()));
OpdAddress = Section->getAddress();
break;
}
}
}
std::vector<std::pair<SymbolRef, uint64_t>> Symbols =
computeSymbolSizes(*Obj);
for (auto &P : Symbols)
res->addSymbol(P.first, P.second, OpdExtractor.get(), OpdAddress);
// If this is a COFF object and we didn't find any symbols, try the export
// table.
if (Symbols.empty()) {
if (auto *CoffObj = dyn_cast<COFFObjectFile>(Obj))
if (auto EC = res->addCoffExportSymbols(CoffObj))
return EC;
}
return std::move(res);
}
SymbolizableObjectFile::SymbolizableObjectFile(ObjectFile *Obj,
std::unique_ptr<DIContext> DICtx)
: Module(Obj), DebugInfoContext(std::move(DICtx)) {}
namespace {
struct OffsetNamePair {
uint32_t Offset;
StringRef Name;
bool operator<(const OffsetNamePair &R) const {
return Offset < R.Offset;
}
};
} // end anonymous namespace
std::error_code SymbolizableObjectFile::addCoffExportSymbols(
const COFFObjectFile *CoffObj) {
// Get all export names and offsets.
std::vector<OffsetNamePair> ExportSyms;
for (const ExportDirectoryEntryRef &Ref : CoffObj->export_directories()) {
StringRef Name;
uint32_t Offset;
if (auto EC = Ref.getSymbolName(Name))
return EC;
if (auto EC = Ref.getExportRVA(Offset))
return EC;
ExportSyms.push_back(OffsetNamePair{Offset, Name});
}
if (ExportSyms.empty())
return std::error_code();
// Sort by ascending offset.
array_pod_sort(ExportSyms.begin(), ExportSyms.end());
// Approximate the symbol sizes by assuming they run to the next symbol.
// FIXME: This assumes all exports are functions.
uint64_t ImageBase = CoffObj->getImageBase();
for (auto I = ExportSyms.begin(), E = ExportSyms.end(); I != E; ++I) {
OffsetNamePair &Export = *I;
// FIXME: The last export has a one byte size now.
uint32_t NextOffset = I != E ? I->Offset : Export.Offset + 1;
uint64_t SymbolStart = ImageBase + Export.Offset;
uint64_t SymbolSize = NextOffset - Export.Offset;
SymbolDesc SD = {SymbolStart, SymbolSize};
Functions.insert(std::make_pair(SD, Export.Name));
}
return std::error_code();
}
std::error_code SymbolizableObjectFile::addSymbol(const SymbolRef &Symbol,
uint64_t SymbolSize,
DataExtractor *OpdExtractor,
uint64_t OpdAddress) {
Expected<SymbolRef::Type> SymbolTypeOrErr = Symbol.getType();
if (!SymbolTypeOrErr)
return errorToErrorCode(SymbolTypeOrErr.takeError());
Fix a crash in running llvm-objdump -t with an invalid Mach-O file already in the test suite. While this is not really an interesting tool and option to run on a Mach-O file to show the symbol table in a generic libObject format it shouldn’t crash. The reason for the crash was in MachOObjectFile::getSymbolType() when it was calling MachOObjectFile::getSymbolSection() without checking its return value for the error case. What makes this fix require a fair bit of diffs is that the method getSymbolType() is in the class ObjectFile defined without an ErrorOr<> so I needed to add that all the sub classes.  And all of the uses needed to be updated and the return value needed to be checked for the error case. The MachOObjectFile version of getSymbolType() “can” get an error in trying to come up with the libObject’s internal SymbolRef::Type when the Mach-O symbol symbol type is an N_SECT type because the code is trying to select from the SymbolRef::ST_Data or SymbolRef::ST_Function values for the SymbolRef::Type. And it needs the Mach-O section to use isData() and isBSS to determine if it will return SymbolRef::ST_Data. One other possible fix I considered is to simply return SymbolRef::ST_Other when MachOObjectFile::getSymbolSection() returned an error. But since in the past when I did such changes that “ate an error in the libObject code” I was asked instead to push the error out of the libObject code I chose not to implement the fix this way. As currently written both the COFF and ELF versions of getSymbolType() can’t get an error. But if isReservedSectionNumber() wanted to check for the two known negative values rather than allowing all negative values or the code wanted to add the same check as in getSymbolAddress() to use getSection() and check for the error then these versions of getSymbolType() could return errors. At the end of the day the error printed now is the generic “Invalid data was encountered while parsing the file” for object_error::parse_failed. In the future when we thread Lang’s new TypedError for recoverable error handling though libObject this will improve. And where the added // Diagnostic(… comment is, it would be changed to produce and error message like “bad section index (42) for symbol at index 8” for this case. llvm-svn: 264187
2016-03-24 04:27:00 +08:00
SymbolRef::Type SymbolType = *SymbolTypeOrErr;
if (SymbolType != SymbolRef::ST_Function && SymbolType != SymbolRef::ST_Data)
return std::error_code();
Expected<uint64_t> SymbolAddressOrErr = Symbol.getAddress();
if (!SymbolAddressOrErr)
return errorToErrorCode(SymbolAddressOrErr.takeError());
uint64_t SymbolAddress = *SymbolAddressOrErr;
if (OpdExtractor) {
// For big-endian PowerPC64 ELF, symbols in the .opd section refer to
// function descriptors. The first word of the descriptor is a pointer to
// the function's code.
// For the purposes of symbolization, pretend the symbol's address is that
// of the function's code, not the descriptor.
uint64_t OpdOffset = SymbolAddress - OpdAddress;
uint32_t OpdOffset32 = OpdOffset;
if (OpdOffset == OpdOffset32 &&
OpdExtractor->isValidOffsetForAddress(OpdOffset32))
SymbolAddress = OpdExtractor->getAddress(&OpdOffset32);
}
Thread Expected<...> up from libObject’s getName() for symbols to allow llvm-objdump to produce a good error message. Produce another specific error message for a malformed Mach-O file when a symbol’s string index is past the end of the string table. The existing test case in test/Object/macho-invalid.test for macho-invalid-symbol-name-past-eof now reports the error with the message indicating that a symbol at a specific index has a bad sting index and that bad string index value. Again converting interfaces to Expected<> from ErrorOr<> does involve touching a number of places. Where the existing code reported the error with a string message or an error code it was converted to do the same. There is some code for this that could be factored into a routine but I would like to leave that for the code owners post-commit to do as they want for handling an llvm::Error. An example of how this could be done is shown in the diff in lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h which had a Check() routine already for std::error_code so I added one like it for llvm::Error . Also there some were bugs in the existing code that did not deal with the old ErrorOr<> return values.  So now with Expected<> since they must be checked and the error handled, I added a TODO and a comment: “// TODO: Actually report errors helpfully” and a call something like consumeError(NameOrErr.takeError()) so the buggy code will not crash since needed to deal with the Error. Note there fixes needed to lld that goes along with this that I will commit right after this. So expect lld not to built after this commit and before the next one. llvm-svn: 266919
2016-04-21 05:24:34 +08:00
Expected<StringRef> SymbolNameOrErr = Symbol.getName();
if (!SymbolNameOrErr)
return errorToErrorCode(SymbolNameOrErr.takeError());
StringRef SymbolName = *SymbolNameOrErr;
// Mach-O symbol table names have leading underscore, skip it.
if (Module->isMachO() && !SymbolName.empty() && 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.
auto &M = SymbolType == SymbolRef::ST_Function ? Functions : Objects;
SymbolDesc SD = { SymbolAddress, SymbolSize };
M.insert(std::make_pair(SD, SymbolName));
return std::error_code();
}
// Return true if this is a 32-bit x86 PE COFF module.
bool SymbolizableObjectFile::isWin32Module() const {
auto *CoffObject = dyn_cast<COFFObjectFile>(Module);
return CoffObject && CoffObject->getMachine() == COFF::IMAGE_FILE_MACHINE_I386;
}
uint64_t SymbolizableObjectFile::getModulePreferredBase() const {
if (auto *CoffObject = dyn_cast<COFFObjectFile>(Module))
return CoffObject->getImageBase();
return 0;
}
bool SymbolizableObjectFile::getNameFromSymbolTable(SymbolRef::Type Type,
uint64_t Address,
std::string &Name,
uint64_t &Addr,
uint64_t &Size) const {
const auto &SymbolMap = Type == SymbolRef::ST_Function ? Functions : Objects;
if (SymbolMap.empty())
return false;
SymbolDesc SD = { Address, Address };
auto SymbolIterator = SymbolMap.upper_bound(SD);
if (SymbolIterator == SymbolMap.begin())
return false;
--SymbolIterator;
if (SymbolIterator->first.Size != 0 &&
SymbolIterator->first.Addr + SymbolIterator->first.Size <= Address)
return false;
Name = SymbolIterator->second.str();
Addr = SymbolIterator->first.Addr;
Size = SymbolIterator->first.Size;
return true;
}
bool SymbolizableObjectFile::shouldOverrideWithSymbolTable(
FunctionNameKind FNKind, bool UseSymbolTable) const {
// When DWARF is used with -gline-tables-only / -gmlt, the symbol table gives
// better answers for linkage names than the DIContext. Otherwise, we are
// probably using PEs and PDBs, and we shouldn't do the override. PE files
// generally only contain the names of exported symbols.
return FNKind == FunctionNameKind::LinkageName && UseSymbolTable &&
isa<DWARFContext>(DebugInfoContext.get());
}
DILineInfo SymbolizableObjectFile::symbolizeCode(uint64_t ModuleOffset,
FunctionNameKind FNKind,
bool UseSymbolTable) const {
DILineInfo LineInfo;
if (DebugInfoContext) {
LineInfo = DebugInfoContext->getLineInfoForAddress(
ModuleOffset, getDILineInfoSpecifier(FNKind));
}
// Override function name from symbol table if necessary.
if (shouldOverrideWithSymbolTable(FNKind, UseSymbolTable)) {
std::string FunctionName;
uint64_t Start, Size;
if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
FunctionName, Start, Size)) {
LineInfo.FunctionName = FunctionName;
}
}
return LineInfo;
}
DIInliningInfo SymbolizableObjectFile::symbolizeInlinedCode(
uint64_t ModuleOffset, FunctionNameKind FNKind, bool UseSymbolTable) const {
DIInliningInfo InlinedContext;
if (DebugInfoContext)
InlinedContext = DebugInfoContext->getInliningInfoForAddress(
ModuleOffset, getDILineInfoSpecifier(FNKind));
// 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 (shouldOverrideWithSymbolTable(FNKind, UseSymbolTable)) {
std::string FunctionName;
uint64_t Start, Size;
if (getNameFromSymbolTable(SymbolRef::ST_Function, ModuleOffset,
FunctionName, Start, Size)) {
InlinedContext.getMutableFrame(InlinedContext.getNumberOfFrames() - 1)
->FunctionName = FunctionName;
}
}
return InlinedContext;
}
DIGlobal SymbolizableObjectFile::symbolizeData(uint64_t ModuleOffset) const {
DIGlobal Res;
getNameFromSymbolTable(SymbolRef::ST_Data, ModuleOffset, Res.Name, Res.Start,
Res.Size);
return Res;
}