[llvm] [llvm-readobj][COFF] Add JSON Output Style (PR #95074)
Miguel A. Arroyo via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 10 21:49:54 PDT 2024
https://github.com/mayanez created https://github.com/llvm/llvm-project/pull/95074
## Description
This PR introduces the `--coff-output-style` in the same vein as the `--elf-output-style`. In particular, it implements a `JSONCOFFDumper` which overrides a handful of the `COFFDumper` (aka LLVM style) print methods.
Most of the changes required for the `JSONCOFFDumper` are minor over the existing dumper with the main exception being the `Resource Directory Table`. The recursive traversal logic the `COFFDumper` implements for the `Resource Directory Table` makes it incompatible with JSON as it doesn't allow for logically segmenting scopes (ie. it uses the `OS` directly adding newlines etc).
I've chosen to leave the existing traversal logic in place and have it be only used for the `COFFDumper`. The `JSONCOFFDumper`'s traversal has been written to be able to keep appropriate scopes.
Tests were added mirroring those that currently exist but validating JSON output. The tests have a `-json.test` suffix similar to what is done in ELF instead of as additional FileCheck prefixes.
>From 4d1be316958a01f009731309ee3b550e96d99b83 Mon Sep 17 00:00:00 2001
From: Miguel Arroyo <miguel.arroyo at rockstargames.com>
Date: Sun, 9 Jun 2024 23:27:53 -0700
Subject: [PATCH] [llvm-readobj][COFF] Add JSON Output Style
---
.../llvm-readobj/COFF/file-summary-json.test | 96 +++++
.../tools/llvm-readobj/COFF/imports-json.test | 171 ++++++++
.../llvm-readobj/COFF/load-config-json.test | 85 ++++
.../tools/llvm-readobj/COFF/load-config.test | 8 +-
.../tools/llvm-readobj/COFF/output-style.test | 4 +
.../tools/llvm-readobj/COFF/pretty-print.test | 43 ++
.../llvm-readobj/COFF/resources-json.test | 388 ++++++++++++++++++
llvm/tools/llvm-readobj/COFFDumper.cpp | 276 ++++++++++++-
llvm/tools/llvm-readobj/Opts.td | 1 +
llvm/tools/llvm-readobj/llvm-readobj.cpp | 12 +
10 files changed, 1070 insertions(+), 14 deletions(-)
create mode 100644 llvm/test/tools/llvm-readobj/COFF/file-summary-json.test
create mode 100644 llvm/test/tools/llvm-readobj/COFF/imports-json.test
create mode 100644 llvm/test/tools/llvm-readobj/COFF/load-config-json.test
create mode 100644 llvm/test/tools/llvm-readobj/COFF/output-style.test
create mode 100644 llvm/test/tools/llvm-readobj/COFF/pretty-print.test
create mode 100644 llvm/test/tools/llvm-readobj/COFF/resources-json.test
diff --git a/llvm/test/tools/llvm-readobj/COFF/file-summary-json.test b/llvm/test/tools/llvm-readobj/COFF/file-summary-json.test
new file mode 100644
index 0000000000000..2e7c080f2bfeb
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/file-summary-json.test
@@ -0,0 +1,96 @@
+## Test how we output JSON file summaries.
+
+# RUN: rm -rf %t.dir
+# RUN: mkdir -p %t.dir
+# RUN: yaml2obj %s -o %t.dir/obj
+
+## Test outputting file summary for a single file.
+# RUN: llvm-readobj --coff-output-style=JSON --pretty-print %t.dir/obj | \
+# RUN: FileCheck %s --check-prefix=SINGLE \
+# RUN: --match-full-lines --strict-whitespace --implicit-check-not={{.}}
+
+# SINGLE:[
+# SINGLE-NEXT: {
+# SINGLE-NEXT: "FileSummary": {
+# SINGLE-NEXT: "File": "{{.*}}/obj",
+# SINGLE-NEXT: "Format": "COFF-x86-64",
+# SINGLE-NEXT: "Arch": "x86_64",
+# SINGLE-NEXT: "AddressSize": "64bit"
+# SINGLE-NEXT: }
+# SINGLE-NEXT: }
+# SINGLE-NEXT:]
+
+## Test outputting file summary for multiple files.
+# RUN: llvm-readobj --coff-output-style=JSON --pretty-print %t.dir/obj %t.dir/obj | \
+# RUN: FileCheck %s --check-prefix=MULTI \
+# RUN: --match-full-lines --strict-whitespace --implicit-check-not={{.}}
+
+# MULTI:[
+# MULTI-NEXT: {
+# MULTI-NEXT: "FileSummary": {
+# MULTI-NEXT: "File": "{{.*}}/obj",
+# MULTI-NEXT: "Format": "COFF-x86-64",
+# MULTI-NEXT: "Arch": "x86_64",
+# MULTI-NEXT: "AddressSize": "64bit"
+# MULTI-NEXT: }
+# MULTI-NEXT: },
+# MULTI-NEXT: {
+# MULTI-NEXT: "FileSummary": {
+# MULTI-NEXT: "File": "{{.*}}/obj",
+# MULTI-NEXT: "Format": "COFF-x86-64",
+# MULTI-NEXT: "Arch": "x86_64",
+# MULTI-NEXT: "AddressSize": "64bit"
+# MULTI-NEXT: }
+# MULTI-NEXT: }
+# MULTI-NEXT:]
+
+## Test outputting file summary for an archive with a single file.
+# RUN: rm -f %t.archive-single
+# RUN: llvm-ar rc %t.archive-single %t.dir/obj
+# RUN: llvm-readobj --coff-output-style=JSON --pretty-print %t.archive-single | \
+# RUN: FileCheck %s --check-prefix=ARCH-SINGLE \
+# RUN: --match-full-lines --strict-whitespace --implicit-check-not={{.}}
+
+# ARCH-SINGLE:[
+# ARCH-SINGLE-NEXT: {
+# ARCH-SINGLE-NEXT: "FileSummary": {
+# ARCH-SINGLE-NEXT: "File": "{{.*}}.archive-single(obj)",
+# ARCH-SINGLE-NEXT: "Format": "COFF-x86-64",
+# ARCH-SINGLE-NEXT: "Arch": "x86_64",
+# ARCH-SINGLE-NEXT: "AddressSize": "64bit"
+# ARCH-SINGLE-NEXT: }
+# ARCH-SINGLE-NEXT: }
+# ARCH-SINGLE-NEXT:]
+
+## Test outputting file summary for an archive with multiple files.
+# RUN: rm -f %t.archive-multiple
+# RUN: llvm-ar rc %t.archive-multiple %t.dir/obj %t.dir/obj
+# RUN: llvm-readobj --coff-output-style=JSON --pretty-print %t.archive-multiple | \
+# RUN: FileCheck %s --check-prefix=ARCH-MULTI \
+# RUN: --match-full-lines --strict-whitespace --implicit-check-not={{.}}
+
+# ARCH-MULTI:[
+# ARCH-MULTI-NEXT: {
+# ARCH-MULTI-NEXT: "FileSummary": {
+# ARCH-MULTI-NEXT: "File": "{{.*}}.archive-multiple(obj)",
+# ARCH-MULTI-NEXT: "Format": "COFF-x86-64",
+# ARCH-MULTI-NEXT: "Arch": "x86_64",
+# ARCH-MULTI-NEXT: "AddressSize": "64bit"
+# ARCH-MULTI-NEXT: }
+# ARCH-MULTI-NEXT: },
+# ARCH-MULTI-NEXT: {
+# ARCH-MULTI-NEXT: "FileSummary": {
+# ARCH-MULTI-NEXT: "File": "{{.*}}.archive-multiple(obj)",
+# ARCH-MULTI-NEXT: "Format": "COFF-x86-64",
+# ARCH-MULTI-NEXT: "Arch": "x86_64",
+# ARCH-MULTI-NEXT: "AddressSize": "64bit"
+# ARCH-MULTI-NEXT: }
+# ARCH-MULTI-NEXT: }
+# ARCH-MULTI-NEXT:]
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+symbols:
\ No newline at end of file
diff --git a/llvm/test/tools/llvm-readobj/COFF/imports-json.test b/llvm/test/tools/llvm-readobj/COFF/imports-json.test
new file mode 100644
index 0000000000000..6b5056f6c6337
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/imports-json.test
@@ -0,0 +1,171 @@
+RUN: llvm-readobj --coff-imports --coff-output-style=JSON --pretty-print %p/Inputs/imports.exe.coff-i386 | FileCheck -check-prefix=X86 %s
+RUN: llvm-readobj --coff-imports --coff-output-style=JSON --pretty-print %p/Inputs/imports.exe.coff-x86-64 --coff-output-style=JSON --pretty-print | FileCheck -check-prefixes=X64 %s
+
+X86: "Import": {
+X86-NEXT: "Name": "KERNEL32.dll",
+X86-NEXT: "ImportLookupTableRVA": 8456,
+X86-NEXT: "ImportAddressTableRVA": 8192,
+X86-NEXT: "Symbols": [
+X86-NEXT: {
+X86-NEXT: "Name": "ExitProcess",
+X86-NEXT: "Ordinal": 337
+X86-NEXT: },
+X86-NEXT: {
+X86-NEXT: "Name": "GetProcAddress",
+X86-NEXT: "Ordinal": 669
+X86-NEXT: },
+X86-NEXT: {
+X86-NEXT: "Name": "FreeLibrary",
+X86-NEXT: "Ordinal": 414
+X86-NEXT: },
+X86-NEXT: {
+X86-NEXT: "Name": "GetLastError",
+X86-NEXT: "Ordinal": 592
+X86-NEXT: },
+X86-NEXT: {
+X86-NEXT: "Name": "RaiseException",
+X86-NEXT: "Ordinal": 1087
+X86-NEXT: },
+X86-NEXT: {
+X86-NEXT: "Name": "LoadLibraryExA",
+X86-NEXT: "Ordinal": 934
+X86-NEXT: }
+X86-NEXT: ]
+X86-NEXT: },
+X86-NEXT: "Import": {
+X86-NEXT: "Name": "USER32.dll",
+X86-NEXT: "ImportLookupTableRVA": 8484,
+X86-NEXT: "ImportAddressTableRVA": 8220,
+X86-NEXT: "Symbols": [
+X86-NEXT: {
+X86-NEXT: "Name": "MessageBoxA",
+X86-NEXT: "Ordinal": 582
+X86-NEXT: }
+X86-NEXT: ]
+X86-NEXT: },
+X86-NEXT: "Import": {
+X86-NEXT: "Name": "mydll.dll",
+X86-NEXT: "ImportLookupTableRVA": 8492,
+X86-NEXT: "ImportAddressTableRVA": 8228,
+X86-NEXT: "Symbols": [
+X86-NEXT: {
+X86-NEXT: "Name": "Func1",
+X86-NEXT: "Ordinal": 0
+X86-NEXT: },
+X86-NEXT: {
+X86-NEXT: "Name": "Func2",
+X86-NEXT: "Ordinal": 1
+X86-NEXT: },
+X86-NEXT: {
+X86-NEXT: "Name": "",
+X86-NEXT: "Ordinal": 3
+X86-NEXT: }
+X86-NEXT: ]
+X86-NEXT: },
+X86-NEXT: "DelayImport": {
+X86-NEXT: "Name": "lazyload.dll",
+X86-NEXT: "Attributes": 1,
+X86-NEXT: "ModuleHandle": 12316,
+X86-NEXT: "ImportAddressTable": 12304,
+X86-NEXT: "ImportNameTable": 8336,
+X86-NEXT: "BoundDelayImportTable": 8364,
+X86-NEXT: "UnloadDelayImportTable": 0,
+X86-NEXT: "Symbols": [
+X86-NEXT: {
+X86-NEXT: "Name": "Func5",
+X86-NEXT: "Ordinal": 0,
+X86-NEXT: "Address": 4198515
+X86-NEXT: },
+X86-NEXT: {
+X86-NEXT: "Name": "Func4",
+X86-NEXT: "Ordinal": 0,
+X86-NEXT: "Address": 4198482
+X86-NEXT: }
+X86-NEXT: ]
+X86-NEXT: }
+X86-NEXT: }
+
+
+X64: "Import": {
+X64-NEXT: "Name": "KERNEL32.dll",
+X64-NEXT: "ImportLookupTableRVA": 8560,
+X64-NEXT: "ImportAddressTableRVA": 8192,
+X64-NEXT: "Symbols": [
+X64-NEXT: {
+X64-NEXT: "Name": "ExitProcess",
+X64-NEXT: "Ordinal": 343
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Name": "GetProcAddress",
+X64-NEXT: "Ordinal": 676
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Name": "FreeLibrary",
+X64-NEXT: "Ordinal": 420
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Name": "GetLastError",
+X64-NEXT: "Ordinal": 598
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Name": "RaiseException",
+X64-NEXT: "Ordinal": 1091
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Name": "LoadLibraryExA",
+X64-NEXT: "Ordinal": 937
+X64-NEXT: }
+X64-NEXT: ]
+X64-NEXT: },
+X64-NEXT: "Import": {
+X64-NEXT: "Name": "USER32.dll",
+X64-NEXT: "ImportLookupTableRVA": 8616,
+X64-NEXT: "ImportAddressTableRVA": 8248,
+X64-NEXT: "Symbols": [
+X64-NEXT: {
+X64-NEXT: "Name": "MessageBoxA",
+X64-NEXT: "Ordinal": 586
+X64-NEXT: }
+X64-NEXT: ]
+X64-NEXT: },
+X64-NEXT: "Import": {
+X64-NEXT: "Name": "mydll.dll",
+X64-NEXT: "ImportLookupTableRVA": 8632,
+X64-NEXT: "ImportAddressTableRVA": 8264,
+X64-NEXT: "Symbols": [
+X64-NEXT: {
+X64-NEXT: "Name": "Func1",
+X64-NEXT: "Ordinal": 0
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Name": "Func2",
+X64-NEXT: "Ordinal": 1
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Name": "",
+X64-NEXT: "Ordinal": 3
+X64-NEXT: }
+X64-NEXT: ]
+X64-NEXT: },
+X64-NEXT: "DelayImport": {
+X64-NEXT: "Name": "lazyload.dll",
+X64-NEXT: "Attributes": 1,
+X64-NEXT: "ModuleHandle": 12328,
+X64-NEXT: "ImportAddressTable": 12304,
+X64-NEXT: "ImportNameTable": 8416,
+X64-NEXT: "BoundDelayImportTable": 8456,
+X64-NEXT: "UnloadDelayImportTable": 0,
+X64-NEXT: "Symbols": [
+X64-NEXT: {
+X64-NEXT: "Name": "Func5",
+X64-NEXT: "Ordinal": 0,
+X64-NEXT: "Address": 5368713457
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Name": "Func4",
+X64-NEXT: "Ordinal": 0,
+X64-NEXT: "Address": 5368713318
+X64-NEXT: }
+X64-NEXT: ]
+X64-NEXT: }
+X64-NEXT: }
diff --git a/llvm/test/tools/llvm-readobj/COFF/load-config-json.test b/llvm/test/tools/llvm-readobj/COFF/load-config-json.test
new file mode 100644
index 0000000000000..93595462918f8
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/load-config-json.test
@@ -0,0 +1,85 @@
+RUN: llvm-readobj --coff-load-config --coff-output-style=JSON --pretty-print %S/Inputs/coff-load-config-x64.dll | FileCheck %s --check-prefix=X64
+
+X64: "LoadConfig": {
+X64-NEXT: "Size": 148,
+X64-NEXT: "TimeDateStamp": {
+X64-NEXT: "Name": "1970-01-01 00:00:00",
+X64-NEXT: "Value": 0
+X64-NEXT: },
+X64-NEXT: "MajorVersion": 0,
+X64-NEXT: "MinorVersion": 0,
+X64-NEXT: "GlobalFlagsClear": 0,
+X64-NEXT: "GlobalFlagsSet": 0,
+X64-NEXT: "CriticalSectionDefaultTimeout": 0,
+X64-NEXT: "DeCommitFreeBlockThreshold": 0,
+X64-NEXT: "DeCommitTotalFreeThreshold": 0,
+X64-NEXT: "LockPrefixTable": 0,
+X64-NEXT: "MaximumAllocationSize": 0,
+X64-NEXT: "VirtualMemoryThreshold": 0,
+X64-NEXT: "ProcessHeapFlags": 0,
+X64-NEXT: "ProcessAffinityMask": 0,
+X64-NEXT: "CSDVersion": 0,
+X64-NEXT: "DependentLoadFlags": 0,
+X64-NEXT: "EditList": 0,
+X64-NEXT: "SecurityCookie": 6442463256,
+X64-NEXT: "SEHandlerTable": 0,
+X64-NEXT: "SEHandlerCount": 0,
+X64-NEXT: "GuardCFCheckFunction": 6442459392,
+X64-NEXT: "GuardCFCheckDispatch": 6442459400,
+X64-NEXT: "GuardCFFunctionTable": 6442459480,
+X64-NEXT: "GuardCFFunctionCount": 9,
+X64-NEXT: "GuardFlags": {
+X64-NEXT: "Value": 79104,
+X64-NEXT: "Flags": [
+X64-NEXT: {
+X64-NEXT: "Name": "CF_FUNCTION_TABLE_PRESENT",
+X64-NEXT: "Value": 1024
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Name": "CF_INSTRUMENTED",
+X64-NEXT: "Value": 256
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Name": "CF_LONGJUMP_TABLE_PRESENT",
+X64-NEXT: "Value": 65536
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Name": "DELAYLOAD_IAT_IN_ITS_OWN_SECTION",
+X64-NEXT: "Value": 8192
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Name": "PROTECT_DELAYLOAD_IAT",
+X64-NEXT: "Value": 4096
+X64-NEXT: }
+X64-NEXT: ]
+X64-NEXT: }
+X64-NEXT: },
+X64-NEXT: "GuardFidTable": [
+X64-NEXT: {
+X64-NEXT: "Address": 6442455040
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Address": 6442455120
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Address": 6442455152
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Address": 6442455264
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Address": 6442455312
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Address": 6442456160
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Address": 6442457456
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Address": 6442457936
+X64-NEXT: },
+X64-NEXT: {
+X64-NEXT: "Address": 6442458512
+X64-NEXT: }
+X64-NEXT: ]
\ No newline at end of file
diff --git a/llvm/test/tools/llvm-readobj/COFF/load-config.test b/llvm/test/tools/llvm-readobj/COFF/load-config.test
index 63ba11a940ad8..3e1325b651488 100644
--- a/llvm/test/tools/llvm-readobj/COFF/load-config.test
+++ b/llvm/test/tools/llvm-readobj/COFF/load-config.test
@@ -5,7 +5,7 @@ RUN: llvm-readobj --coff-load-config %S/Inputs/coff-load-config-data-end.exe | F
RUN: llvm-readobj --coff-load-config %S/Inputs/coff-no-load-config.exe | FileCheck %s --check-prefix=NOCONFIG
-X86: LoadConfig [
+X86: LoadConfig {
X86: Size: 0x5C
X86: TimeDateStamp: 1970-01-01 00:00:00 (0x0)
X86: MajorVersion: 0x0
@@ -37,7 +37,7 @@ X86: CF_LONGJUMP_TABLE_PRESENT (0x10000)
X86: DELAYLOAD_IAT_IN_ITS_OWN_SECTION (0x2000)
X86: PROTECT_DELAYLOAD_IAT (0x1000)
X86: ]
-X86: ]
+X86: }
X86: SEHTable [
X86: 0x10001BE0
X86: 0x10001E30
@@ -56,7 +56,7 @@ X86: 0x10001BE0
X86: 0x10001DC0
X86: ]
-X64: LoadConfig [
+X64: LoadConfig {
X64: Size: 0x94
X64: TimeDateStamp: 1970-01-01 00:00:00 (0x0)
X64: MajorVersion: 0x0
@@ -88,7 +88,7 @@ X64: CF_LONGJUMP_TABLE_PRESENT (0x10000)
X64: DELAYLOAD_IAT_IN_ITS_OWN_SECTION (0x2000)
X64: PROTECT_DELAYLOAD_IAT (0x1000)
X64: ]
-X64: ]
+X64: }
X64-NOT: SEHTable
X64: GuardFidTable [
X64: 0x180001000
diff --git a/llvm/test/tools/llvm-readobj/COFF/output-style.test b/llvm/test/tools/llvm-readobj/COFF/output-style.test
new file mode 100644
index 0000000000000..70a1d5112b05d
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/output-style.test
@@ -0,0 +1,4 @@
+## Error for an unknown output style.
+RUN: not llvm-readobj --coff-output-style=unknown 2>&1 | FileCheck %s
+
+CHECK: error: --coff-output-style value should be either 'LLVM' or 'JSON', but was 'unknown'
\ No newline at end of file
diff --git a/llvm/test/tools/llvm-readobj/COFF/pretty-print.test b/llvm/test/tools/llvm-readobj/COFF/pretty-print.test
new file mode 100644
index 0000000000000..0ae42a7e09b73
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/pretty-print.test
@@ -0,0 +1,43 @@
+## Test the JSON pretty-print flag.
+#
+# RUN: yaml2obj %s -o %t.pretty
+
+## Test JSON with pretty-print off.
+# RUN: llvm-readobj --coff-output-style=JSON %t.pretty | \
+# RUN: FileCheck %s --check-prefix=NO-PRETTY \
+# RUN: --strict-whitespace
+
+# NO-PRETTY:[
+# NO-PRETTY-SAME:{
+# NO-PRETTY-SAME:"FileSummary":{
+# NO-PRETTY-SAME:"File":"{{.*}}.pretty",
+# NO-PRETTY-SAME:"Format":"COFF-x86-64",
+# NO-PRETTY-SAME:"Arch":"x86_64",
+# NO-PRETTY-SAME:"AddressSize":"64bit"
+# NO-PRETTY-SAME:}
+# NO-PRETTY-SAME:}
+# NO-PRETTY-SAME:]
+
+## Test JSON with pretty-print on.
+# RUN: llvm-readobj --coff-output-style=JSON --pretty-print %t.pretty | \
+# RUN: FileCheck %s --check-prefix=PRETTY \
+# RUN: --match-full-lines --strict-whitespace
+
+# PRETTY:[
+# PRETTY-NEXT: {
+# PRETTY-NEXT: "FileSummary": {
+# PRETTY-NEXT: "File": "{{.*}}.pretty",
+# PRETTY-NEXT: "Format": "COFF-x86-64",
+# PRETTY-NEXT: "Arch": "x86_64",
+# PRETTY-NEXT: "AddressSize": "64bit"
+# PRETTY-NEXT: }
+# PRETTY-NEXT: }
+# PRETTY-NEXT:]
+
+--- !COFF
+header:
+ Machine: IMAGE_FILE_MACHINE_AMD64
+ Characteristics: [ ]
+sections:
+symbols:
+
diff --git a/llvm/test/tools/llvm-readobj/COFF/resources-json.test b/llvm/test/tools/llvm-readobj/COFF/resources-json.test
new file mode 100644
index 0000000000000..7e752c971c6a1
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/resources-json.test
@@ -0,0 +1,388 @@
+RUN: llvm-readobj --coff-resources --coff-output-style=JSON --pretty-print %p/Inputs/zero-string-table.obj.coff-i386 \
+RUN: | FileCheck %s -check-prefix ZERO
+RUN: llvm-readobj --coff-resources --coff-output-style=JSON --pretty-print %p/Inputs/resources/test_resource.obj.coff \
+RUN: | FileCheck %s -check-prefix TEST_RES
+
+
+ZERO: "Resource Sections": [
+ZERO-NEXT: {
+ZERO-NEXT: "Name": ".rsrc$01",
+ZERO-NEXT: "Total Number of Resources": 1,
+ZERO-NEXT: "Base Table Address": 392,
+ZERO-NEXT: "Resource Type Table": {
+ZERO-NEXT: "Number of String Entries": 0,
+ZERO-NEXT: "Number of ID Entries": 1,
+ZERO-NEXT: "Entries": [
+ZERO-NEXT: {
+ZERO-NEXT: "Table Offset": 24,
+ZERO-NEXT: "Type": 6,
+ZERO-NEXT: "Resource Name Table": {
+ZERO-NEXT: "Number of String Entries": 0,
+ZERO-NEXT: "Number of ID Entries": 1,
+ZERO-NEXT: "Entries": [
+ZERO-NEXT: {
+ZERO-NEXT: "Table Offset": 48,
+ZERO-NEXT: "Name": "",
+ZERO-NEXT: "ID": 1,
+ZERO-NEXT: "Resource Language Table": {
+ZERO-NEXT: "Number of String Entries": 0,
+ZERO-NEXT: "Number of ID Entries": 1,
+ZERO-NEXT: "Entries": [
+ZERO-NEXT: {
+ZERO-NEXT: "Entry Offset": 72,
+ZERO-NEXT: "Time/Date Stamp": {
+ZERO-NEXT: "Name": "1970-01-01 00:00:00",
+ZERO-NEXT: "Value": 0
+ZERO-NEXT: },
+ZERO-NEXT: "Major Version": 0,
+ZERO-NEXT: "Minor Version": 0,
+ZERO-NEXT: "Characteristics": 0,
+ZERO-NEXT: "Data": {
+ZERO-NEXT: "DataRVA": 0,
+ZERO-NEXT: "DataSize": 42,
+ZERO-NEXT: "Codepage": 0,
+ZERO-NEXT: "Reserved": 0,
+ZERO-NEXT: "Data": {
+ZERO-NEXT: "Offset": 0,
+ZERO-NEXT: "Bytes": [
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 5,
+ZERO-NEXT: 0,
+ZERO-NEXT: 72,
+ZERO-NEXT: 0,
+ZERO-NEXT: 101,
+ZERO-NEXT: 0,
+ZERO-NEXT: 108,
+ZERO-NEXT: 0,
+ZERO-NEXT: 108,
+ZERO-NEXT: 0,
+ZERO-NEXT: 111,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0,
+ZERO-NEXT: 0
+ZERO-NEXT: ]
+ZERO-NEXT: }
+ZERO-NEXT: }
+ZERO-NEXT: }
+ZERO-NEXT: ]
+ZERO-NEXT: }
+ZERO-NEXT: }
+ZERO-NEXT: ]
+ZERO-NEXT: }
+ZERO-NEXT: }
+ZERO-NEXT: ]
+ZERO-NEXT: }
+ZERO-NEXT: },
+ZERO-NEXT: {
+ZERO-NEXT: "Name": ".rsrc$02"
+ZERO-NEXT: }
+ZERO-NEXT: ]
+
+
+TEST_RES: "Resource Sections": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Name": ".rsrc$01",
+TEST_RES-NEXT: "Total Number of Resources": 7,
+TEST_RES-NEXT: "Base Table Address": 448,
+TEST_RES-NEXT: "Resource Type Table": {
+TEST_RES-NEXT: "Number of String Entries": 0,
+TEST_RES-NEXT: "Number of ID Entries": 4,
+TEST_RES-NEXT: "Entries": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Table Offset": 48,
+TEST_RES-NEXT: "Type": 2,
+TEST_RES-NEXT: "Resource Name Table": {
+TEST_RES-NEXT: "Number of String Entries": 2,
+TEST_RES-NEXT: "Number of ID Entries": 0,
+TEST_RES-NEXT: "Entries": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Table Offset": 168,
+TEST_RES-NEXT: "Name": "CURSOR",
+TEST_RES-NEXT: "ID": 0,
+TEST_RES-NEXT: "Resource Language Table": {
+TEST_RES-NEXT: "Number of String Entries": 0,
+TEST_RES-NEXT: "Number of ID Entries": 1,
+TEST_RES-NEXT: "Entries": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Entry Offset": 336,
+TEST_RES-NEXT: "Time/Date Stamp": {
+TEST_RES-NEXT: "Name": "1970-01-01 00:00:00",
+TEST_RES-NEXT: "Value": 0
+TEST_RES-NEXT: },
+TEST_RES-NEXT: "Major Version": 0,
+TEST_RES-NEXT: "Minor Version": 0,
+TEST_RES-NEXT: "Characteristics": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "DataRVA": 0,
+TEST_RES-NEXT: "DataSize": 808,
+TEST_RES-NEXT: "Codepage": 0,
+TEST_RES-NEXT: "Reserved": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "Offset": 0,
+TEST_RES-NEXT: "Bytes": [
+TEST_RES: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: },
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Table Offset": 192,
+TEST_RES-NEXT: "Name": "OKAY",
+TEST_RES-NEXT: "ID": 0,
+TEST_RES-NEXT: "Resource Language Table": {
+TEST_RES-NEXT: "Number of String Entries": 0,
+TEST_RES-NEXT: "Number of ID Entries": 1,
+TEST_RES-NEXT: "Entries": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Entry Offset": 352,
+TEST_RES-NEXT: "Time/Date Stamp": {
+TEST_RES-NEXT: "Name": "1970-01-01 00:00:00",
+TEST_RES-NEXT: "Value": 0
+TEST_RES-NEXT: },
+TEST_RES-NEXT: "Major Version": 0,
+TEST_RES-NEXT: "Minor Version": 0,
+TEST_RES-NEXT: "Characteristics": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "DataRVA": 0,
+TEST_RES-NEXT: "DataSize": 808,
+TEST_RES-NEXT: "Codepage": 0,
+TEST_RES-NEXT: "Reserved": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "Offset": 0,
+TEST_RES-NEXT: "Bytes": [
+TEST_RES: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: },
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Table Offset": 80,
+TEST_RES-NEXT: "Type": 4,
+TEST_RES-NEXT: "Resource Name Table": {
+TEST_RES-NEXT: "Number of String Entries": 1,
+TEST_RES-NEXT: "Number of ID Entries": 1,
+TEST_RES-NEXT: "Entries": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Table Offset": 216,
+TEST_RES-NEXT: "Name": "\"EAT\"",
+TEST_RES-NEXT: "ID": 0,
+TEST_RES-NEXT: "Resource Language Table": {
+TEST_RES-NEXT: "Number of String Entries": 0,
+TEST_RES-NEXT: "Number of ID Entries": 1,
+TEST_RES-NEXT: "Entries": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Entry Offset": 368,
+TEST_RES-NEXT: "Time/Date Stamp": {
+TEST_RES-NEXT: "Name": "1970-01-01 00:00:00",
+TEST_RES-NEXT: "Value": 0
+TEST_RES-NEXT: },
+TEST_RES-NEXT: "Major Version": 0,
+TEST_RES-NEXT: "Minor Version": 0,
+TEST_RES-NEXT: "Characteristics": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "DataRVA": 0,
+TEST_RES-NEXT: "DataSize": 48,
+TEST_RES-NEXT: "Codepage": 0,
+TEST_RES-NEXT: "Reserved": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "Offset": 0,
+TEST_RES-NEXT: "Bytes": [
+TEST_RES: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: },
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Table Offset": 240,
+TEST_RES-NEXT: "Name": "",
+TEST_RES-NEXT: "ID": 14432,
+TEST_RES-NEXT: "Resource Language Table": {
+TEST_RES-NEXT: "Number of String Entries": 0,
+TEST_RES-NEXT: "Number of ID Entries": 1,
+TEST_RES-NEXT: "Entries": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Entry Offset": 384,
+TEST_RES-NEXT: "Time/Date Stamp": {
+TEST_RES-NEXT: "Name": "1970-01-01 00:00:00",
+TEST_RES-NEXT: "Value": 0
+TEST_RES-NEXT: },
+TEST_RES-NEXT: "Major Version": 0,
+TEST_RES-NEXT: "Minor Version": 0,
+TEST_RES-NEXT: "Characteristics": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "DataRVA": 0,
+TEST_RES-NEXT: "DataSize": 46,
+TEST_RES-NEXT: "Codepage": 0,
+TEST_RES-NEXT: "Reserved": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "Offset": 0,
+TEST_RES-NEXT: "Bytes": [
+TEST_RES: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: },
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Table Offset": 112,
+TEST_RES-NEXT: "Type": 5,
+TEST_RES-NEXT: "Resource Name Table": {
+TEST_RES-NEXT: "Number of String Entries": 1,
+TEST_RES-NEXT: "Number of ID Entries": 0,
+TEST_RES-NEXT: "Entries": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Table Offset": 264,
+TEST_RES-NEXT: "Name": "TESTDIALOG",
+TEST_RES-NEXT: "ID": 0,
+TEST_RES-NEXT: "Resource Language Table": {
+TEST_RES-NEXT: "Number of String Entries": 0,
+TEST_RES-NEXT: "Number of ID Entries": 1,
+TEST_RES-NEXT: "Entries": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Entry Offset": 400,
+TEST_RES-NEXT: "Time/Date Stamp": {
+TEST_RES-NEXT: "Name": "1970-01-01 00:00:00",
+TEST_RES-NEXT: "Value": 0
+TEST_RES-NEXT: },
+TEST_RES-NEXT: "Major Version": 0,
+TEST_RES-NEXT: "Minor Version": 0,
+TEST_RES-NEXT: "Characteristics": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "DataRVA": 0,
+TEST_RES-NEXT: "DataSize": 108,
+TEST_RES-NEXT: "Codepage": 0,
+TEST_RES-NEXT: "Reserved": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "Offset": 0,
+TEST_RES-NEXT: "Bytes": [
+TEST_RES: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: },
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Table Offset": 136,
+TEST_RES-NEXT: "Type": 9,
+TEST_RES-NEXT: "Resource Name Table": {
+TEST_RES-NEXT: "Number of String Entries": 1,
+TEST_RES-NEXT: "Number of ID Entries": 1,
+TEST_RES-NEXT: "Entries": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Table Offset": 288,
+TEST_RES-NEXT: "Name": "MYACCELERATORS",
+TEST_RES-NEXT: "ID": 0,
+TEST_RES-NEXT: "Resource Language Table": {
+TEST_RES-NEXT: "Number of String Entries": 0,
+TEST_RES-NEXT: "Number of ID Entries": 1,
+TEST_RES-NEXT: "Entries": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Entry Offset": 416,
+TEST_RES-NEXT: "Time/Date Stamp": {
+TEST_RES-NEXT: "Name": "1970-01-01 00:00:00",
+TEST_RES-NEXT: "Value": 0
+TEST_RES-NEXT: },
+TEST_RES-NEXT: "Major Version": 0,
+TEST_RES-NEXT: "Minor Version": 0,
+TEST_RES-NEXT: "Characteristics": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "DataRVA": 0,
+TEST_RES-NEXT: "DataSize": 24,
+TEST_RES-NEXT: "Codepage": 0,
+TEST_RES-NEXT: "Reserved": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "Offset": 0,
+TEST_RES-NEXT: "Bytes": [
+TEST_RES: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: },
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Table Offset": 312,
+TEST_RES-NEXT: "Name": "",
+TEST_RES-NEXT: "ID": 12,
+TEST_RES-NEXT: "Resource Language Table": {
+TEST_RES-NEXT: "Number of String Entries": 0,
+TEST_RES-NEXT: "Number of ID Entries": 1,
+TEST_RES-NEXT: "Entries": [
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Entry Offset": 432,
+TEST_RES-NEXT: "Time/Date Stamp": {
+TEST_RES-NEXT: "Name": "1970-01-01 00:00:00",
+TEST_RES-NEXT: "Value": 0
+TEST_RES-NEXT: },
+TEST_RES-NEXT: "Major Version": 0,
+TEST_RES-NEXT: "Minor Version": 0,
+TEST_RES-NEXT: "Characteristics": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "DataRVA": 0,
+TEST_RES-NEXT: "DataSize": 24,
+TEST_RES-NEXT: "Codepage": 0,
+TEST_RES-NEXT: "Reserved": 0,
+TEST_RES-NEXT: "Data": {
+TEST_RES-NEXT: "Offset": 0,
+TEST_RES-NEXT: "Bytes": [
+TEST_RES: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
+TEST_RES-NEXT: }
+TEST_RES-NEXT: },
+TEST_RES-NEXT: {
+TEST_RES-NEXT: "Name": ".rsrc$02"
+TEST_RES-NEXT: }
+TEST_RES-NEXT: ]
\ No newline at end of file
diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp
index b104774d37a93..e05165a2a6cab 100644
--- a/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -54,6 +54,7 @@
#include "llvm/Support/Win64EH.h"
#include "llvm/Support/raw_ostream.h"
#include <ctime>
+#include <functional>
using namespace llvm;
using namespace llvm::object;
@@ -108,8 +109,15 @@ class COFFDumper : public ObjDumper {
void printStackMap() const override;
void printAddrsig() override;
void printCGProfile() override;
+ virtual void printImportedSymbols(iterator_range<imported_symbol_iterator> Range);
+ virtual void printDelayImportedSymbols(
+ const DelayImportDirectoryEntryRef &I,
+ iterator_range<imported_symbol_iterator> Range);
+ typedef void (*PrintExtraCB)(raw_ostream &, const uint8_t *);
+ virtual void printRVATable(uint64_t TableVA, uint64_t Count, uint64_t EntrySize,
+ PrintExtraCB PrintExtra = nullptr);
-private:
+protected:
StringRef getSymbolName(uint32_t Index);
void printSymbols(bool ExtraSymInfo) override;
void printDynamicSymbols() override;
@@ -126,10 +134,6 @@ class COFFDumper : public ObjDumper {
void printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables);
template <typename IntTy>
void printCOFFTLSDirectory(const coff_tls_directory<IntTy> *TlsTable);
- typedef void (*PrintExtraCB)(raw_ostream &, const uint8_t *);
- void printRVATable(uint64_t TableVA, uint64_t Count, uint64_t EntrySize,
- PrintExtraCB PrintExtra = nullptr);
-
void printCodeViewSymbolSection(StringRef SectionName, const SectionRef &Section);
void printCodeViewTypeSection(StringRef SectionName, const SectionRef &Section);
StringRef getFileNameForFileOffset(uint32_t FileOffset);
@@ -174,10 +178,6 @@ class COFFDumper : public ObjDumper {
std::error_code resolveSymbolName(const coff_section *Section,
StringRef SectionContents,
const void *RelocPtr, StringRef &Name);
- void printImportedSymbols(iterator_range<imported_symbol_iterator> Range);
- void printDelayImportedSymbols(
- const DelayImportDirectoryEntryRef &I,
- iterator_range<imported_symbol_iterator> Range);
typedef DenseMap<const coff_section*, std::vector<RelocationRef> > RelocMapTy;
@@ -242,12 +242,45 @@ class COFFObjectDumpDelegate : public SymbolDumpDelegate {
StringRef SectionContents;
};
+class JSONCOFFDumper : public COFFDumper {
+public:
+ JSONCOFFDumper(const object::COFFObjectFile *ObjF, ScopedPrinter &Writer)
+ : COFFDumper(ObjF, Writer) {}
+
+ void printFileSummary(StringRef FileStr, ObjectFile &Obj,
+ ArrayRef<std::string> InputFilenames,
+ const Archive *A) override;
+ void
+ printImportedSymbols(iterator_range<imported_symbol_iterator> Range) override;
+ void printDelayImportedSymbols(
+ const DelayImportDirectoryEntryRef &I,
+ iterator_range<imported_symbol_iterator> Range) override;
+ void printCOFFResources() override;
+ void printRVATable(uint64_t TableVA, uint64_t Count, uint64_t EntrySize,
+ PrintExtraCB PrintExtra = nullptr) override;
+
+private:
+ using ResourceEntryTy = std::pair<std::string, std::reference_wrapper<const coff_resource_dir_entry>>;
+ std::vector<ResourceEntryTy> getResourceTableEntries(ResourceSectionRef RSF, const coff_resource_dir_table &Table, bool SubDirEntriesOnly = true);
+ void printResourceDirectoryTable(const coff_resource_dir_table &Table);
+ void printResourceDirectoryTypeTable(ResourceSectionRef RSF,
+ const coff_resource_dir_table &TypeTable);
+ void printResourceDirectoryNameTable(ResourceSectionRef RSF, const coff_resource_dir_table &NameTable);
+ void printResourceDirectoryLanguageTable(ResourceSectionRef RSF, const coff_resource_dir_table &LanguageTable);
+
+ std::unique_ptr<DictScope> FileScope;
+};
+
} // end namespace
namespace llvm {
std::unique_ptr<ObjDumper> createCOFFDumper(const object::COFFObjectFile &Obj,
ScopedPrinter &Writer) {
+ if (opts::Output == opts::JSON) {
+ return std::make_unique<JSONCOFFDumper>(&Obj, Writer);
+ }
+
return std::make_unique<COFFDumper>(&Obj, Writer);
}
@@ -979,7 +1012,7 @@ void COFFDumper::printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables) {
if (!Conf)
return;
- ListScope LS(W, "LoadConfig");
+ DictScope LS(W, "LoadConfig");
char FormattedTime[20] = {};
time_t TDS = Conf->TimeDateStamp;
strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS));
@@ -2234,3 +2267,226 @@ void COFFDumper::printCOFFTLSDirectory(
ArrayRef(ImageSectionCharacteristics),
COFF::SectionCharacteristics(COFF::IMAGE_SCN_ALIGN_MASK));
}
+
+void JSONCOFFDumper::printFileSummary(StringRef FileStr, ObjectFile &Obj,
+ ArrayRef<std::string> InputFilenames,
+ const Archive *A) {
+ FileScope = std::make_unique<DictScope>(this->W);
+ DictScope D(this->W, "FileSummary");
+ this->W.printString("File", FileStr);
+ this->W.printString("Format", Obj.getFileFormatName());
+ this->W.printString("Arch", Triple::getArchTypeName(Obj.getArch()));
+ this->W.printString(
+ "AddressSize",
+ std::string(formatv("{0}bit", 8 * Obj.getBytesInAddress())));
+ this->printLoadName();
+}
+
+void JSONCOFFDumper::printImportedSymbols(
+ iterator_range<imported_symbol_iterator> Range) {
+
+ ListScope SymbolsScope(W, "Symbols");
+ for (const ImportedSymbolRef &I : Range) {
+ DictScope SymbolScope(W);
+ StringRef Sym;
+ if (Error E = I.getSymbolName(Sym))
+ reportError(std::move(E), Obj->getFileName());
+
+ uint16_t Ordinal;
+ if (Error E = I.getOrdinal(Ordinal))
+ reportError(std::move(E), Obj->getFileName());
+
+ W.printString("Name", Sym);
+ W.printNumber("Ordinal", Ordinal);
+ }
+}
+
+void JSONCOFFDumper::printDelayImportedSymbols(
+ const DelayImportDirectoryEntryRef &I,
+ iterator_range<imported_symbol_iterator> Range) {
+ int Index = 0;
+
+ ListScope SymbolsScope(W, "Symbols");
+ for (const ImportedSymbolRef &S : Range) {
+ DictScope SymbolScope(W);
+ StringRef Sym;
+ if (Error E = S.getSymbolName(Sym))
+ reportError(std::move(E), Obj->getFileName());
+
+ uint16_t Ordinal;
+ if (Error E = S.getOrdinal(Ordinal))
+ reportError(std::move(E), Obj->getFileName());
+
+ uint64_t Addr;
+ if (Error E = I.getImportAddress(Index++, Addr))
+ reportError(std::move(E), Obj->getFileName());
+
+ W.printString("Name", Sym);
+ W.printNumber("Ordinal", Ordinal);
+ W.printHex("Address", Addr);
+ }
+}
+
+void JSONCOFFDumper::printRVATable(uint64_t TableVA, uint64_t Count,
+ uint64_t EntrySize, PrintExtraCB PrintExtra) {
+ uintptr_t TableStart, TableEnd;
+ if (Error E = Obj->getVaPtr(TableVA, TableStart))
+ reportError(std::move(E), Obj->getFileName());
+ if (Error E =
+ Obj->getVaPtr(TableVA + Count * EntrySize - 1, TableEnd))
+ reportError(std::move(E), Obj->getFileName());
+ TableEnd++;
+ for (uintptr_t I = TableStart; I < TableEnd; I += EntrySize) {
+ DictScope D(W);
+ uint32_t RVA = *reinterpret_cast<const ulittle32_t *>(I);
+ W.printHex("Address", Obj->getImageBase() + RVA);
+ }
+}
+
+void JSONCOFFDumper::printCOFFResources() {
+ ListScope ResourcesD(W, "Resource Sections");
+ for (const SectionRef &S : Obj->sections()) {
+ StringRef Name = unwrapOrError(Obj->getFileName(), S.getName());
+ if (!Name.starts_with(".rsrc"))
+ continue;
+
+ DictScope D(W);
+ W.printString("Name", Name);
+ StringRef Ref = unwrapOrError(Obj->getFileName(), S.getContents());
+
+ if ((Name == ".rsrc") || (Name == ".rsrc$01")) {
+ ResourceSectionRef RSF;
+ Error E = RSF.load(Obj, S);
+ if (E)
+ reportError(std::move(E), Obj->getFileName());
+ auto &TypeTable = unwrapOrError(Obj->getFileName(), RSF.getBaseTable());
+ W.printNumber("Total Number of Resources",
+ countTotalTableEntries(RSF, TypeTable, "Type"));
+ W.printHex("Base Table Address",
+ Obj->getCOFFSection(S)->PointerToRawData);
+ printResourceDirectoryTypeTable(RSF, TypeTable);
+ }
+ if (opts::SectionData)
+ W.printBinaryBlock("Data", Ref);
+ }
+}
+
+std::vector<JSONCOFFDumper::ResourceEntryTy> JSONCOFFDumper::getResourceTableEntries(ResourceSectionRef RSF, const coff_resource_dir_table &Table, bool SubDirEntriesOnly) {
+ std::vector<ResourceEntryTy> TableEntries;
+ for (int i = 0; i < Table.NumberOfNameEntries + Table.NumberOfIDEntries; ++i) {
+ auto &Entry = unwrapOrError(Obj->getFileName(), RSF.getTableEntry(Table, i));
+
+ std::string Name;
+
+ if (i < Table.NumberOfNameEntries) {
+ ArrayRef<UTF16> RawEntryNameString =
+ unwrapOrError(Obj->getFileName(), RSF.getEntryNameString(Entry));
+ std::vector<UTF16> EndianCorrectedNameString;
+ if (llvm::sys::IsBigEndianHost) {
+ EndianCorrectedNameString.resize(RawEntryNameString.size() + 1);
+ std::copy(RawEntryNameString.begin(), RawEntryNameString.end(),
+ EndianCorrectedNameString.begin() + 1);
+ EndianCorrectedNameString[0] = UNI_UTF16_BYTE_ORDER_MARK_SWAPPED;
+ RawEntryNameString = ArrayRef(EndianCorrectedNameString);
+ }
+ if (!llvm::convertUTF16ToUTF8String(RawEntryNameString, Name))
+ reportError(errorCodeToError(object_error::parse_failed),
+ Obj->getFileName());
+ }
+
+ if (Entry.Offset.isSubDir()) {
+ TableEntries.push_back({Name, Entry});
+ if (SubDirEntriesOnly)
+ continue;
+ }
+
+ TableEntries.push_back({Name, Entry});
+ }
+
+ return TableEntries;
+}
+
+void JSONCOFFDumper::printResourceDirectoryTable(const coff_resource_dir_table &Table) {
+ W.printNumber("Number of String Entries", Table.NumberOfNameEntries);
+ W.printNumber("Number of ID Entries", Table.NumberOfIDEntries);
+}
+
+void JSONCOFFDumper::printResourceDirectoryLanguageTable(ResourceSectionRef RSF, const coff_resource_dir_table &LanguageTable) {
+ DictScope D(W, "Resource Language Table");
+ printResourceDirectoryTable(LanguageTable);
+
+ std::vector<ResourceEntryTy> LanguageTableEntries = getResourceTableEntries(RSF, LanguageTable, false);
+
+ ListScope LLS(W, "Entries");
+ for (auto <P : LanguageTableEntries) {
+ const auto &LanguageTableEntry = LTP.second.get();
+
+ DictScope LD(W);
+ W.printHex("Entry Offset", LanguageTableEntry.Offset.value());
+ char FormattedTime[20] = {};
+ time_t TDS = time_t(LanguageTable.TimeDateStamp);
+ strftime(FormattedTime, 20, "%Y-%m-%d %H:%M:%S", gmtime(&TDS));
+ W.printHex("Time/Date Stamp", FormattedTime, LanguageTable.TimeDateStamp);
+ W.printNumber("Major Version", LanguageTable.MajorVersion);
+ W.printNumber("Minor Version", LanguageTable.MinorVersion);
+ W.printNumber("Characteristics", LanguageTable.Characteristics);
+ DictScope DataScope(W, "Data");
+ auto &DataEntry =
+ unwrapOrError(Obj->getFileName(), RSF.getEntryData(LanguageTableEntry));
+ W.printHex("DataRVA", DataEntry.DataRVA);
+ W.printNumber("DataSize", DataEntry.DataSize);
+ W.printNumber("Codepage", DataEntry.Codepage);
+ W.printNumber("Reserved", DataEntry.Reserved);
+ StringRef Contents =
+ unwrapOrError(Obj->getFileName(), RSF.getContents(DataEntry));
+ W.printBinaryBlock("Data", Contents);
+ }
+}
+
+void JSONCOFFDumper::printResourceDirectoryNameTable(ResourceSectionRef RSF, const coff_resource_dir_table &NameTable) {
+ DictScope D(W, "Resource Name Table");
+ printResourceDirectoryTable(NameTable);
+
+ std::vector<ResourceEntryTy> NameTableEntries = getResourceTableEntries(RSF, NameTable);
+
+ ListScope NLS(W, "Entries");
+ for (auto &NTP : NameTableEntries) {
+ auto &Name = NTP.first;
+ const auto &NameTableEntry = NTP.second.get();
+ DictScope ND(W);
+
+ W.printHex("Table Offset", NameTableEntry.Offset.value());
+ W.printString("Name", Name);
+
+ // The ID can be ignored if name is non-empty. We emit the "ID" so data is consistent.
+ auto ID = !Name.empty() ? 0 : NameTableEntry.Identifier.ID;
+ W.printNumber("ID", ID);
+
+ auto &LanguageTable =
+ unwrapOrError(Obj->getFileName(), RSF.getEntrySubDir(NameTableEntry));
+ printResourceDirectoryLanguageTable(RSF, LanguageTable);
+ }
+}
+
+void JSONCOFFDumper::printResourceDirectoryTypeTable(
+ ResourceSectionRef RSF, const coff_resource_dir_table &TypeTable) {
+ DictScope D(W, "Resource Type Table");
+
+ printResourceDirectoryTable(TypeTable);
+
+ ListScope LS(W, "Entries");
+ std::vector<ResourceEntryTy> TypeTableEntries = getResourceTableEntries(RSF, TypeTable);
+
+ for (auto &TTP : TypeTableEntries) {
+ const auto &TypeTableEntry = TTP.second.get();
+
+ DictScope TD(W);
+ W.printHex("Table Offset", TypeTableEntry.Offset.value());
+ W.printNumber("Type", TypeTableEntry.Identifier.ID);
+
+ auto &NameTable =
+ unwrapOrError(Obj->getFileName(), RSF.getEntrySubDir(TypeTableEntry));
+
+ printResourceDirectoryNameTable(RSF, NameTable);
+ }
+}
\ No newline at end of file
diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td
index 7d574d875d22e..2b752b8f64a3c 100644
--- a/llvm/tools/llvm-readobj/Opts.td
+++ b/llvm/tools/llvm-readobj/Opts.td
@@ -87,6 +87,7 @@ def coff_imports : FF<"coff-imports", "Display import table">, Group<grp_coff>;
def coff_load_config : FF<"coff-load-config", "Display load config">, Group<grp_coff>;
def coff_resources : FF<"coff-resources", "Display .rsrc section">, Group<grp_coff>;
def coff_tls_directory : FF<"coff-tls-directory", "Display TLS directory">, Group<grp_coff>;
+defm coff_output_style: Eq<"coff-output-style", "Specify COFF dump style: LLVM, JSON">, Group<grp_coff>;
// XCOFF specific options.
def grp_xcoff : OptionGroup<"kind">, HelpText<"OPTIONS (XCOFF specific)">;
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index 9ac324cc672f0..98f0ff6b107d0 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -309,6 +309,18 @@ static void parseOptions(const opt::InputArgList &Args) {
opts::COFFLoadConfig = Args.hasArg(OPT_coff_load_config);
opts::COFFResources = Args.hasArg(OPT_coff_resources);
opts::COFFTLSDirectory = Args.hasArg(OPT_coff_tls_directory);
+ if (Arg *A = Args.getLastArg(OPT_coff_output_style_EQ)) {
+ std::string OutputStyleChoice = A->getValue();
+ opts::Output = StringSwitch<opts::OutputStyleTy>(OutputStyleChoice)
+ .Case("LLVM", opts::OutputStyleTy::LLVM)
+ .Case("JSON", opts::OutputStyleTy::JSON)
+ .Default(opts::OutputStyleTy::UNKNOWN);
+ if (opts::Output == opts::OutputStyleTy::UNKNOWN) {
+ error("--coff-output-style value should be either 'LLVM' or "
+ "'JSON', but was '" +
+ OutputStyleChoice + "'");
+ }
+ }
// XCOFF specific options.
opts::XCOFFAuxiliaryHeader = Args.hasArg(OPT_auxiliary_header);
More information about the llvm-commits
mailing list