From: Reid Kleckner Date: Thu, 22 Jun 2017 01:10:29 +0000 (+0000) Subject: [llvm-readobj] Dump the COFF image load config X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=367f21dc721cc8cfd6c75b3a1cf6b28fbb482c04;p=llvm [llvm-readobj] Dump the COFF image load config This includes the safe SEH tables and the control flow guard function table. LLD will emit the guard table soon, and I need a tool that dumps them for testing. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@305979 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/Object/COFF.h b/include/llvm/Object/COFF.h index ae695a52959..bf0172822d3 100644 --- a/include/llvm/Object/COFF.h +++ b/include/llvm/Object/COFF.h @@ -562,8 +562,26 @@ struct coff_tls_directory { using coff_tls_directory32 = coff_tls_directory; using coff_tls_directory64 = coff_tls_directory; +/// Bits in control flow guard flags as we understand them. +enum class coff_guard_flags : uint32_t { + CFInstrumented = 0x00000100, + HasFidTable = 0x00000400, + ProtectDelayLoadIAT = 0x00001000, + DelayLoadIATSection = 0x00002000, // Delay load in separate section + HasLongJmpTable = 0x00010000, + FidTableHasFlags = 0x10000000, // Indicates that fid tables are 5 bytes +}; + +struct coff_load_config_code_integrity { + support::ulittle16_t Flags; + support::ulittle16_t Catalog; + support::ulittle32_t CatalogOffset; + support::ulittle32_t Reserved; +}; + +/// 32-bit load config (IMAGE_LOAD_CONFIG_DIRECTORY32) struct coff_load_configuration32 { - support::ulittle32_t Characteristics; + support::ulittle32_t Size; support::ulittle32_t TimeDateStamp; support::ulittle16_t MajorVersion; support::ulittle16_t MinorVersion; @@ -578,34 +596,81 @@ struct coff_load_configuration32 { support::ulittle32_t ProcessAffinityMask; support::ulittle32_t ProcessHeapFlags; support::ulittle16_t CSDVersion; - support::ulittle16_t Reserved; + support::ulittle16_t DependentLoadFlags; support::ulittle32_t EditList; support::ulittle32_t SecurityCookie; support::ulittle32_t SEHandlerTable; support::ulittle32_t SEHandlerCount; + + // Added in MSVC 2015 for /guard:cf. + support::ulittle32_t GuardCFCheckFunction; + support::ulittle32_t GuardCFCheckDispatch; + support::ulittle32_t GuardCFFunctionTable; + support::ulittle32_t GuardCFFunctionCount; + support::ulittle32_t GuardFlags; // coff_guard_flags + + // Added in MSVC 2017 + coff_load_config_code_integrity CodeIntegrity; + support::ulittle32_t GuardAddressTakenIatEntryTable; + support::ulittle32_t GuardAddressTakenIatEntryCount; + support::ulittle32_t GuardLongJumpTargetTable; + support::ulittle32_t GuardLongJumpTargetCount; + support::ulittle32_t DynamicValueRelocTable; + support::ulittle32_t CHPEMetadataPointer; + support::ulittle32_t GuardRFFailureRoutine; + support::ulittle32_t GuardRFFailureRoutineFunctionPointer; + support::ulittle32_t DynamicValueRelocTableOffset; + support::ulittle16_t DynamicValueRelocTableSection; + support::ulittle16_t Reserved2; + support::ulittle32_t GuardRFVerifyStackPointerFunctionPointer; + support::ulittle32_t HotPatchTableOffset; }; +/// 64-bit load config (IMAGE_LOAD_CONFIG_DIRECTORY64) struct coff_load_configuration64 { - support::ulittle32_t Characteristics; + support::ulittle32_t Size; support::ulittle32_t TimeDateStamp; support::ulittle16_t MajorVersion; support::ulittle16_t MinorVersion; support::ulittle32_t GlobalFlagsClear; support::ulittle32_t GlobalFlagsSet; support::ulittle32_t CriticalSectionDefaultTimeout; - support::ulittle32_t DeCommitFreeBlockThreshold; - support::ulittle32_t DeCommitTotalFreeThreshold; - support::ulittle32_t LockPrefixTable; - support::ulittle32_t MaximumAllocationSize; - support::ulittle32_t VirtualMemoryThreshold; - support::ulittle32_t ProcessAffinityMask; + support::ulittle64_t DeCommitFreeBlockThreshold; + support::ulittle64_t DeCommitTotalFreeThreshold; + support::ulittle64_t LockPrefixTable; + support::ulittle64_t MaximumAllocationSize; + support::ulittle64_t VirtualMemoryThreshold; + support::ulittle64_t ProcessAffinityMask; support::ulittle32_t ProcessHeapFlags; support::ulittle16_t CSDVersion; - support::ulittle16_t Reserved; - support::ulittle32_t EditList; + support::ulittle16_t DependentLoadFlags; + support::ulittle64_t EditList; support::ulittle64_t SecurityCookie; support::ulittle64_t SEHandlerTable; support::ulittle64_t SEHandlerCount; + + // Added in MSVC 2015 for /guard:cf. + support::ulittle64_t GuardCFCheckFunction; + support::ulittle64_t GuardCFCheckDispatch; + support::ulittle64_t GuardCFFunctionTable; + support::ulittle64_t GuardCFFunctionCount; + support::ulittle32_t GuardFlags; + + // Added in MSVC 2017 + coff_load_config_code_integrity CodeIntegrity; + support::ulittle64_t GuardAddressTakenIatEntryTable; + support::ulittle64_t GuardAddressTakenIatEntryCount; + support::ulittle64_t GuardLongJumpTargetTable; + support::ulittle64_t GuardLongJumpTargetCount; + support::ulittle64_t DynamicValueRelocTable; + support::ulittle64_t CHPEMetadataPointer; + support::ulittle64_t GuardRFFailureRoutine; + support::ulittle64_t GuardRFFailureRoutineFunctionPointer; + support::ulittle32_t DynamicValueRelocTableOffset; + support::ulittle16_t DynamicValueRelocTableSection; + support::ulittle16_t Reserved2; + support::ulittle64_t GuardRFVerifyStackPointerFunctionPointer; + support::ulittle32_t HotPatchTableOffset; }; struct coff_runtime_function_x64 { @@ -684,6 +749,8 @@ private: const coff_base_reloc_block_header *BaseRelocEnd; const debug_directory *DebugDirectoryBegin; const debug_directory *DebugDirectoryEnd; + // Either coff_load_configuration32 or coff_load_configuration64. + const void *LoadConfig; std::error_code getString(uint32_t offset, StringRef &Res) const; @@ -698,6 +765,7 @@ private: std::error_code initExportTablePtr(); std::error_code initBaseRelocPtr(); std::error_code initDebugDirectoryPtr(); + std::error_code initLoadConfigPtr(); public: uintptr_t getSymbolTable() const { @@ -775,6 +843,16 @@ public: return getRawNumberOfSymbols(); } + const coff_load_configuration32 *getLoadConfig32() const { + assert(!is64()); + return reinterpret_cast(LoadConfig); + } + + const coff_load_configuration64 *getLoadConfig64() const { + assert(is64()); + return reinterpret_cast(LoadConfig); + } + protected: void moveSymbolNext(DataRefImpl &Symb) const override; Expected getSymbolName(DataRefImpl Symb) const override; diff --git a/lib/Object/COFFObjectFile.cpp b/lib/Object/COFFObjectFile.cpp index 579c8dde366..9a760d86e7e 100644 --- a/lib/Object/COFFObjectFile.cpp +++ b/lib/Object/COFFObjectFile.cpp @@ -650,6 +650,23 @@ std::error_code COFFObjectFile::initDebugDirectoryPtr() { return std::error_code(); } +std::error_code COFFObjectFile::initLoadConfigPtr() { + // Get the RVA of the debug directory. Do nothing if it does not exist. + const data_directory *DataEntry; + if (getDataDirectory(COFF::LOAD_CONFIG_TABLE, DataEntry)) + return std::error_code(); + + // Do nothing if the RVA is NULL. + if (DataEntry->RelativeVirtualAddress == 0) + return std::error_code(); + uintptr_t IntPtr = 0; + if (std::error_code EC = getRvaPtr(DataEntry->RelativeVirtualAddress, IntPtr)) + return EC; + + LoadConfig = (const void *)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), @@ -784,6 +801,9 @@ COFFObjectFile::COFFObjectFile(MemoryBufferRef Object, std::error_code &EC) if ((EC = initDebugDirectoryPtr())) return; + if ((EC = initLoadConfigPtr())) + return; + EC = std::error_code(); } diff --git a/test/tools/llvm-readobj/Inputs/coff-load-config-x64.dll b/test/tools/llvm-readobj/Inputs/coff-load-config-x64.dll new file mode 100644 index 00000000000..26101f93c41 Binary files /dev/null and b/test/tools/llvm-readobj/Inputs/coff-load-config-x64.dll differ diff --git a/test/tools/llvm-readobj/Inputs/coff-load-config-x86.dll b/test/tools/llvm-readobj/Inputs/coff-load-config-x86.dll new file mode 100644 index 00000000000..c334c7be721 Binary files /dev/null and b/test/tools/llvm-readobj/Inputs/coff-load-config-x86.dll differ diff --git a/test/tools/llvm-readobj/coff-load-config.test b/test/tools/llvm-readobj/coff-load-config.test new file mode 100644 index 00000000000..97bd2e09292 --- /dev/null +++ b/test/tools/llvm-readobj/coff-load-config.test @@ -0,0 +1,87 @@ +RUN: llvm-readobj -coff-load-config %S/Inputs/coff-load-config-x86.dll | FileCheck %s --check-prefix=X86 +RUN: llvm-readobj -coff-load-config %S/Inputs/coff-load-config-x64.dll | FileCheck %s --check-prefix=X64 + +X86: LoadConfig [ +X86: Size: 0x5C +X86: TimeDateStamp: 1970-01-01 00:00:00 (0x0) +X86: MajorVersion: 0x0 +X86: MinorVersion: 0x0 +X86: GlobalFlagsClear: 0x0 +X86: GlobalFlagsSet: 0x0 +X86: CriticalSectionDefaultTimeout: 0x0 +X86: DeCommitFreeBlockThreshold: 0x0 +X86: DeCommitTotalFreeThreshold: 0x0 +X86: LockPrefixTable: 0x0 +X86: MaximumAllocationSize: 0x0 +X86: VirtualMemoryThreshold: 0x0 +X86: ProcessHeapFlags: 0x0 +X86: ProcessAffinityMask: 0x0 +X86: CSDVersion: 0x0 +X86: DependentLoadFlags: 0x0 +X86: EditList: 0x0 +X86: SecurityCookie: 0x10003004 +X86: SEHandlerTable: 0x100021C0 +X86: SEHandlerCount: 2 +X86: GuardCFCheckFunction: 0x1000207C +X86: GuardCFCheckDispatch: 0x0 +X86: GuardCFFunctionTable: 0x100020A4 +X86: GuardCFFunctionCount: 11 +X86: GuardFlags: 0x13500 +X86: ] +X86: SEHTable [ +X86: 0x10001BE0 +X86: 0x10001E30 +X86: ] +X86: GuardFidTable [ +X86: 0x10001000 +X86: 0x10001040 +X86: 0x10001060 +X86: 0x10001100 +X86: 0x10001120 +X86: 0x10001150 +X86: 0x10001460 +X86: 0x100019B0 +X86: 0x10001B40 +X86: 0x10001BE0 +X86: 0x10001DC0 +X86: ] + +X64: LoadConfig [ +X64: Size: 0x94 +X64: TimeDateStamp: 1970-01-01 00:00:00 (0x0) +X64: MajorVersion: 0x0 +X64: MinorVersion: 0x0 +X64: GlobalFlagsClear: 0x0 +X64: GlobalFlagsSet: 0x0 +X64: CriticalSectionDefaultTimeout: 0x0 +X64: DeCommitFreeBlockThreshold: 0x0 +X64: DeCommitTotalFreeThreshold: 0x0 +X64: LockPrefixTable: 0x0 +X64: MaximumAllocationSize: 0x0 +X64: VirtualMemoryThreshold: 0x0 +X64: ProcessHeapFlags: 0x0 +X64: ProcessAffinityMask: 0x0 +X64: CSDVersion: 0x0 +X64: DependentLoadFlags: 0x0 +X64: EditList: 0x0 +X64: SecurityCookie: 0x180003018 +X64: SEHandlerTable: 0 +X64: SEHandlerCount: 0 +X64: GuardCFCheckFunction: 0x180002100 +X64: GuardCFCheckDispatch: 0x180002108 +X64: GuardCFFunctionTable: 0x180002158 +X64: GuardCFFunctionCount: 9 +X64: GuardFlags: 0x13500 +X64: ] +X64-NOT: SEHTable +X64: GuardFidTable [ +X64: 0x180001000 +X64: 0x180001050 +X64: 0x180001070 +X64: 0x1800010E0 +X64: 0x180001110 +X64: 0x180001460 +X64: 0x180001970 +X64: 0x180001B50 +X64: 0x180001D90 +X64: ] diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index bb30180325e..373c528feea 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -68,6 +68,14 @@ using namespace llvm::Win64EH; namespace { +struct LoadConfigTables { + uint64_t SEHTableVA = 0; + uint64_t SEHTableCount = 0; + uint32_t GuardFlags = 0; + uint64_t GuardFidTableVA = 0; + uint64_t GuardFidTableCount = 0; +}; + class COFFDumper : public ObjDumper { public: friend class COFFObjectDumpDelegate; @@ -86,6 +94,7 @@ public: void printCOFFBaseReloc() override; void printCOFFDebugDirectory() override; void printCOFFResources() override; + void printCOFFLoadConfig() override; void printCodeViewDebugInfo() override; void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVIDs, llvm::codeview::TypeTableBuilder &CVTypes) override; @@ -100,6 +109,11 @@ private: template void printPEHeader(const PEHeader *Hdr); void printBaseOfDataField(const pe32_header *Hdr); void printBaseOfDataField(const pe32plus_header *Hdr); + template + void printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables); + typedef void (*PrintExtraCB)(raw_ostream &, const uint8_t *); + void printRVATable(uint64_t TableVA, uint64_t Count, uint64_t EntrySize, + PrintExtraCB PrintExtra = 0); void printCodeViewSymbolSection(StringRef SectionName, const SectionRef &Section); void printCodeViewTypeSection(StringRef SectionName, const SectionRef &Section); @@ -745,6 +759,125 @@ void COFFDumper::printCOFFDebugDirectory() { } } +void COFFDumper::printRVATable(uint64_t TableVA, uint64_t Count, + uint64_t EntrySize, PrintExtraCB PrintExtra) { + uintptr_t TableStart, TableEnd; + error(Obj->getVaPtr(TableVA, TableStart)); + error(Obj->getVaPtr(TableVA + Count * EntrySize, TableEnd)); + for (uintptr_t I = TableStart; I < TableEnd; I += EntrySize) { + uint32_t RVA = *reinterpret_cast(I); + raw_ostream &OS = W.startLine(); + OS << "0x" << utohexstr(Obj->getImageBase() + RVA); + if (PrintExtra) + PrintExtra(OS, reinterpret_cast(I)); + OS << '\n'; + } +} + +void COFFDumper::printCOFFLoadConfig() { + LoadConfigTables Tables; + if (Obj->is64()) + printCOFFLoadConfig(Obj->getLoadConfig64(), Tables); + else + printCOFFLoadConfig(Obj->getLoadConfig32(), Tables); + + if (Tables.SEHTableVA) { + ListScope LS(W, "SEHTable"); + printRVATable(Tables.SEHTableVA, Tables.SEHTableCount, 4); + } + + if (Tables.GuardFidTableVA) { + ListScope LS(W, "GuardFidTable"); + if (Tables.GuardFlags & uint32_t(coff_guard_flags::FidTableHasFlags)) { + auto PrintGuardFlags = [](raw_ostream &OS, const uint8_t *Entry) { + uint8_t Flags = *reinterpret_cast(Entry + 4); + if (Flags) + OS << " flags " << utohexstr(Flags); + }; + printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 5, + PrintGuardFlags); + } else { + printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 4); + } + } +} + +template +void COFFDumper::printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables) { + ListScope LS(W, "LoadConfig"); + char FormattedTime[20] = {}; + time_t TDS = Conf->TimeDateStamp; + strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS)); + W.printHex("Size", Conf->Size); + + // Print everything before SecurityCookie. The vast majority of images today + // have all these fields. + if (Conf->Size < offsetof(T, SEHandlerTable)) + return; + W.printHex("TimeDateStamp", FormattedTime, TDS); + W.printHex("MajorVersion", Conf->MajorVersion); + W.printHex("MinorVersion", Conf->MinorVersion); + W.printHex("GlobalFlagsClear", Conf->GlobalFlagsClear); + W.printHex("GlobalFlagsSet", Conf->GlobalFlagsSet); + W.printHex("CriticalSectionDefaultTimeout", + Conf->CriticalSectionDefaultTimeout); + W.printHex("DeCommitFreeBlockThreshold", Conf->DeCommitFreeBlockThreshold); + W.printHex("DeCommitTotalFreeThreshold", Conf->DeCommitTotalFreeThreshold); + W.printHex("LockPrefixTable", Conf->LockPrefixTable); + W.printHex("MaximumAllocationSize", Conf->MaximumAllocationSize); + W.printHex("VirtualMemoryThreshold", Conf->VirtualMemoryThreshold); + W.printHex("ProcessHeapFlags", Conf->ProcessHeapFlags); + W.printHex("ProcessAffinityMask", Conf->ProcessAffinityMask); + W.printHex("CSDVersion", Conf->CSDVersion); + W.printHex("DependentLoadFlags", Conf->DependentLoadFlags); + W.printHex("EditList", Conf->EditList); + W.printHex("SecurityCookie", Conf->SecurityCookie); + + // Print the safe SEH table if present. + if (Conf->Size < offsetof(coff_load_configuration32, GuardCFCheckFunction)) + return; + W.printHex("SEHandlerTable", Conf->SEHandlerTable); + W.printNumber("SEHandlerCount", Conf->SEHandlerCount); + + Tables.SEHTableVA = Conf->SEHandlerTable; + Tables.SEHTableCount = Conf->SEHandlerCount; + + // Print everything before CodeIntegrity. (2015) + if (Conf->Size < offsetof(T, CodeIntegrity)) + return; + W.printHex("GuardCFCheckFunction", Conf->GuardCFCheckFunction); + W.printHex("GuardCFCheckDispatch", Conf->GuardCFCheckDispatch); + W.printHex("GuardCFFunctionTable", Conf->GuardCFFunctionTable); + W.printNumber("GuardCFFunctionCount", Conf->GuardCFFunctionCount); + W.printHex("GuardFlags", Conf->GuardFlags); + + Tables.GuardFidTableVA = Conf->GuardCFFunctionTable; + Tables.GuardFidTableCount = Conf->GuardCFFunctionCount; + Tables.GuardFlags = Conf->GuardFlags; + + // Print the rest. (2017) + if (Conf->Size < sizeof(T)) + return; + W.printHex("GuardAddressTakenIatEntryTable", + Conf->GuardAddressTakenIatEntryTable); + W.printNumber("GuardAddressTakenIatEntryCount", + Conf->GuardAddressTakenIatEntryCount); + W.printHex("GuardLongJumpTargetTable", Conf->GuardLongJumpTargetTable); + W.printNumber("GuardLongJumpTargetCount", Conf->GuardLongJumpTargetCount); + W.printHex("DynamicValueRelocTable", Conf->DynamicValueRelocTable); + W.printHex("CHPEMetadataPointer", Conf->CHPEMetadataPointer); + W.printHex("GuardRFFailureRoutine", Conf->GuardRFFailureRoutine); + W.printHex("GuardRFFailureRoutineFunctionPointer", + Conf->GuardRFFailureRoutineFunctionPointer); + W.printHex("DynamicValueRelocTableOffset", + Conf->DynamicValueRelocTableOffset); + W.printNumber("DynamicValueRelocTableSection", + Conf->DynamicValueRelocTableSection); + W.printHex("GuardRFVerifyStackPointerFunctionPointer", + Conf->GuardRFVerifyStackPointerFunctionPointer); + W.printHex("HotPatchTableOffset", Conf->HotPatchTableOffset); +} + void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) { W.printHex("BaseOfData", Hdr->BaseOfData); } diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index 48f825c527c..43883c2d217 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -68,6 +68,7 @@ public: virtual void printCOFFBaseReloc() { } virtual void printCOFFDebugDirectory() { } virtual void printCOFFResources() {} + virtual void printCOFFLoadConfig() { } virtual void printCodeViewDebugInfo() { } virtual void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVIDs, llvm::codeview::TypeTableBuilder &CVTypes) {} diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index cd7244a8f97..51991a3f067 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -218,6 +218,11 @@ namespace opts { cl::opt COFFResources("coff-resources", cl::desc("Display the PE/COFF .rsrc section")); + // -coff-load-config + cl::opt + COFFLoadConfig("coff-load-config", + cl::desc("Display the PE/COFF load config")); + // -macho-data-in-code cl::opt MachODataInCode("macho-data-in-code", @@ -444,6 +449,8 @@ static void dumpObject(const ObjectFile *Obj) { Dumper->printCOFFDebugDirectory(); if (opts::COFFResources) Dumper->printCOFFResources(); + if (opts::COFFLoadConfig) + Dumper->printCOFFLoadConfig(); if (opts::CodeView) Dumper->printCodeViewDebugInfo(); if (opts::CodeViewMergedTypes)