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

llvm-svn: 302386
This commit is contained in:
Eric Beckmann 2017-05-07 22:47:22 +00:00
parent 7e3e7afca8
commit 33fca46ec3
9 changed files with 357 additions and 28 deletions

View File

@ -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 DelayImportDirectoryEntryRef;
class ExportDirectoryEntryRef;
class ImportDirectoryEntryRef;
class ImportedSymbolRef;
class ResourceSectionRef;
using import_directory_iterator = content_iterator<ImportDirectoryEntryRef>;
using delay_import_directory_iterator =
@ -623,6 +625,26 @@ struct coff_base_reloc_block_entry {
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;
@ -1047,6 +1069,23 @@ private:
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

View File

@ -152,6 +152,30 @@ namespace COFF {
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;

View File

@ -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 @@ void COFFObjectFile::moveSymbolNext(DataRefImpl &Ref) const {
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;
}
@ -1591,3 +1592,47 @@ std::error_code BaseRelocRef::getRVA(uint32_t &Result) const {
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);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 822 B

View File

@ -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
}

View File

@ -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: ]

View File

@ -121,6 +121,10 @@ private:
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 @@ private:
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;
@ -534,6 +541,29 @@ static const EnumEntry<uint8_t> FileChecksumKindNames[] = {
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,
@ -1503,18 +1533,73 @@ void COFFDumper::printCOFFResources() {
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");
}
W.printBinaryBlock(Name.str() + " Data", Ref);
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);
}
}
}
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()) {