[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:
Reid Kleckner 2016-06-02 17:10:43 +00:00
parent 6393ef135c
commit 2da433ea99
9 changed files with 218 additions and 6 deletions

View File

@ -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; }

View File

@ -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 {

View File

@ -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();
}

View File

@ -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.

View File

@ -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: ]

View File

@ -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);
}
}
}

View File

@ -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) {}

View File

@ -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)