forked from OSchip/llvm-project
[COFF] Expose the PE debug data directory and dump it
This directory is used to find if there is a PDB associated with an executable. I plan to use this functionality to teach llvm-symbolizer whether it should use DIA or DWARF to symbolize a given DLL. Reviewers: majnemer Differential Revision: http://reviews.llvm.org/D20885 llvm-svn: 271539
This commit is contained in:
parent
6393ef135c
commit
2da433ea99
|
@ -169,6 +169,26 @@ struct import_directory_table_entry {
|
|||
support::ulittle32_t ImportAddressTableRVA;
|
||||
};
|
||||
|
||||
struct debug_directory {
|
||||
support::ulittle32_t Characteristics;
|
||||
support::ulittle32_t TimeDateStamp;
|
||||
support::ulittle16_t MajorVersion;
|
||||
support::ulittle16_t MinorVersion;
|
||||
support::ulittle32_t Type;
|
||||
support::ulittle32_t SizeOfData;
|
||||
support::ulittle32_t AddressOfRawData;
|
||||
support::ulittle32_t PointerToRawData;
|
||||
};
|
||||
|
||||
/// Information that is resent in debug_directory::AddressOfRawData if Type is
|
||||
/// IMAGE_DEBUG_TYPE_CODEVIEW.
|
||||
struct debug_pdb_info {
|
||||
support::ulittle32_t Signature;
|
||||
uint8_t Guid[16];
|
||||
support::ulittle32_t Age;
|
||||
// PDBFileName: The null-terminated PDB file name follows.
|
||||
};
|
||||
|
||||
template <typename IntTy>
|
||||
struct import_lookup_table_entry {
|
||||
IntTy Data;
|
||||
|
@ -620,6 +640,8 @@ private:
|
|||
const export_directory_table_entry *ExportDirectory;
|
||||
const coff_base_reloc_block_header *BaseRelocHeader;
|
||||
const coff_base_reloc_block_header *BaseRelocEnd;
|
||||
const debug_directory *DebugDirectoryBegin;
|
||||
const debug_directory *DebugDirectoryEnd;
|
||||
|
||||
std::error_code getString(uint32_t offset, StringRef &Res) const;
|
||||
|
||||
|
@ -633,6 +655,7 @@ private:
|
|||
std::error_code initDelayImportTablePtr();
|
||||
std::error_code initExportTablePtr();
|
||||
std::error_code initBaseRelocPtr();
|
||||
std::error_code initDebugDirectoryPtr();
|
||||
|
||||
public:
|
||||
uintptr_t getSymbolTable() const {
|
||||
|
@ -754,12 +777,21 @@ public:
|
|||
export_directory_iterator export_directory_end() const;
|
||||
base_reloc_iterator base_reloc_begin() const;
|
||||
base_reloc_iterator base_reloc_end() const;
|
||||
const debug_directory *debug_directory_begin() const {
|
||||
return DebugDirectoryBegin;
|
||||
}
|
||||
const debug_directory *debug_directory_end() const {
|
||||
return DebugDirectoryEnd;
|
||||
}
|
||||
|
||||
iterator_range<import_directory_iterator> import_directories() const;
|
||||
iterator_range<delay_import_directory_iterator>
|
||||
delay_import_directories() const;
|
||||
iterator_range<export_directory_iterator> export_directories() const;
|
||||
iterator_range<base_reloc_iterator> base_relocs() const;
|
||||
iterator_range<const debug_directory *> debug_directories() const {
|
||||
return make_range(debug_directory_begin(), debug_directory_end());
|
||||
}
|
||||
|
||||
const dos_header *getDOSHeader() const {
|
||||
if (!PE32Header && !PE32PlusHeader)
|
||||
|
@ -828,9 +860,20 @@ public:
|
|||
uint64_t getImageBase() const;
|
||||
std::error_code getVaPtr(uint64_t VA, uintptr_t &Res) const;
|
||||
std::error_code getRvaPtr(uint32_t Rva, uintptr_t &Res) const;
|
||||
|
||||
/// Given an RVA base and size, returns a valid array of bytes or an error
|
||||
/// code if the RVA and size is not contained completely within a valid
|
||||
/// section.
|
||||
std::error_code getRvaAndSizeAsBytes(uint32_t RVA, uint32_t Size,
|
||||
ArrayRef<uint8_t> &Contents) const;
|
||||
|
||||
std::error_code getHintName(uint32_t Rva, uint16_t &Hint,
|
||||
StringRef &Name) const;
|
||||
|
||||
std::error_code getDebugPDBInfo(const debug_directory *DebugDir,
|
||||
const debug_pdb_info *&Info,
|
||||
StringRef &PDBFileName) const;
|
||||
|
||||
bool isRelocatableObject() const override;
|
||||
bool is64() const { return PE32PlusHeader; }
|
||||
|
||||
|
|
|
@ -529,7 +529,7 @@ namespace COFF {
|
|||
EXCEPTION_TABLE,
|
||||
CERTIFICATE_TABLE,
|
||||
BASE_RELOCATION_TABLE,
|
||||
DEBUG,
|
||||
DEBUG_DIRECTORY,
|
||||
ARCHITECTURE,
|
||||
GLOBAL_PTR,
|
||||
TLS_TABLE,
|
||||
|
@ -598,7 +598,12 @@ namespace COFF {
|
|||
IMAGE_DEBUG_TYPE_OMAP_TO_SRC = 7,
|
||||
IMAGE_DEBUG_TYPE_OMAP_FROM_SRC = 8,
|
||||
IMAGE_DEBUG_TYPE_BORLAND = 9,
|
||||
IMAGE_DEBUG_TYPE_CLSID = 11
|
||||
IMAGE_DEBUG_TYPE_CLSID = 11,
|
||||
IMAGE_DEBUG_TYPE_VC_FEATURE = 12,
|
||||
IMAGE_DEBUG_TYPE_POGO = 13,
|
||||
IMAGE_DEBUG_TYPE_ILTCG = 14,
|
||||
IMAGE_DEBUG_TYPE_MPX = 15,
|
||||
IMAGE_DEBUG_TYPE_NO_TIMESTAMP = 16,
|
||||
};
|
||||
|
||||
enum BaseRelocationType {
|
||||
|
|
|
@ -453,6 +453,27 @@ std::error_code COFFObjectFile::getRvaPtr(uint32_t Addr, uintptr_t &Res) const {
|
|||
return object_error::parse_failed;
|
||||
}
|
||||
|
||||
std::error_code
|
||||
COFFObjectFile::getRvaAndSizeAsBytes(uint32_t RVA, uint32_t Size,
|
||||
ArrayRef<uint8_t> &Contents) const {
|
||||
for (const SectionRef &S : sections()) {
|
||||
const coff_section *Section = getCOFFSection(S);
|
||||
uint32_t SectionStart = Section->VirtualAddress;
|
||||
// Check if this RVA is within the section bounds. Be careful about integer
|
||||
// overflow.
|
||||
uint32_t OffsetIntoSection = RVA - SectionStart;
|
||||
if (SectionStart <= RVA && OffsetIntoSection < Section->VirtualSize &&
|
||||
Size <= Section->VirtualSize - OffsetIntoSection) {
|
||||
uintptr_t Begin =
|
||||
uintptr_t(base()) + Section->PointerToRawData + OffsetIntoSection;
|
||||
Contents =
|
||||
ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(Begin), Size);
|
||||
return std::error_code();
|
||||
}
|
||||
}
|
||||
return object_error::parse_failed;
|
||||
}
|
||||
|
||||
// Returns hint and name fields, assuming \p Rva is pointing to a Hint/Name
|
||||
// table entry.
|
||||
std::error_code COFFObjectFile::getHintName(uint32_t Rva, uint16_t &Hint,
|
||||
|
@ -466,6 +487,24 @@ std::error_code COFFObjectFile::getHintName(uint32_t Rva, uint16_t &Hint,
|
|||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code COFFObjectFile::getDebugPDBInfo(const debug_directory *DebugDir,
|
||||
const debug_pdb_info *&PDBInfo,
|
||||
StringRef &PDBFileName) const {
|
||||
ArrayRef<uint8_t> InfoBytes;
|
||||
if (std::error_code EC = getRvaAndSizeAsBytes(
|
||||
DebugDir->AddressOfRawData, DebugDir->SizeOfData, InfoBytes))
|
||||
return EC;
|
||||
if (InfoBytes.size() < sizeof(debug_pdb_info) + 1)
|
||||
return object_error::parse_failed;
|
||||
PDBInfo = reinterpret_cast<const debug_pdb_info *>(InfoBytes.data());
|
||||
InfoBytes = InfoBytes.drop_front(sizeof(debug_pdb_info));
|
||||
PDBFileName = StringRef(reinterpret_cast<const char *>(InfoBytes.data()),
|
||||
InfoBytes.size());
|
||||
// Truncate the name at the first null byte. Ignore any padding.
|
||||
PDBFileName = PDBFileName.split('\0').first;
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
// Find the import table.
|
||||
std::error_code COFFObjectFile::initImportTablePtr() {
|
||||
// First, we get the RVA of the import table. If the file lacks a pointer to
|
||||
|
@ -551,6 +590,31 @@ std::error_code COFFObjectFile::initBaseRelocPtr() {
|
|||
return std::error_code();
|
||||
}
|
||||
|
||||
std::error_code COFFObjectFile::initDebugDirectoryPtr() {
|
||||
// Get the RVA of the debug directory. Do nothing if it does not exist.
|
||||
const data_directory *DataEntry;
|
||||
if (getDataDirectory(COFF::DEBUG_DIRECTORY, DataEntry))
|
||||
return std::error_code();
|
||||
|
||||
// Do nothing if the RVA is NULL.
|
||||
if (DataEntry->RelativeVirtualAddress == 0)
|
||||
return std::error_code();
|
||||
|
||||
// Check that the size is a multiple of the entry size.
|
||||
if (DataEntry->Size % sizeof(debug_directory) != 0)
|
||||
return object_error::parse_failed;
|
||||
|
||||
uintptr_t IntPtr = 0;
|
||||
if (std::error_code EC = getRvaPtr(DataEntry->RelativeVirtualAddress, IntPtr))
|
||||
return EC;
|
||||
DebugDirectoryBegin = reinterpret_cast<const debug_directory *>(IntPtr);
|
||||
if (std::error_code EC = getRvaPtr(
|
||||
DataEntry->RelativeVirtualAddress + DataEntry->Size, IntPtr))
|
||||
return EC;
|
||||
DebugDirectoryEnd = reinterpret_cast<const debug_directory *>(IntPtr);
|
||||
return std::error_code();
|
||||
}
|
||||
|
||||
COFFObjectFile::COFFObjectFile(MemoryBufferRef Object, std::error_code &EC)
|
||||
: ObjectFile(Binary::ID_COFF, Object), COFFHeader(nullptr),
|
||||
COFFBigObjHeader(nullptr), PE32Header(nullptr), PE32PlusHeader(nullptr),
|
||||
|
@ -558,8 +622,8 @@ COFFObjectFile::COFFObjectFile(MemoryBufferRef Object, std::error_code &EC)
|
|||
SymbolTable32(nullptr), StringTable(nullptr), StringTableSize(0),
|
||||
ImportDirectory(nullptr), NumberOfImportDirectory(0),
|
||||
DelayImportDirectory(nullptr), NumberOfDelayImportDirectory(0),
|
||||
ExportDirectory(nullptr), BaseRelocHeader(nullptr),
|
||||
BaseRelocEnd(nullptr) {
|
||||
ExportDirectory(nullptr), BaseRelocHeader(nullptr), BaseRelocEnd(nullptr),
|
||||
DebugDirectoryBegin(nullptr), DebugDirectoryEnd(nullptr) {
|
||||
// Check that we at least have enough room for a header.
|
||||
if (!checkSize(Data, EC, sizeof(coff_file_header)))
|
||||
return;
|
||||
|
@ -675,6 +739,10 @@ COFFObjectFile::COFFObjectFile(MemoryBufferRef Object, std::error_code &EC)
|
|||
if ((EC = initBaseRelocPtr()))
|
||||
return;
|
||||
|
||||
// Initialize the pointer to the export table.
|
||||
if ((EC = initDebugDirectoryPtr()))
|
||||
return;
|
||||
|
||||
EC = std::error_code();
|
||||
}
|
||||
|
||||
|
|
|
@ -397,7 +397,7 @@ void MappingTraits<COFFYAML::PEHeader>::mapping(IO &IO,
|
|||
IO.mapOptional("CertificateTable", PH.DataDirectories[COFF::CERTIFICATE_TABLE]);
|
||||
IO.mapOptional("BaseRelocationTable",
|
||||
PH.DataDirectories[COFF::BASE_RELOCATION_TABLE]);
|
||||
IO.mapOptional("Debug", PH.DataDirectories[COFF::DEBUG]);
|
||||
IO.mapOptional("Debug", PH.DataDirectories[COFF::DEBUG_DIRECTORY]);
|
||||
IO.mapOptional("Architecture", PH.DataDirectories[COFF::ARCHITECTURE]);
|
||||
IO.mapOptional("GlobalPtr", PH.DataDirectories[COFF::GLOBAL_PTR]);
|
||||
IO.mapOptional("TlsTable", PH.DataDirectories[COFF::TLS_TABLE]);
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,34 @@
|
|||
RUN: llvm-readobj -coff-debug-directory %p/Inputs/has_pdb.exe | FileCheck %s
|
||||
|
||||
CHECK: DebugDirectory [
|
||||
CHECK: DebugEntry {
|
||||
CHECK: Characteristics: 0x0
|
||||
CHECK: TimeDateStamp: 2016-06-01 22:53:16 (0x574F675C)
|
||||
CHECK: MajorVersion: 0x0
|
||||
CHECK: MinorVersion: 0x0
|
||||
CHECK: Type: CodeView (0x2)
|
||||
CHECK: SizeOfData: 0x36
|
||||
CHECK: AddressOfRawData: 0x5B068
|
||||
CHECK: PointerToRawData: 0x5A268
|
||||
CHECK: PDBInfo {
|
||||
CHECK: PDBSignature: 0x53445352
|
||||
CHECK: PDBGUID: (96 83 40 42 81 07 9D 40 90 1B 4A 3C 0D 4F 56 32)
|
||||
CHECK: PDBAge: 3
|
||||
CHECK: PDBFileName: D:\src\llvm\build\has_pdb.pdb
|
||||
CHECK: }
|
||||
CHECK: }
|
||||
CHECK: DebugEntry {
|
||||
CHECK: Characteristics: 0x0
|
||||
CHECK: TimeDateStamp: 2016-06-01 22:53:16 (0x574F675C)
|
||||
CHECK: MajorVersion: 0x0
|
||||
CHECK: MinorVersion: 0x0
|
||||
CHECK: Type: VCFeature (0xC)
|
||||
CHECK: SizeOfData: 0x14
|
||||
CHECK: AddressOfRawData: 0x5B0A0
|
||||
CHECK: PointerToRawData: 0x5A2A0
|
||||
CHECK: RawData (
|
||||
CHECK: 0000: 00000000 C1000000 C1000000 00000000 |................|
|
||||
CHECK: 0010: C0000000 |....|
|
||||
CHECK: )
|
||||
CHECK: }
|
||||
CHECK: ]
|
|
@ -75,6 +75,7 @@ public:
|
|||
void printCOFFExports() override;
|
||||
void printCOFFDirectives() override;
|
||||
void printCOFFBaseReloc() override;
|
||||
void printCOFFDebugDirectory() override;
|
||||
void printCodeViewDebugInfo() override;
|
||||
void
|
||||
mergeCodeViewTypes(llvm::codeview::MemoryTypeTableBuilder &CVTypes) override;
|
||||
|
@ -475,6 +476,25 @@ static const EnumEntry<COFF::COMDATType> ImageCOMDATSelect[] = {
|
|||
{ "Newest" , COFF::IMAGE_COMDAT_SELECT_NEWEST }
|
||||
};
|
||||
|
||||
static const EnumEntry<COFF::DebugType> ImageDebugType[] = {
|
||||
{ "Unknown" , COFF::IMAGE_DEBUG_TYPE_UNKNOWN },
|
||||
{ "COFF" , COFF::IMAGE_DEBUG_TYPE_COFF },
|
||||
{ "CodeView" , COFF::IMAGE_DEBUG_TYPE_CODEVIEW },
|
||||
{ "FPO" , COFF::IMAGE_DEBUG_TYPE_FPO },
|
||||
{ "Misc" , COFF::IMAGE_DEBUG_TYPE_MISC },
|
||||
{ "Exception" , COFF::IMAGE_DEBUG_TYPE_EXCEPTION },
|
||||
{ "Fixup" , COFF::IMAGE_DEBUG_TYPE_FIXUP },
|
||||
{ "OmapToSrc" , COFF::IMAGE_DEBUG_TYPE_OMAP_TO_SRC },
|
||||
{ "OmapFromSrc", COFF::IMAGE_DEBUG_TYPE_OMAP_FROM_SRC },
|
||||
{ "Borland" , COFF::IMAGE_DEBUG_TYPE_BORLAND },
|
||||
{ "CLSID" , COFF::IMAGE_DEBUG_TYPE_CLSID },
|
||||
{ "VCFeature" , COFF::IMAGE_DEBUG_TYPE_VC_FEATURE },
|
||||
{ "POGO" , COFF::IMAGE_DEBUG_TYPE_POGO },
|
||||
{ "ILTCG" , COFF::IMAGE_DEBUG_TYPE_ILTCG },
|
||||
{ "MPX" , COFF::IMAGE_DEBUG_TYPE_MPX },
|
||||
{ "NoTimestamp", COFF::IMAGE_DEBUG_TYPE_NO_TIMESTAMP },
|
||||
};
|
||||
|
||||
static const EnumEntry<COFF::WeakExternalCharacteristics>
|
||||
WeakExternalCharacteristics[] = {
|
||||
{ "NoLibrary", COFF::IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY },
|
||||
|
@ -643,8 +663,42 @@ void COFFDumper::printPEHeader(const PEHeader *Hdr) {
|
|||
"DelayImportDescriptor", "CLRRuntimeHeader", "Reserved"
|
||||
};
|
||||
|
||||
for (uint32_t i = 0; i < Hdr->NumberOfRvaAndSize; ++i) {
|
||||
for (uint32_t i = 0; i < Hdr->NumberOfRvaAndSize; ++i)
|
||||
printDataDirectory(i, directory[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void COFFDumper::printCOFFDebugDirectory() {
|
||||
ListScope LS(W, "DebugDirectory");
|
||||
for (const debug_directory &D : Obj->debug_directories()) {
|
||||
char FormattedTime[20] = {};
|
||||
time_t TDS = D.TimeDateStamp;
|
||||
strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS));
|
||||
DictScope S(W, "DebugEntry");
|
||||
W.printHex("Characteristics", D.Characteristics);
|
||||
W.printHex("TimeDateStamp", FormattedTime, D.TimeDateStamp);
|
||||
W.printHex("MajorVersion", D.MajorVersion);
|
||||
W.printHex("MinorVersion", D.MinorVersion);
|
||||
W.printEnum("Type", D.Type, makeArrayRef(ImageDebugType));
|
||||
W.printHex("SizeOfData", D.SizeOfData);
|
||||
W.printHex("AddressOfRawData", D.AddressOfRawData);
|
||||
W.printHex("PointerToRawData", D.PointerToRawData);
|
||||
if (D.Type == COFF::IMAGE_DEBUG_TYPE_CODEVIEW) {
|
||||
const debug_pdb_info *PDBInfo;
|
||||
StringRef PDBFileName;
|
||||
error(Obj->getDebugPDBInfo(&D, PDBInfo, PDBFileName));
|
||||
DictScope PDBScope(W, "PDBInfo");
|
||||
W.printHex("PDBSignature", PDBInfo->Signature);
|
||||
W.printBinary("PDBGUID", makeArrayRef(PDBInfo->Guid));
|
||||
W.printNumber("PDBAge", PDBInfo->Age);
|
||||
W.printString("PDBFileName", PDBFileName);
|
||||
} else {
|
||||
// FIXME: Type values of 12 and 13 are commonly observed but are not in
|
||||
// the documented type enum. Figure out what they mean.
|
||||
ArrayRef<uint8_t> RawData;
|
||||
error(
|
||||
Obj->getRvaAndSizeAsBytes(D.AddressOfRawData, D.SizeOfData, RawData));
|
||||
W.printBinaryBlock("RawData", RawData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ public:
|
|||
virtual void printCOFFExports() { }
|
||||
virtual void printCOFFDirectives() { }
|
||||
virtual void printCOFFBaseReloc() { }
|
||||
virtual void printCOFFDebugDirectory() { }
|
||||
virtual void printCodeViewDebugInfo() { }
|
||||
virtual void
|
||||
mergeCodeViewTypes(llvm::codeview::MemoryTypeTableBuilder &CVTypes) {}
|
||||
|
|
|
@ -196,6 +196,11 @@ namespace opts {
|
|||
COFFBaseRelocs("coff-basereloc",
|
||||
cl::desc("Display the PE/COFF .reloc section"));
|
||||
|
||||
// -coff-debug-directory
|
||||
cl::opt<bool>
|
||||
COFFDebugDirectory("coff-debug-directory",
|
||||
cl::desc("Display the PE/COFF debug directory"));
|
||||
|
||||
// -macho-data-in-code
|
||||
cl::opt<bool>
|
||||
MachODataInCode("macho-data-in-code",
|
||||
|
@ -392,6 +397,8 @@ static void dumpObject(const ObjectFile *Obj) {
|
|||
Dumper->printCOFFDirectives();
|
||||
if (opts::COFFBaseRelocs)
|
||||
Dumper->printCOFFBaseReloc();
|
||||
if (opts::COFFDebugDirectory)
|
||||
Dumper->printCOFFDebugDirectory();
|
||||
if (opts::CodeView)
|
||||
Dumper->printCodeViewDebugInfo();
|
||||
if (opts::CodeViewMergedTypes)
|
||||
|
|
Loading…
Reference in New Issue