Index: llvm/include/llvm/Object/COFF.h =================================================================== --- llvm/include/llvm/Object/COFF.h +++ llvm/include/llvm/Object/COFF.h @@ -40,6 +40,7 @@ class ExportDirectoryEntryRef; class ImportDirectoryEntryRef; class ImportedSymbolRef; +class ResourceSectionRef; using import_directory_iterator = content_iterator; using delay_import_directory_iterator = @@ -623,6 +624,26 @@ int getOffset() const { return Data & ((1 << 12) - 1); } }; +struct coff_resource_dir_table { + support::ulittle32_t Characteristics; + support::ulittle32_t TimeDateStamp; + support::ulittle16_t MajorVersion; + support::ulittle16_t MinorVersion; + support::ulittle16_t NumberOfNameEntries; + support::ulittle16_t NumberOfIDEntries; +}; + +struct coff_resource_dir_entry { + union { + support::ulittle32_t NameOffset; + support::ulittle32_t ID; + } Identifier; + union { + support::ulittle32_t DataEntryOffset; + support::ulittle32_t SubdirOffset; + } Offset; +}; + class COFFObjectFile : public ObjectFile { private: friend class ImportDirectoryEntryRef; @@ -1038,6 +1059,70 @@ const COFFObjectFile *OwningObject = nullptr; }; +class ResourceSectionRef { +public: + ResourceSectionRef() = default; + ResourceSectionRef(const coff_resource_dir_table *BaseTable) + : BaseTable(BaseTable) {} + + std::error_code getTableAtOffset(uint32_t Offset, + const coff_resource_dir_table *&Table); + std::error_code getDirStringAtOffset(uint32_t Offset, + StringRef &DirString) const; + + StringRef convertIDToType(uint32_t ID) const { + switch (ID) { + case 1: + return StringRef("kRT_CURSOR (ID 1)"); + case 2: + return StringRef("kRT_BITMAP (ID 2)"); + case 3: + return StringRef("kRT_ICON (ID 3)"); + case 4: + return StringRef("kRT_MENU (ID 4)"); + case 5: + return StringRef("kRT_DIALOG (ID 5)"); + case 6: + return StringRef("kRT_STRING (ID 6)"); + case 7: + return StringRef("kRT_FONTDIR (ID 7)"); + case 8: + return StringRef("kRT_FONT (ID 8)"); + case 9: + return StringRef("kRT_ACCELERATOR (ID 9)"); + case 10: + return StringRef("kRT_RCDATA (ID 10)"); + case 11: + return StringRef("kRT_MESSAGETABLE (ID 11)"); + case 12: + return StringRef("kRT_GROUP_CURSOR (ID 12)"); + case 14: + return StringRef("kRT_GROUP_ICON (ID 14)"); + case 16: + return StringRef("kRT_VERSION (ID 16)"); + case 17: + return StringRef("kRT_DLGINCLUDE (ID 17)"); + case 19: + return StringRef("kRT_PLUGPLAY (ID 19)"); + case 20: + return StringRef("kRT_VXD (ID 20)"); + case 21: + return StringRef("kRT_ANICURSOR (ID 21)"); + case 22: + return StringRef("kRT_ANIICON (ID 22)"); + case 23: + return StringRef("kRT_HTML (ID 23)"); + case 24: + return StringRef("kRT_MANIFEST (ID 24)"); + default: + return StringRef("UNKNOWN ERROR CODE " + ID); + } + } + +private: + const coff_resource_dir_table *BaseTable; +}; + // Corresponds to `_FPO_DATA` structure in the PE/COFF spec. struct FpoData { support::ulittle32_t Offset; // ulOffStart: Offset 1st byte of function code Index: llvm/lib/Object/COFFObjectFile.cpp =================================================================== --- llvm/lib/Object/COFFObjectFile.cpp +++ llvm/lib/Object/COFFObjectFile.cpp @@ -1591,3 +1591,21 @@ Result = Header->PageRVA + Entry[Index].getOffset(); return std::error_code(); } + +std::error_code +ResourceSectionRef::getTableAtOffset(uint32_t Offset, + const coff_resource_dir_table *&Table) { + Table = reinterpret_cast( + reinterpret_cast(BaseTable) + Offset); + return std::error_code(); +} + +std::error_code +ResourceSectionRef::getDirStringAtOffset(uint32_t Offset, + StringRef &DirString) const { + auto Length = reinterpret_cast( + reinterpret_cast(BaseTable) + Offset); + auto *Data = reinterpret_cast(Length + 1); + DirString = StringRef(Data, (size_t)*Length); + return std::error_code(); +} Index: llvm/test/tools/llvm-readobj/resources.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-readobj/resources.test @@ -0,0 +1,41 @@ +RUN: llvm-readobj -coff-resources -section-data %p/Inputs/zero-string-table.obj.coff-i386 \ +RUN: | FileCheck %s -check-prefix RESOURCE +RUN: llvm-readobj -coff-resources %p/Inputs/chrome_dll.o \ +RUN: | FileCheck %s -check-prefix CHROME + +RESOURCE: Resources [ +RESOURCE-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +RESOURCE-NEXT: Major Version: 0 +RESOURCE-NEXT: Minor Version: 0 +RESOURCE-NEXT: Name Entries: 0 +RESOURCE-NEXT: ID Entries: 1 +RESOURCE-NEXT: Type: kRT_STRING (ID 6) [ +RESOURCE-NEXT: ] +RESOURCE-NEXT: .rsrc$01 Data ( +RESOURCE-NEXT: 0000: 00000000 00000000 00000000 00000100 |................| +RESOURCE-NEXT: 0010: 06000000 18000080 00000000 00000000 |................| +RESOURCE-NEXT: 0020: 00000000 00000100 01000000 30000080 |............0...| +RESOURCE-NEXT: 0030: 00000000 00000000 00000000 00000100 |................| +RESOURCE-NEXT: 0040: 09040000 48000000 00000000 2A000000 |....H.......*...| +RESOURCE-NEXT: 0050: 00000000 00000000 |........| +RESOURCE-NEXT: ) +RESOURCE-NEXT: .rsrc$02 Data ( +RESOURCE-NEXT: 0000: 00000500 48006500 6C006C00 6F000000 |....H.e.l.l.o...| +RESOURCE-NEXT: 0010: 00000000 00000000 00000000 00000000 |................| +RESOURCE-NEXT: 0020: 00000000 00000000 00000000 00000000 |................| +RESOURCE-NEXT: ) +RESOURCE-NEXT: ] + +CHROME: Resources [ +CHROME-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +CHROME-NEXT: Major Version: 0 +CHROME-NEXT: Minor Version: 0 +CHROME-NEXT: Name Entries: 0 +CHROME-NEXT: ID Entries: 3 +CHROME-NEXT: Type: kRT_ICON (ID 3) [ +CHROME-NEXT: ] +CHROME-NEXT: Type: kRT_ACCELERATOR (ID 9) [ +CHROME-NEXT: ] +CHROME-NEXT: Type: kRT_GROUP_ICON (ID 14) [ +CHROME-NEXT: ] +CHROME-NEXT: ] Index: llvm/tools/llvm-readobj/COFFDumper.cpp =================================================================== --- llvm/tools/llvm-readobj/COFFDumper.cpp +++ llvm/tools/llvm-readobj/COFFDumper.cpp @@ -78,6 +78,7 @@ void printCOFFDirectives() override; void printCOFFBaseReloc() override; void printCOFFDebugDirectory() override; + void printCOFFResources() override; void printCodeViewDebugInfo() override; void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVIDs, llvm::codeview::TypeTableBuilder &CVTypes) override; @@ -1527,6 +1528,52 @@ } } +void COFFDumper::printCOFFResources() { + ListScope ResourcesD(W, "Resources"); + for (const SectionRef &S : Obj->sections()) { + StringRef Name; + error(S.getName(Name)); + if (!Name.startswith(".rsrc")) + continue; + + StringRef Ref; + error(S.getContents(Ref)); + + if ((Name == ".rsrc") || (Name == ".rsrc$01")) { + ResourceSectionRef RSF( + reinterpret_cast(Ref.data())); + const coff_resource_dir_table *TypeTable; + error(RSF.getTableAtOffset(0, TypeTable)); + char FormattedTime[20] = {}; + time_t TDS = time_t(TypeTable->TimeDateStamp); + strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS)); + W.printHex("Time/Date Stamp", FormattedTime, TypeTable->TimeDateStamp); + W.printNumber("Major Version", TypeTable->MajorVersion); + W.printNumber("Minor Version", TypeTable->MinorVersion); + W.printNumber("Name Entries", TypeTable->NumberOfNameEntries); + W.printNumber("ID Entries", TypeTable->NumberOfIDEntries); + + // Iterate through type level in resource directory tree. + for (int i = 0; + i < TypeTable->NumberOfNameEntries + TypeTable->NumberOfIDEntries; + i++) { + auto Entry = + (reinterpret_cast(TypeTable + 1)) + + i; + StringRef TypeName; + if (i < TypeTable->NumberOfNameEntries) + error( + RSF.getDirStringAtOffset(Entry->Identifier.NameOffset, TypeName)); + else + TypeName = RSF.convertIDToType(Entry->Identifier.ID); + ListScope ResourceType(W, "Type: " + TypeName.str()); + } + } + if (opts::SectionData) + W.printBinaryBlock(Name.str() + " Data", Ref); + } +} + void COFFDumper::printStackMap() const { object::SectionRef StackMapSection; for (auto Sec : Obj->sections()) { Index: llvm/tools/llvm-readobj/ObjDumper.h =================================================================== --- llvm/tools/llvm-readobj/ObjDumper.h +++ llvm/tools/llvm-readobj/ObjDumper.h @@ -67,6 +67,7 @@ virtual void printCOFFDirectives() { } virtual void printCOFFBaseReloc() { } virtual void printCOFFDebugDirectory() { } + virtual void printCOFFResources() {} virtual void printCodeViewDebugInfo() { } virtual void mergeCodeViewTypes(llvm::codeview::TypeTableBuilder &CVIDs, llvm::codeview::TypeTableBuilder &CVTypes) {} Index: llvm/tools/llvm-readobj/llvm-readobj.cpp =================================================================== --- llvm/tools/llvm-readobj/llvm-readobj.cpp +++ llvm/tools/llvm-readobj/llvm-readobj.cpp @@ -214,6 +214,10 @@ COFFDebugDirectory("coff-debug-directory", cl::desc("Display the PE/COFF debug directory")); + // -coff-resources + cl::opt COFFResources("coff-resources", + cl::desc("Display the PE/COFF .rsrc section")); + // -macho-data-in-code cl::opt MachODataInCode("macho-data-in-code", @@ -445,6 +449,8 @@ Dumper->printCOFFBaseReloc(); if (opts::COFFDebugDirectory) Dumper->printCOFFDebugDirectory(); + if (opts::COFFResources) + Dumper->printCOFFResources(); if (opts::CodeView) Dumper->printCodeViewDebugInfo(); if (opts::CodeViewMergedTypes)