Index: llvm/trunk/include/llvm/Object/COFF.h =================================================================== --- llvm/trunk/include/llvm/Object/COFF.h +++ llvm/trunk/include/llvm/Object/COFF.h @@ -20,6 +20,7 @@ #include "llvm/Object/Binary.h" #include "llvm/Object/Error.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Support/BinaryByteStream.h" #include "llvm/Support/COFF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" @@ -40,6 +41,7 @@ class ExportDirectoryEntryRef; class ImportDirectoryEntryRef; class ImportedSymbolRef; +class ResourceSectionRef; using import_directory_iterator = content_iterator; using delay_import_directory_iterator = @@ -623,6 +625,26 @@ int getOffset() const { return Data & ((1 << 12) - 1); } }; +struct coff_resource_dir_entry { + union { + support::ulittle32_t NameOffset; + support::ulittle32_t ID; + uint32_t getNameOffset() const { + return maskTrailingOnes(31) & NameOffset; + } + } Identifier; + union { + support::ulittle32_t DataEntryOffset; + support::ulittle32_t SubdirOffset; + + bool isSubDir() const { return SubdirOffset >> 31; } + uint32_t value() const { + return maskTrailingOnes(31) & SubdirOffset; + } + + } Offset; +}; + struct coff_resource_dir_table { support::ulittle32_t Characteristics; support::ulittle32_t TimeDateStamp; @@ -1047,6 +1069,23 @@ const COFFObjectFile *OwningObject = nullptr; }; +class ResourceSectionRef { +public: + ResourceSectionRef() = default; + explicit ResourceSectionRef(StringRef Ref) : BBS(Ref, support::little) {} + + ErrorOr getEntryNameString(const coff_resource_dir_entry &Entry); + ErrorOr + getEntrySubDir(const coff_resource_dir_entry &Entry); + ErrorOr getBaseTable(); + +private: + BinaryByteStream BBS; + + ErrorOr getTableAtOffset(uint32_t Offset); + ErrorOr getDirStringAtOffset(uint32_t Offset); +}; + // 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/trunk/include/llvm/Support/COFF.h =================================================================== --- llvm/trunk/include/llvm/Support/COFF.h +++ llvm/trunk/include/llvm/Support/COFF.h @@ -152,6 +152,30 @@ IMAGE_FILE_BYTES_REVERSED_HI = 0x8000 }; + enum ResourceTypeID { + RID_Cursor = 1, + RID_Bitmap = 2, + RID_Icon = 3, + RID_Menu = 4, + RID_Dialog = 5, + RID_String = 6, + RID_FontDir = 7, + RID_Font = 8, + RID_Accelerator = 9, + RID_RCData = 10, + RID_MessageTable = 11, + RID_Group_Cursor = 12, + RID_Group_Icon = 14, + RID_Version = 16, + RID_DLGInclude = 17, + RID_PlugPlay = 19, + RID_VXD = 20, + RID_AniCursor = 21, + RID_AniIcon = 22, + RID_HTML = 23, + RID_Manifest = 24, + }; + struct symbol { char Name[NameSize]; uint32_t Value; Index: llvm/trunk/lib/Object/COFFObjectFile.cpp =================================================================== --- llvm/trunk/lib/Object/COFFObjectFile.cpp +++ llvm/trunk/lib/Object/COFFObjectFile.cpp @@ -19,7 +19,9 @@ #include "llvm/Object/COFF.h" #include "llvm/Object/Error.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Support/BinaryStreamReader.h" #include "llvm/Support/COFF.h" +#include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Endian.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" @@ -159,8 +161,7 @@ Expected COFFObjectFile::getSymbolName(DataRefImpl Ref) const { COFFSymbolRef Symb = getCOFFSymbol(Ref); StringRef Result; - std::error_code EC = getSymbolName(Symb, Result); - if (EC) + if (std::error_code EC = getSymbolName(Symb, Result)) return errorCodeToError(EC); return Result; } @@ -1591,3 +1592,47 @@ Result = Header->PageRVA + Entry[Index].getOffset(); return std::error_code(); } + +#define RETURN_IF_ERROR(X) \ + if (auto EC = errorToErrorCode(X)) \ + return EC; + +ErrorOr ResourceSectionRef::getDirStringAtOffset(uint32_t Offset) { + BinaryStreamReader Reader = BinaryStreamReader(BBS); + Reader.setOffset(Offset); + uint16_t Length; + RETURN_IF_ERROR(Reader.readInteger(Length)); + ArrayRef RawDirString; + // Strings are stored as 2-byte aligned unicode characters but readFixedString + // assumes byte string, so we double length. + RETURN_IF_ERROR(Reader.readArray(RawDirString, Length)); + std::string DirString; + if (!llvm::convertUTF16ToUTF8String(RawDirString, DirString)) + return object_error::parse_failed; + return DirString; +} + +ErrorOr +ResourceSectionRef::getEntryNameString(const coff_resource_dir_entry &Entry) { + return getDirStringAtOffset(Entry.Identifier.getNameOffset()); +} + +ErrorOr +ResourceSectionRef::getTableAtOffset(uint32_t Offset) { + const coff_resource_dir_table *Table = nullptr; + + BinaryStreamReader Reader(BBS); + Reader.setOffset(Offset); + RETURN_IF_ERROR(Reader.readObject(Table)); + assert(Table != nullptr); + return *Table; +} + +ErrorOr +ResourceSectionRef::getEntrySubDir(const coff_resource_dir_entry &Entry) { + return getTableAtOffset(Entry.Offset.value()); +} + +ErrorOr ResourceSectionRef::getBaseTable() { + return getTableAtOffset(0); +} Index: llvm/trunk/test/tools/llvm-readobj/Inputs/resources/test_resource.rc =================================================================== --- llvm/trunk/test/tools/llvm-readobj/Inputs/resources/test_resource.rc +++ llvm/trunk/test/tools/llvm-readobj/Inputs/resources/test_resource.rc @@ -0,0 +1,44 @@ +#include "windows.h" + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +myaccelerators ACCELERATORS +{ + "^C", 999, VIRTKEY, ALT + "D", 1100, VIRTKEY, CONTROL, SHIFT + "^R", 444, ASCII, NOINVERT +} + +cursor BITMAP "cursor_small.bmp" +okay BITMAP "okay_small.bmp" + +14432 MENU +LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED +{ + MENUITEM "yu", 100 + MENUITEM "shala", 101 + MENUITEM "kaoya", 102 +} + +testdialog DIALOG 10, 10, 200, 300 +STYLE WS_POPUP | WS_BORDER +CAPTION "Test" +{ + CTEXT "Continue:", 1, 10, 10, 230, 14 + PUSHBUTTON "&OK", 2, 66, 134, 161, 13 +} + +12 ACCELERATORS +{ + "X", 164, VIRTKEY, ALT + "H", 5678, VIRTKEY, CONTROL, SHIFT + "^R", 444, ASCII, NOINVERT +} + +"eat" MENU +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +{ + MENUITEM "fish", 100 + MENUITEM "salad", 101 + MENUITEM "duck", 102 +} Index: llvm/trunk/test/tools/llvm-readobj/resources.test =================================================================== --- llvm/trunk/test/tools/llvm-readobj/resources.test +++ llvm/trunk/test/tools/llvm-readobj/resources.test @@ -1,19 +1,111 @@ -RUN: llvm-readobj -coff-resources %p/Inputs/zero-string-table.obj.coff-i386 \ -RUN: | FileCheck %s -check-prefix RESOURCE +// Check dumping of the .rsrc section(s) +// The input was generated with the following commands, using the original Windows +// rc.exe and cvtres.exe: +// > rc /fo test_resource.res /nologo test_resource.rc +// > cvtres /machine:X86 /readonly /nologo /out:test_resource.o test_resource.res -RESOURCE: Resources [ -RESOURCE-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) -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: ] +RUN: llvm-readobj -coff-resources -section-data %p/Inputs/zero-string-table.obj.coff-i386 \ +RUN: | FileCheck %s -check-prefix ZERO +RUN: llvm-readobj -coff-resources %p/Inputs/resources/test_resource.o \ +RUN: | FileCheck %s -check-prefix TEST_RES + +ZERO: Resources [ +ZERO-NEXT: String Name Entries: 0 +ZERO-NEXT: ID Entries: 1 +ZERO-NEXT: Type: kRT_STRING (ID 6) [ +ZERO-NEXT: String Name Entries: 0 +ZERO-NEXT: ID Entries: 1 +ZERO-NEXT: Name: (ID 1) [ +ZERO-NEXT: String Name Entries: 0 +ZERO-NEXT: ID Entries: 1 +ZERO-NEXT: Language: (ID 1033) [ +ZERO-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +ZERO-NEXT: Major Version: 0 +ZERO-NEXT: Minor Version: 0 +ZERO-NEXT: ] +ZERO-NEXT: ] +ZERO-NEXT: ] + + +TEST_RES: Resources [ +TEST_RES-NEXT: String Name Entries: 0 +TEST_RES-NEXT: ID Entries: 4 +TEST_RES-NEXT: Type: kRT_BITMAP (ID 2) [ +TEST_RES-NEXT: String Name Entries: 2 +TEST_RES-NEXT: ID Entries: 0 +TEST_RES-NEXT: Name: CURSOR [ +TEST_RES-NEXT: String Name Entries: 0 +TEST_RES-NEXT: ID Entries: 1 +TEST_RES-NEXT: Language: (ID 1033) [ +TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +TEST_RES-NEXT: Major Version: 0 +TEST_RES-NEXT: Minor Version: 0 +TEST_RES-NEXT: ] +TEST_RES-NEXT: ] +TEST_RES-NEXT: Name: OKAY [ +TEST_RES-NEXT: String Name Entries: 0 +TEST_RES-NEXT: ID Entries: 1 +TEST_RES-NEXT: Language: (ID 1033) [ +TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +TEST_RES-NEXT: Major Version: 0 +TEST_RES-NEXT: Minor Version: 0 +TEST_RES-NEXT: ] +TEST_RES-NEXT: ] +TEST_RES-NEXT: ] +TEST_RES-NEXT: Type: kRT_MENU (ID 4) [ +TEST_RES-NEXT: String Name Entries: 1 +TEST_RES-NEXT: ID Entries: 1 +TEST_RES-NEXT: Name: "EAT" [ +TEST_RES-NEXT: String Name Entries: 0 +TEST_RES-NEXT: ID Entries: 1 +TEST_RES-NEXT: Language: (ID 3081) [ +TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +TEST_RES-NEXT: Major Version: 0 +TEST_RES-NEXT: Minor Version: 0 +TEST_RES-NEXT: ] +TEST_RES-NEXT: ] +TEST_RES-NEXT: Name: (ID 14432) [ +TEST_RES-NEXT: String Name Entries: 0 +TEST_RES-NEXT: ID Entries: 1 +TEST_RES-NEXT: Language: (ID 2052) [ +TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +TEST_RES-NEXT: Major Version: 0 +TEST_RES-NEXT: Minor Version: 0 +TEST_RES-NEXT: ] +TEST_RES-NEXT: ] +TEST_RES-NEXT: ] +TEST_RES-NEXT: Type: kRT_DIALOG (ID 5) [ +TEST_RES-NEXT: String Name Entries: 1 +TEST_RES-NEXT: ID Entries: 0 +TEST_RES-NEXT: Name: TESTDIALOG [ +TEST_RES-NEXT: String Name Entries: 0 +TEST_RES-NEXT: ID Entries: 1 +TEST_RES-NEXT: Language: (ID 1033) [ +TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +TEST_RES-NEXT: Major Version: 0 +TEST_RES-NEXT: Minor Version: 0 +TEST_RES-NEXT: ] +TEST_RES-NEXT: ] +TEST_RES-NEXT: ] +TEST_RES-NEXT: Type: kRT_ACCELERATOR (ID 9) [ +TEST_RES-NEXT: String Name Entries: 1 +TEST_RES-NEXT: ID Entries: 1 +TEST_RES-NEXT: Name: MYACCELERATORS [ +TEST_RES-NEXT: String Name Entries: 0 +TEST_RES-NEXT: ID Entries: 1 +TEST_RES-NEXT: Language: (ID 1033) [ +TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +TEST_RES-NEXT: Major Version: 0 +TEST_RES-NEXT: Minor Version: 0 +TEST_RES-NEXT: ] +TEST_RES-NEXT: ] +TEST_RES-NEXT: Name: (ID 12) [ +TEST_RES-NEXT: String Name Entries: 0 +TEST_RES-NEXT: ID Entries: 1 +TEST_RES-NEXT: Language: (ID 1033) [ +TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0) +TEST_RES-NEXT: Major Version: 0 +TEST_RES-NEXT: Minor Version: 0 +TEST_RES-NEXT: ] +TEST_RES-NEXT: ] +TEST_RES-NEXT: ] Index: llvm/trunk/tools/llvm-readobj/COFFDumper.cpp =================================================================== --- llvm/trunk/tools/llvm-readobj/COFFDumper.cpp +++ llvm/trunk/tools/llvm-readobj/COFFDumper.cpp @@ -121,6 +121,10 @@ uint32_t RelocOffset, uint32_t Offset, StringRef *RelocSym = nullptr); + void printResourceDirectoryTable(ResourceSectionRef RSF, + const coff_resource_dir_table &Table, + StringRef Level); + void printBinaryBlockWithRelocs(StringRef Label, const SectionRef &Sec, StringRef SectionContents, StringRef Block); @@ -140,6 +144,9 @@ void printDelayImportedSymbols( const DelayImportDirectoryEntryRef &I, iterator_range Range); + ErrorOr + getResourceDirectoryTableEntry(const coff_resource_dir_table &Table, + uint32_t Index); typedef DenseMap > RelocMapTy; @@ -534,6 +541,29 @@ LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, SHA256), }; +static const EnumEntry ResourceTypeNames[]{ + {"kRT_CURSOR (ID 1)", COFF::RID_Cursor}, + {"kRT_BITMAP (ID 2)", COFF::RID_Bitmap}, + {"kRT_ICON (ID 3)", COFF::RID_Icon}, + {"kRT_MENU (ID 4)", COFF::RID_Menu}, + {"kRT_DIALOG (ID 5)", COFF::RID_Dialog}, + {"kRT_STRING (ID 6)", COFF::RID_String}, + {"kRT_FONTDIR (ID 7)", COFF::RID_FontDir}, + {"kRT_FONT (ID 8)", COFF::RID_Font}, + {"kRT_ACCELERATOR (ID 9)", COFF::RID_Accelerator}, + {"kRT_RCDATA (ID 10)", COFF::RID_RCData}, + {"kRT_MESSAGETABLE (ID 11)", COFF::RID_MessageTable}, + {"kRT_GROUP_CURSOR (ID 12)", COFF::RID_Group_Cursor}, + {"kRT_GROUP_ICON (ID 14)", COFF::RID_Group_Icon}, + {"kRT_VERSION (ID 16)", COFF::RID_Version}, + {"kRT_DLGINCLUDE (ID 17)", COFF::RID_DLGInclude}, + {"kRT_PLUGPLAY (ID 19)", COFF::RID_PlugPlay}, + {"kRT_VXD (ID 20)", COFF::RID_VXD}, + {"kRT_ANICURSOR (ID 21)", COFF::RID_AniCursor}, + {"kRT_ANIICON (ID 22)", COFF::RID_AniIcon}, + {"kRT_HTML (ID 23)", COFF::RID_HTML}, + {"kRT_MANIFEST (ID 24)", COFF::RID_Manifest}}; + template static std::error_code getSymbolAuxData(const COFFObjectFile *Obj, COFFSymbolRef Symbol, @@ -1503,18 +1533,73 @@ error(S.getContents(Ref)); if ((Name == ".rsrc") || (Name == ".rsrc$01")) { - auto Table = - reinterpret_cast(Ref.data()); - char FormattedTime[20]; - time_t TDS = time_t(Table->TimeDateStamp); - strftime(FormattedTime, sizeof(FormattedTime), "%Y-%m-%d %H:%M:%S", - gmtime(&TDS)); - W.printHex("Time/Date Stamp", FormattedTime, Table->TimeDateStamp); + ResourceSectionRef RSF(Ref); + auto &BaseTable = unwrapOrError(RSF.getBaseTable()); + printResourceDirectoryTable(RSF, BaseTable, "Type"); + } + if (opts::SectionData) + W.printBinaryBlock(Name.str() + " Data", Ref); + } +} + +void COFFDumper::printResourceDirectoryTable( + ResourceSectionRef RSF, const coff_resource_dir_table &Table, + StringRef Level) { + W.printNumber("String Name Entries", Table.NumberOfNameEntries); + W.printNumber("ID Entries", Table.NumberOfIDEntries); + + char FormattedTime[20] = {}; + time_t TDS = time_t(Table.TimeDateStamp); + strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS)); + + // Iterate through level in resource directory tree. + for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries; + i++) { + auto Entry = unwrapOrError(getResourceDirectoryTableEntry(Table, i)); + StringRef Name; + SmallString<20> IDStr; + raw_svector_ostream OS(IDStr); + if (i < Table.NumberOfNameEntries) { + StringRef EntryNameString = unwrapOrError(RSF.getEntryNameString(Entry)); + OS << ": "; + OS << EntryNameString.str(); + } else { + if (Level == "Type") { + ScopedPrinter Printer(OS); + Printer.printEnum("", Entry.Identifier.ID, + makeArrayRef(ResourceTypeNames)); + IDStr = IDStr.slice(0, IDStr.find_first_of(")", 0) + 1); + } else { + OS << ": (ID " << Entry.Identifier.ID << ")"; + } + } + Name = StringRef(IDStr); + ListScope ResourceType(W, Level.str() + Name.str()); + if (Entry.Offset.isSubDir()) { + StringRef NextLevel; + if (Level == "Name") + NextLevel = "Language"; + else + NextLevel = "Name"; + auto &NextTable = unwrapOrError(RSF.getEntrySubDir(Entry)); + printResourceDirectoryTable(RSF, NextTable, NextLevel); + } else { + W.printHex("Time/Date Stamp", FormattedTime, Table.TimeDateStamp); + W.printNumber("Major Version", Table.MajorVersion); + W.printNumber("Minor Version", Table.MinorVersion); } - W.printBinaryBlock(Name.str() + " Data", Ref); } } +ErrorOr +COFFDumper::getResourceDirectoryTableEntry(const coff_resource_dir_table &Table, + uint32_t Index) { + if (Index >= Table.NumberOfNameEntries + Table.NumberOfIDEntries) + return object_error::parse_failed; + auto TablePtr = reinterpret_cast(&Table + 1); + return TablePtr[Index]; +} + void COFFDumper::printStackMap() const { object::SectionRef StackMapSection; for (auto Sec : Obj->sections()) {