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
llvm-svn: 302399
#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"
class ExportDirectoryEntryRef;
class ImportDirectoryEntryRef;
class ImportedSymbolRef;
+class ResourceSectionRef;
using import_directory_iterator = content_iterator<ImportDirectoryEntryRef>;
using delay_import_directory_iterator =
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<uint32_t>(31) & NameOffset;
+ }
+ } Identifier;
+ union {
+ support::ulittle32_t DataEntryOffset;
+ support::ulittle32_t SubdirOffset;
+
+ bool isSubDir() const { return SubdirOffset >> 31; }
+ uint32_t value() const {
+ return maskTrailingOnes<uint32_t>(31) & SubdirOffset;
+ }
+
+ } Offset;
+};
+
struct coff_resource_dir_table {
support::ulittle32_t Characteristics;
support::ulittle32_t TimeDateStamp;
const COFFObjectFile *OwningObject = nullptr;
};
+class ResourceSectionRef {
+public:
+ ResourceSectionRef() = default;
+ explicit ResourceSectionRef(StringRef Ref) : BBS(Ref, support::little) {}
+
+ ErrorOr<StringRef> getEntryNameString(const coff_resource_dir_entry &Entry);
+ ErrorOr<const coff_resource_dir_table &>
+ getEntrySubDir(const coff_resource_dir_entry &Entry);
+ ErrorOr<const coff_resource_dir_table &> getBaseTable();
+
+private:
+ BinaryByteStream BBS;
+
+ ErrorOr<const coff_resource_dir_table &> getTableAtOffset(uint32_t Offset);
+ ErrorOr<StringRef> 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
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;
#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"
Expected<StringRef> 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;
}
Result = Header->PageRVA + Entry[Index].getOffset();
return std::error_code();
}
+
+#define RETURN_IF_ERROR(X) \
+ if (auto EC = errorToErrorCode(X)) \
+ return EC;
+
+ErrorOr<StringRef> ResourceSectionRef::getDirStringAtOffset(uint32_t Offset) {
+ BinaryStreamReader Reader = BinaryStreamReader(BBS);
+ Reader.setOffset(Offset);
+ uint16_t Length;
+ RETURN_IF_ERROR(Reader.readInteger(Length));
+ ArrayRef<UTF16> 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<StringRef>
+ResourceSectionRef::getEntryNameString(const coff_resource_dir_entry &Entry) {
+ return getDirStringAtOffset(Entry.Identifier.getNameOffset());
+}
+
+ErrorOr<const coff_resource_dir_table &>
+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<const coff_resource_dir_table &>
+ResourceSectionRef::getEntrySubDir(const coff_resource_dir_entry &Entry) {
+ return getTableAtOffset(Entry.Offset.value());
+}
+
+ErrorOr<const coff_resource_dir_table &> ResourceSectionRef::getBaseTable() {
+ return getTableAtOffset(0);
+}
--- /dev/null
+#include "windows.h"\r
+\r
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\r
+\r
+myaccelerators ACCELERATORS\r
+{\r
+ "^C", 999, VIRTKEY, ALT\r
+ "D", 1100, VIRTKEY, CONTROL, SHIFT\r
+ "^R", 444, ASCII, NOINVERT\r
+}\r
+\r
+cursor BITMAP "cursor_small.bmp"\r
+okay BITMAP "okay_small.bmp"\r
+\r
+14432 MENU\r
+LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED\r
+{\r
+ MENUITEM "yu", 100\r
+ MENUITEM "shala", 101\r
+ MENUITEM "kaoya", 102\r
+}\r
+\r
+testdialog DIALOG 10, 10, 200, 300\r
+STYLE WS_POPUP | WS_BORDER\r
+CAPTION "Test"\r
+{\r
+ CTEXT "Continue:", 1, 10, 10, 230, 14\r
+ PUSHBUTTON "&OK", 2, 66, 134, 161, 13\r
+}\r
+\r
+12 ACCELERATORS\r
+{\r
+ "X", 164, VIRTKEY, ALT\r
+ "H", 5678, VIRTKEY, CONTROL, SHIFT\r
+ "^R", 444, ASCII, NOINVERT\r
+}\r
+\r
+"eat" MENU\r
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS\r
+{\r
+ MENUITEM "fish", 100\r
+ MENUITEM "salad", 101\r
+ MENUITEM "duck", 102\r
+}\r
-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: ]
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);
void printDelayImportedSymbols(
const DelayImportDirectoryEntryRef &I,
iterator_range<imported_symbol_iterator> Range);
+ ErrorOr<const coff_resource_dir_entry &>
+ getResourceDirectoryTableEntry(const coff_resource_dir_table &Table,
+ uint32_t Index);
typedef DenseMap<const coff_section*, std::vector<RelocationRef> > RelocMapTy;
LLVM_READOBJ_ENUM_CLASS_ENT(FileChecksumKind, SHA256),
};
+static const EnumEntry<COFF::ResourceTypeID> 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 <typename T>
static std::error_code getSymbolAuxData(const COFFObjectFile *Obj,
COFFSymbolRef Symbol,
error(S.getContents(Ref));
if ((Name == ".rsrc") || (Name == ".rsrc$01")) {
- auto Table =
- reinterpret_cast<const coff_resource_dir_table *>(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<const coff_resource_dir_entry &>
+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<const coff_resource_dir_entry *>(&Table + 1);
+ return TablePtr[Index];
+}
+
void COFFDumper::printStackMap() const {
object::SectionRef StackMapSection;
for (auto Sec : Obj->sections()) {