diff --git a/include/llvm/Object/COFF.h b/include/llvm/Object/COFF.h index 731ae2192e5..f859b7dea1a 100644 --- a/include/llvm/Object/COFF.h +++ b/include/llvm/Object/COFF.h @@ -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 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_directories() const; iterator_range delay_import_directories() const; iterator_range export_directories() const; iterator_range base_relocs() const; + iterator_range 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 &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; } diff --git a/include/llvm/Support/COFF.h b/include/llvm/Support/COFF.h index 3c0abe1f466..a9328aa7803 100644 --- a/include/llvm/Support/COFF.h +++ b/include/llvm/Support/COFF.h @@ -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 { diff --git a/lib/Object/COFFObjectFile.cpp b/lib/Object/COFFObjectFile.cpp index 5f464969adb..8390edda3bf 100644 --- a/lib/Object/COFFObjectFile.cpp +++ b/lib/Object/COFFObjectFile.cpp @@ -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 &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(reinterpret_cast(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 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(InfoBytes.data()); + InfoBytes = InfoBytes.drop_front(sizeof(debug_pdb_info)); + PDBFileName = StringRef(reinterpret_cast(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(IntPtr); + if (std::error_code EC = getRvaPtr( + DataEntry->RelativeVirtualAddress + DataEntry->Size, IntPtr)) + return EC; + DebugDirectoryEnd = reinterpret_cast(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(); } diff --git a/lib/ObjectYAML/COFFYAML.cpp b/lib/ObjectYAML/COFFYAML.cpp index f3a4f005eb9..02ae0535615 100644 --- a/lib/ObjectYAML/COFFYAML.cpp +++ b/lib/ObjectYAML/COFFYAML.cpp @@ -397,7 +397,7 @@ void MappingTraits::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]); diff --git a/test/tools/llvm-readobj/Inputs/has_pdb.exe b/test/tools/llvm-readobj/Inputs/has_pdb.exe new file mode 100644 index 00000000000..60cc9f59fc1 Binary files /dev/null and b/test/tools/llvm-readobj/Inputs/has_pdb.exe differ diff --git a/test/tools/llvm-readobj/coff-debug-directory.test b/test/tools/llvm-readobj/coff-debug-directory.test new file mode 100644 index 00000000000..f5c31c77abf --- /dev/null +++ b/test/tools/llvm-readobj/coff-debug-directory.test @@ -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: ] diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index fccfd465c57..306ebf90b87 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -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 ImageCOMDATSelect[] = { { "Newest" , COFF::IMAGE_COMDAT_SELECT_NEWEST } }; +static const EnumEntry 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 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 RawData; + error( + Obj->getRvaAndSizeAsBytes(D.AddressOfRawData, D.SizeOfData, RawData)); + W.printBinaryBlock("RawData", RawData); } } } diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index c6f7c64ced5..a39fc260b54 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -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) {} diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index 386b32b6cc7..edeb8c95484 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -196,6 +196,11 @@ namespace opts { COFFBaseRelocs("coff-basereloc", cl::desc("Display the PE/COFF .reloc section")); + // -coff-debug-directory + cl::opt + COFFDebugDirectory("coff-debug-directory", + cl::desc("Display the PE/COFF debug directory")); + // -macho-data-in-code cl::opt 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)