llvm.org GIT mirror llvm / 4899f66
Update llvm-readobj -coff-resources to display tree structure. Summary: Continue making updates to llvm-readobj to display resource sections. This is necessary for testing the up and coming cvtres tool. Reviewers: zturner Subscribers: llvm-commits Differential Revision: https://reviews.llvm.org/D32609 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@302386 91177308-0d34-0410-b5e6-96231b3b80d8 Eric Beckmann 3 years ago
9 changed file(s) with 359 addition(s) and 30 deletion(s). Raw diff Collapse all Expand all
1919 #include "llvm/Object/Binary.h"
2020 #include "llvm/Object/Error.h"
2121 #include "llvm/Object/ObjectFile.h"
22 #include "llvm/Support/BinaryByteStream.h"
2223 #include "llvm/Support/COFF.h"
2324 #include "llvm/Support/Endian.h"
2425 #include "llvm/Support/ErrorHandling.h"
3940 class ExportDirectoryEntryRef;
4041 class ImportDirectoryEntryRef;
4142 class ImportedSymbolRef;
43 class ResourceSectionRef;
4244
4345 using import_directory_iterator = content_iterator;
4446 using delay_import_directory_iterator =
622624 int getOffset() const { return Data & ((1 << 12) - 1); }
623625 };
624626
627 struct coff_resource_dir_entry {
628 union {
629 support::ulittle32_t NameOffset;
630 support::ulittle32_t ID;
631 uint32_t getNameOffset() const {
632 return maskTrailingOnes(31) & NameOffset;
633 }
634 } Identifier;
635 union {
636 support::ulittle32_t DataEntryOffset;
637 support::ulittle32_t SubdirOffset;
638
639 bool isSubDir() const { return SubdirOffset >> 31; }
640 uint32_t value() const {
641 return maskTrailingOnes(31) & SubdirOffset;
642 }
643
644 } Offset;
645 };
646
625647 struct coff_resource_dir_table {
626648 support::ulittle32_t Characteristics;
627649 support::ulittle32_t TimeDateStamp;
10461068 const COFFObjectFile *OwningObject = nullptr;
10471069 };
10481070
1071 class ResourceSectionRef {
1072 public:
1073 ResourceSectionRef() = default;
1074 explicit ResourceSectionRef(StringRef Ref) : BBS(Ref, support::little) {}
1075
1076 ErrorOr getEntryNameString(const coff_resource_dir_entry &Entry);
1077 ErrorOr
1078 getEntrySubDir(const coff_resource_dir_entry &Entry);
1079 ErrorOr getBaseTable();
1080
1081 private:
1082 BinaryByteStream BBS;
1083
1084 ErrorOr getTableAtOffset(uint32_t Offset);
1085 ErrorOr getDirStringAtOffset(uint32_t Offset);
1086 };
1087
10491088 // Corresponds to `_FPO_DATA` structure in the PE/COFF spec.
10501089 struct FpoData {
10511090 support::ulittle32_t Offset; // ulOffStart: Offset 1st byte of function code
149149 /// Big endian: the MSB precedes the LSB in memory. This is deprecated
150150 /// and should be 0.
151151 IMAGE_FILE_BYTES_REVERSED_HI = 0x8000
152 };
153
154 enum ResourceTypeID {
155 RID_Cursor = 1,
156 RID_Bitmap = 2,
157 RID_Icon = 3,
158 RID_Menu = 4,
159 RID_Dialog = 5,
160 RID_String = 6,
161 RID_FontDir = 7,
162 RID_Font = 8,
163 RID_Accelerator = 9,
164 RID_RCData = 10,
165 RID_MessageTable = 11,
166 RID_Group_Cursor = 12,
167 RID_Group_Icon = 14,
168 RID_Version = 16,
169 RID_DLGInclude = 17,
170 RID_PlugPlay = 19,
171 RID_VXD = 20,
172 RID_AniCursor = 21,
173 RID_AniIcon = 22,
174 RID_HTML = 23,
175 RID_Manifest = 24,
152176 };
153177
154178 struct symbol {
1818 #include "llvm/Object/COFF.h"
1919 #include "llvm/Object/Error.h"
2020 #include "llvm/Object/ObjectFile.h"
21 #include "llvm/Support/BinaryStreamReader.h"
2122 #include "llvm/Support/COFF.h"
23 #include "llvm/Support/ConvertUTF.h"
2224 #include "llvm/Support/Endian.h"
2325 #include "llvm/Support/Error.h"
2426 #include "llvm/Support/ErrorHandling.h"
158160 Expected COFFObjectFile::getSymbolName(DataRefImpl Ref) const {
159161 COFFSymbolRef Symb = getCOFFSymbol(Ref);
160162 StringRef Result;
161 std::error_code EC = getSymbolName(Symb, Result);
162 if (EC)
163 if (std::error_code EC = getSymbolName(Symb, Result))
163164 return errorCodeToError(EC);
164165 return Result;
165166 }
15901591 Result = Header->PageRVA + Entry[Index].getOffset();
15911592 return std::error_code();
15921593 }
1594
1595 #define RETURN_IF_ERROR(X) \
1596 if (auto EC = errorToErrorCode(X)) \
1597 return EC;
1598
1599 ErrorOr ResourceSectionRef::getDirStringAtOffset(uint32_t Offset) {
1600 BinaryStreamReader Reader = BinaryStreamReader(BBS);
1601 Reader.setOffset(Offset);
1602 uint16_t Length;
1603 RETURN_IF_ERROR(Reader.readInteger(Length));
1604 ArrayRef RawDirString;
1605 // Strings are stored as 2-byte aligned unicode characters but readFixedString
1606 // assumes byte string, so we double length.
1607 RETURN_IF_ERROR(Reader.readArray(RawDirString, Length));
1608 std::string DirString;
1609 if (!llvm::convertUTF16ToUTF8String(RawDirString, DirString))
1610 return object_error::parse_failed;
1611 return DirString;
1612 }
1613
1614 ErrorOr
1615 ResourceSectionRef::getEntryNameString(const coff_resource_dir_entry &Entry) {
1616 return getDirStringAtOffset(Entry.Identifier.getNameOffset());
1617 }
1618
1619 ErrorOr
1620 ResourceSectionRef::getTableAtOffset(uint32_t Offset) {
1621 const coff_resource_dir_table *Table = nullptr;
1622
1623 BinaryStreamReader Reader(BBS);
1624 Reader.setOffset(Offset);
1625 RETURN_IF_ERROR(Reader.readObject(Table));
1626 assert(Table != nullptr);
1627 return *Table;
1628 }
1629
1630 ErrorOr
1631 ResourceSectionRef::getEntrySubDir(const coff_resource_dir_entry &Entry) {
1632 return getTableAtOffset(Entry.Offset.value());
1633 }
1634
1635 ErrorOr ResourceSectionRef::getBaseTable() {
1636 return getTableAtOffset(0);
1637 }
0 #include "windows.h"
1
2 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
3
4 myaccelerators ACCELERATORS
5 {
6 "^C", 999, VIRTKEY, ALT
7 "D", 1100, VIRTKEY, CONTROL, SHIFT
8 "^R", 444, ASCII, NOINVERT
9 }
10
11 cursor BITMAP "cursor_small.bmp"
12 okay BITMAP "okay_small.bmp"
13
14 14432 MENU
15 LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
16 {
17 MENUITEM "yu", 100
18 MENUITEM "shala", 101
19 MENUITEM "kaoya", 102
20 }
21
22 testdialog DIALOG 10, 10, 200, 300
23 STYLE WS_POPUP | WS_BORDER
24 CAPTION "Test"
25 {
26 CTEXT "Continue:", 1, 10, 10, 230, 14
27 PUSHBUTTON "&OK", 2, 66, 134, 161, 13
28 }
29
30 12 ACCELERATORS
31 {
32 "X", 164, VIRTKEY, ALT
33 "H", 5678, VIRTKEY, CONTROL, SHIFT
34 "^R", 444, ASCII, NOINVERT
35 }
36
37 "eat" MENU
38 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS
39 {
40 MENUITEM "fish", 100
41 MENUITEM "salad", 101
42 MENUITEM "duck", 102
43 }
None RUN: llvm-readobj -coff-resources %p/Inputs/zero-string-table.obj.coff-i386 \
1 RUN: | FileCheck %s -check-prefix RESOURCE
0 // Check dumping of the .rsrc section(s)
1 // The input was generated with the following commands, using the original Windows
2 // rc.exe and cvtres.exe:
3 // > rc /fo test_resource.res /nologo test_resource.rc
4 // > cvtres /machine:X86 /readonly /nologo /out:test_resource.o test_resource.res
25
3 RESOURCE: Resources [
4 RESOURCE-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0)
5 RESOURCE-NEXT: .rsrc$01 Data (
6 RESOURCE-NEXT: 0000: 00000000 00000000 00000000 00000100 |................|
7 RESOURCE-NEXT: 0010: 06000000 18000080 00000000 00000000 |................|
8 RESOURCE-NEXT: 0020: 00000000 00000100 01000000 30000080 |............0...|
9 RESOURCE-NEXT: 0030: 00000000 00000000 00000000 00000100 |................|
10 RESOURCE-NEXT: 0040: 09040000 48000000 00000000 2A000000 |....H.......*...|
11 RESOURCE-NEXT: 0050: 00000000 00000000 |........|
12 RESOURCE-NEXT: )
13 RESOURCE-NEXT: .rsrc$02 Data (
14 RESOURCE-NEXT: 0000: 00000500 48006500 6C006C00 6F000000 |....H.e.l.l.o...|
15 RESOURCE-NEXT: 0010: 00000000 00000000 00000000 00000000 |................|
16 RESOURCE-NEXT: 0020: 00000000 00000000 00000000 00000000 |................|
17 RESOURCE-NEXT: )
18 RESOURCE-NEXT: ]
6 RUN: llvm-readobj -coff-resources -section-data %p/Inputs/zero-string-table.obj.coff-i386 \
7 RUN: | FileCheck %s -check-prefix ZERO
8 RUN: llvm-readobj -coff-resources %p/Inputs/resources/test_resource.o \
9 RUN: | FileCheck %s -check-prefix TEST_RES
10
11 ZERO: Resources [
12 ZERO-NEXT: String Name Entries: 0
13 ZERO-NEXT: ID Entries: 1
14 ZERO-NEXT: Type: kRT_STRING (ID 6) [
15 ZERO-NEXT: String Name Entries: 0
16 ZERO-NEXT: ID Entries: 1
17 ZERO-NEXT: Name: (ID 1) [
18 ZERO-NEXT: String Name Entries: 0
19 ZERO-NEXT: ID Entries: 1
20 ZERO-NEXT: Language: (ID 1033) [
21 ZERO-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0)
22 ZERO-NEXT: Major Version: 0
23 ZERO-NEXT: Minor Version: 0
24 ZERO-NEXT: ]
25 ZERO-NEXT: ]
26 ZERO-NEXT: ]
27
28
29 TEST_RES: Resources [
30 TEST_RES-NEXT: String Name Entries: 0
31 TEST_RES-NEXT: ID Entries: 4
32 TEST_RES-NEXT: Type: kRT_BITMAP (ID 2) [
33 TEST_RES-NEXT: String Name Entries: 2
34 TEST_RES-NEXT: ID Entries: 0
35 TEST_RES-NEXT: Name: CURSOR [
36 TEST_RES-NEXT: String Name Entries: 0
37 TEST_RES-NEXT: ID Entries: 1
38 TEST_RES-NEXT: Language: (ID 1033) [
39 TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0)
40 TEST_RES-NEXT: Major Version: 0
41 TEST_RES-NEXT: Minor Version: 0
42 TEST_RES-NEXT: ]
43 TEST_RES-NEXT: ]
44 TEST_RES-NEXT: Name: OKAY [
45 TEST_RES-NEXT: String Name Entries: 0
46 TEST_RES-NEXT: ID Entries: 1
47 TEST_RES-NEXT: Language: (ID 1033) [
48 TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0)
49 TEST_RES-NEXT: Major Version: 0
50 TEST_RES-NEXT: Minor Version: 0
51 TEST_RES-NEXT: ]
52 TEST_RES-NEXT: ]
53 TEST_RES-NEXT: ]
54 TEST_RES-NEXT: Type: kRT_MENU (ID 4) [
55 TEST_RES-NEXT: String Name Entries: 1
56 TEST_RES-NEXT: ID Entries: 1
57 TEST_RES-NEXT: Name: "EAT" [
58 TEST_RES-NEXT: String Name Entries: 0
59 TEST_RES-NEXT: ID Entries: 1
60 TEST_RES-NEXT: Language: (ID 3081) [
61 TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0)
62 TEST_RES-NEXT: Major Version: 0
63 TEST_RES-NEXT: Minor Version: 0
64 TEST_RES-NEXT: ]
65 TEST_RES-NEXT: ]
66 TEST_RES-NEXT: Name: (ID 14432) [
67 TEST_RES-NEXT: String Name Entries: 0
68 TEST_RES-NEXT: ID Entries: 1
69 TEST_RES-NEXT: Language: (ID 2052) [
70 TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0)
71 TEST_RES-NEXT: Major Version: 0
72 TEST_RES-NEXT: Minor Version: 0
73 TEST_RES-NEXT: ]
74 TEST_RES-NEXT: ]
75 TEST_RES-NEXT: ]
76 TEST_RES-NEXT: Type: kRT_DIALOG (ID 5) [
77 TEST_RES-NEXT: String Name Entries: 1
78 TEST_RES-NEXT: ID Entries: 0
79 TEST_RES-NEXT: Name: TESTDIALOG [
80 TEST_RES-NEXT: String Name Entries: 0
81 TEST_RES-NEXT: ID Entries: 1
82 TEST_RES-NEXT: Language: (ID 1033) [
83 TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0)
84 TEST_RES-NEXT: Major Version: 0
85 TEST_RES-NEXT: Minor Version: 0
86 TEST_RES-NEXT: ]
87 TEST_RES-NEXT: ]
88 TEST_RES-NEXT: ]
89 TEST_RES-NEXT: Type: kRT_ACCELERATOR (ID 9) [
90 TEST_RES-NEXT: String Name Entries: 1
91 TEST_RES-NEXT: ID Entries: 1
92 TEST_RES-NEXT: Name: MYACCELERATORS [
93 TEST_RES-NEXT: String Name Entries: 0
94 TEST_RES-NEXT: ID Entries: 1
95 TEST_RES-NEXT: Language: (ID 1033) [
96 TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0)
97 TEST_RES-NEXT: Major Version: 0
98 TEST_RES-NEXT: Minor Version: 0
99 TEST_RES-NEXT: ]
100 TEST_RES-NEXT: ]
101 TEST_RES-NEXT: Name: (ID 12) [
102 TEST_RES-NEXT: String Name Entries: 0
103 TEST_RES-NEXT: ID Entries: 1
104 TEST_RES-NEXT: Language: (ID 1033) [
105 TEST_RES-NEXT: Time/Date Stamp: 1970-01-01 00:00:00 (0x0)
106 TEST_RES-NEXT: Major Version: 0
107 TEST_RES-NEXT: Minor Version: 0
108 TEST_RES-NEXT: ]
109 TEST_RES-NEXT: ]
110 TEST_RES-NEXT: ]
120120 uint32_t RelocOffset, uint32_t Offset,
121121 StringRef *RelocSym = nullptr);
122122
123 void printResourceDirectoryTable(ResourceSectionRef RSF,
124 const coff_resource_dir_table &Table,
125 StringRef Level);
126
123127 void printBinaryBlockWithRelocs(StringRef Label, const SectionRef &Sec,
124128 StringRef SectionContents, StringRef Block);
125129
139143 void printDelayImportedSymbols(
140144 const DelayImportDirectoryEntryRef &I,
141145 iterator_range Range);
146 ErrorOr
147 getResourceDirectoryTableEntry(const coff_resource_dir_table &Table,
148 uint32_t Index);
142149
143150 typedef DenseMap > RelocMapTy;
144151
533540 LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, SHA256),
534541 };
535542
543 static const EnumEntry ResourceTypeNames[]{
544 {"kRT_CURSOR (ID 1)", COFF::RID_Cursor},
545 {"kRT_BITMAP (ID 2)", COFF::RID_Bitmap},
546 {"kRT_ICON (ID 3)", COFF::RID_Icon},
547 {"kRT_MENU (ID 4)", COFF::RID_Menu},
548 {"kRT_DIALOG (ID 5)", COFF::RID_Dialog},
549 {"kRT_STRING (ID 6)", COFF::RID_String},
550 {"kRT_FONTDIR (ID 7)", COFF::RID_FontDir},
551 {"kRT_FONT (ID 8)", COFF::RID_Font},
552 {"kRT_ACCELERATOR (ID 9)", COFF::RID_Accelerator},
553 {"kRT_RCDATA (ID 10)", COFF::RID_RCData},
554 {"kRT_MESSAGETABLE (ID 11)", COFF::RID_MessageTable},
555 {"kRT_GROUP_CURSOR (ID 12)", COFF::RID_Group_Cursor},
556 {"kRT_GROUP_ICON (ID 14)", COFF::RID_Group_Icon},
557 {"kRT_VERSION (ID 16)", COFF::RID_Version},
558 {"kRT_DLGINCLUDE (ID 17)", COFF::RID_DLGInclude},
559 {"kRT_PLUGPLAY (ID 19)", COFF::RID_PlugPlay},
560 {"kRT_VXD (ID 20)", COFF::RID_VXD},
561 {"kRT_ANICURSOR (ID 21)", COFF::RID_AniCursor},
562 {"kRT_ANIICON (ID 22)", COFF::RID_AniIcon},
563 {"kRT_HTML (ID 23)", COFF::RID_HTML},
564 {"kRT_MANIFEST (ID 24)", COFF::RID_Manifest}};
565
536566 template
537567 static std::error_code getSymbolAuxData(const COFFObjectFile *Obj,
538568 COFFSymbolRef Symbol,
15021532 error(S.getContents(Ref));
15031533
15041534 if ((Name == ".rsrc") || (Name == ".rsrc$01")) {
1505 auto Table =
1506 reinterpret_cast(Ref.data());
1507 char FormattedTime[20];
1508 time_t TDS = time_t(Table->TimeDateStamp);
1509 strftime(FormattedTime, sizeof(FormattedTime), "%Y-%m-%d %H:%M:%S",
1510 gmtime(&TDS));
1511 W.printHex("Time/Date Stamp", FormattedTime, Table->TimeDateStamp);
1512 }
1513 W.printBinaryBlock(Name.str() + " Data", Ref);
1514 }
1535 ResourceSectionRef RSF(Ref);
1536 auto &BaseTable = unwrapOrError(RSF.getBaseTable());
1537 printResourceDirectoryTable(RSF, BaseTable, "Type");
1538 }
1539 if (opts::SectionData)
1540 W.printBinaryBlock(Name.str() + " Data", Ref);
1541 }
1542 }
1543
1544 void COFFDumper::printResourceDirectoryTable(
1545 ResourceSectionRef RSF, const coff_resource_dir_table &Table,
1546 StringRef Level) {
1547 W.printNumber("String Name Entries", Table.NumberOfNameEntries);
1548 W.printNumber("ID Entries", Table.NumberOfIDEntries);
1549
1550 char FormattedTime[20] = {};
1551 time_t TDS = time_t(Table.TimeDateStamp);
1552 strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS));
1553
1554 // Iterate through level in resource directory tree.
1555 for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries;
1556 i++) {
1557 auto Entry = unwrapOrError(getResourceDirectoryTableEntry(Table, i));
1558 StringRef Name;
1559 SmallString<20> IDStr;
1560 raw_svector_ostream OS(IDStr);
1561 if (i < Table.NumberOfNameEntries) {
1562 StringRef EntryNameString = unwrapOrError(RSF.getEntryNameString(Entry));
1563 OS << ": ";
1564 OS << EntryNameString.str();
1565 } else {
1566 if (Level == "Type") {
1567 ScopedPrinter Printer(OS);
1568 Printer.printEnum("", Entry.Identifier.ID,
1569 makeArrayRef(ResourceTypeNames));
1570 IDStr = IDStr.slice(0, IDStr.find_first_of(")", 0) + 1);
1571 } else {
1572 OS << ": (ID " << Entry.Identifier.ID << ")";
1573 }
1574 }
1575 Name = StringRef(IDStr);
1576 ListScope ResourceType(W, Level.str() + Name.str());
1577 if (Entry.Offset.isSubDir()) {
1578 StringRef NextLevel;
1579 if (Level == "Name")
1580 NextLevel = "Language";
1581 else
1582 NextLevel = "Name";
1583 auto &NextTable = unwrapOrError(RSF.getEntrySubDir(Entry));
1584 printResourceDirectoryTable(RSF, NextTable, NextLevel);
1585 } else {
1586 W.printHex("Time/Date Stamp", FormattedTime, Table.TimeDateStamp);
1587 W.printNumber("Major Version", Table.MajorVersion);
1588 W.printNumber("Minor Version", Table.MinorVersion);
1589 }
1590 }
1591 }
1592
1593 ErrorOr
1594 COFFDumper::getResourceDirectoryTableEntry(const coff_resource_dir_table &Table,
1595 uint32_t Index) {
1596 if (Index >= Table.NumberOfNameEntries + Table.NumberOfIDEntries)
1597 return object_error::parse_failed;
1598 auto TablePtr = reinterpret_cast(&Table + 1);
1599 return TablePtr[Index];
15151600 }
15161601
15171602 void COFFDumper::printStackMap() const {