[llvm] [llvm-readobj][COFF] Implement --coff-pseudoreloc in llvm-readobj to dump runtime pseudo-relocation records (PR #151816)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Aug 2 06:10:20 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-binary-utilities
Author: Tomohiro Kashiwada (kikairoya)
<details>
<summary>Changes</summary>
MinGW toolchain uses "runtime pseudo-relocation" mechanism to support auto-importing symbols from DLLs.
There is no commonly used tools for dump the pseudo-relocation records, so we implement that functionality in llvm-readobj.
---
Full diff: https://github.com/llvm/llvm-project/pull/151816.diff
6 Files Affected:
- (added) llvm/test/tools/llvm-readobj/COFF/Inputs/pseudoreloc.exe ()
- (added) llvm/test/tools/llvm-readobj/COFF/pseudoreloc.test (+97)
- (modified) llvm/tools/llvm-readobj/COFFDumper.cpp (+109)
- (modified) llvm/tools/llvm-readobj/ObjDumper.h (+1)
- (modified) llvm/tools/llvm-readobj/Opts.td (+3)
- (modified) llvm/tools/llvm-readobj/llvm-readobj.cpp (+4)
``````````diff
diff --git a/llvm/test/tools/llvm-readobj/COFF/Inputs/pseudoreloc.exe b/llvm/test/tools/llvm-readobj/COFF/Inputs/pseudoreloc.exe
new file mode 100644
index 0000000000000..d4106e99d96f3
Binary files /dev/null and b/llvm/test/tools/llvm-readobj/COFF/Inputs/pseudoreloc.exe differ
diff --git a/llvm/test/tools/llvm-readobj/COFF/pseudoreloc.test b/llvm/test/tools/llvm-readobj/COFF/pseudoreloc.test
new file mode 100644
index 0000000000000..f3db464b4ae69
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/pseudoreloc.test
@@ -0,0 +1,97 @@
+RUN: llvm-readobj --coff-pseudoreloc %p/Inputs/pseudoreloc.exe | FileCheck %s
+RUN: llvm-readobj --coff-pseudoreloc %p/Inputs/nop.exe.coff-x86-64 | FileCheck %s --check-prefix=NOSYM
+RUN: llvm-readobj --coff-pseudoreloc %p/Inputs/trivial.obj.coff-i386 | FileCheck %s --check-prefix=NORELOC
+
+CHECK: Format: COFF-i386
+CHECK-NEXT: Arch: i386
+CHECK-NEXT: AddressSize: 32bit
+CHECK-NEXT: PseudoReloc [
+CHECK-NEXT: Entry {
+CHECK-NEXT: Symbol: 0x{{[0-9A-Z]+}}
+CHECK-NEXT: SymbolName: sym1
+CHECK-NEXT: Target: 0x{{[0-9A-Z]+}}
+CHECK-NEXT: BitWidth: {{[0-9]+}}
+CHECK-NEXT: }
+CHECK-NEXT: Entry {
+CHECK-NEXT: Symbol: 0x{{[0-9A-Z]+}}
+CHECK-NEXT: SymbolName: sym2
+CHECK-NEXT: Target: 0x{{[0-9A-Z]+}}
+CHECK-NEXT: BitWidth: {{[0-9]+}}
+CHECK-NEXT: }
+CHECK-NEXT: Entry {
+CHECK-NEXT: Symbol: 0x{{[0-9A-Z]+}}
+CHECK-NEXT: SymbolName: sym1
+CHECK-NEXT: Target: 0x{{[0-9A-Z]+}}
+CHECK-NEXT: BitWidth: {{[0-9]+}}
+CHECK-NEXT: }
+CHECK-NEXT: ]
+
+NOSYM-NOT: PseudoReloc
+NOSYM: The symbol table has been stripped
+NOSYM-NOT: PseudoReloc
+
+NORELOC-NOT: PseudoReloc
+NORELOC: The symbols for runtime pseudo-relocation are not found
+NORELOC-NOT: PseudoReloc
+
+
+pseudoreloc.exe is generated by following script:
+
+#--- generate.sh
+llvm-mc -triple i386-mingw32 -filetype obj pseudoreloc.dll.s -o pseudoreloc.dll.o
+ld.lld -m i386pe --dll pseudoreloc.dll.o -o pseudoreloc.dll -entry=
+llvm-mc -triple i386-mingw32 -filetype obj pseudoreloc.s -o pseudoreloc.o
+ld.lld -m i386pe pseudoreloc.o pseudoreloc.dll -o pseudoreloc.exe -entry=start
+
+#--- pseudoreloc.dll.s
+ .data
+ .globl _sym1
+_sym1:
+ .long 0x11223344
+ .globl _sym2
+_sym2:
+ .long 0x55667788
+ .section .drectve
+ .ascii " -export:sym1,data "
+ .ascii " -export:sym2,data "
+ .addrsig
+
+#--- pseudoreloc.s
+ .text
+ .globl _start
+_start:
+ mov _local1b, %eax
+ movsb (%eax), %ecx
+ mov _local2, %eax
+ movsb (%eax), %edx
+ mov _local1a, %eax
+ movsb (%eax), %eax
+ add %edx, %eax
+ add %ecx, %eax
+ ret
+
+ .globl __pei386_runtime_relocator
+__pei386_runtime_relocator:
+ mov ___RUNTIME_PSEUDO_RELOC_LIST__, %eax
+ mov ___RUNTIME_PSEUDO_RELOC_LIST_END__, %ecx
+ sub %ecx, %eax
+ ret
+
+ .data
+ .globl _local1a
+ .p2align 2
+_local1a:
+ .long _sym1+1
+
+ .globl _local2
+ .p2align 2
+_local2:
+ .long _sym2+1
+
+ .globl _local1b
+ .p2align 2
+_local1b:
+ .long _sym1+3
+
+ .addrsig
+
diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp
index 96e0a634648e4..45ca018b714f2 100644
--- a/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -95,6 +95,7 @@ class COFFDumper : public ObjDumper {
void printCOFFExports() override;
void printCOFFDirectives() override;
void printCOFFBaseReloc() override;
+ void printCOFFPseudoReloc() override;
void printCOFFDebugDirectory() override;
void printCOFFTLSDirectory() override;
void printCOFFResources() override;
@@ -2000,6 +2001,114 @@ void COFFDumper::printCOFFBaseReloc() {
}
}
+void COFFDumper::printCOFFPseudoReloc() {
+ const StringRef RelocBeginName = Obj->getArch() == Triple::x86
+ ? "___RUNTIME_PSEUDO_RELOC_LIST__"
+ : "__RUNTIME_PSEUDO_RELOC_LIST__";
+ const StringRef RelocEndName = Obj->getArch() == Triple::x86
+ ? "___RUNTIME_PSEUDO_RELOC_LIST_END__"
+ : "__RUNTIME_PSEUDO_RELOC_LIST_END__";
+
+ COFFSymbolRef RelocBegin, RelocEnd;
+ auto Count = Obj->getNumberOfSymbols();
+ if (Count == 0) {
+ W.startLine() << "The symbol table has been stripped\n";
+ return;
+ }
+ for (auto i = 0u;
+ i < Count && (!RelocBegin.getRawPtr() || !RelocEnd.getRawPtr()); ++i) {
+ auto Sym = Obj->getSymbol(i);
+ if (Sym.takeError())
+ continue;
+ auto Name = Obj->getSymbolName(*Sym);
+ if (Name.takeError())
+ continue;
+ if (*Name == RelocBeginName) {
+ if (Sym->getSectionNumber() > 0)
+ RelocBegin = *Sym;
+ } else if (*Name == RelocEndName) {
+ if (Sym->getSectionNumber() > 0)
+ RelocEnd = *Sym;
+ }
+ }
+ if (!RelocBegin.getRawPtr() || !RelocEnd.getRawPtr()) {
+ W.startLine()
+ << "The symbols for runtime pseudo-relocation are not found\n";
+ return;
+ }
+
+ ArrayRef<uint8_t> Data;
+ auto Section = Obj->getSection(RelocBegin.getSectionNumber());
+ if (auto E = Section.takeError()) {
+ reportError(std::move(E), Obj->getFileName());
+ return;
+ }
+ if (auto E = Obj->getSectionContents(*Section, Data)) {
+ reportError(std::move(E), Obj->getFileName());
+ return;
+ }
+ ArrayRef<uint8_t> RawRelocs =
+ Data.take_front(RelocEnd.getValue()).drop_front(RelocBegin.getValue());
+ struct alignas(4) PseudoRelocationHeader {
+ uint32_t Zero1;
+ uint32_t Zero2;
+ uint32_t Signature;
+ };
+ static const PseudoRelocationHeader HeaderV2 = {0, 0, 1};
+ if (RawRelocs.size() < sizeof(HeaderV2) ||
+ (memcmp(RawRelocs.data(), &HeaderV2, sizeof(HeaderV2)) != 0)) {
+ reportWarning(
+ createStringError("Invalid runtime pseudo-relocation records"),
+ Obj->getFileName());
+ return;
+ }
+ struct alignas(4) PseudoRelocationRecord {
+ uint32_t Symbol;
+ uint32_t Target;
+ uint32_t BitSize;
+ };
+ ArrayRef<PseudoRelocationRecord> RelocRecords(
+ reinterpret_cast<const PseudoRelocationRecord *>(
+ RawRelocs.data() + sizeof(PseudoRelocationHeader)),
+ (RawRelocs.size() - sizeof(PseudoRelocationHeader)) /
+ sizeof(PseudoRelocationRecord));
+
+ // Cache of symbol searched at least once in IAT
+ DenseMap<uint32_t, StringRef> ImportedSymbols;
+
+ ListScope D(W, "PseudoReloc");
+ for (const auto &Reloc : RelocRecords) {
+ DictScope Entry(W, "Entry");
+ W.printHex("Symbol", Reloc.Symbol);
+
+ // find and print the pointed symbol from IAT
+ [&]() {
+ for (auto D : Obj->import_directories()) {
+ uint32_t RVA;
+ if (auto E = D.getImportAddressTableRVA(RVA))
+ reportError(std::move(E), Obj->getFileName());
+ if (Reloc.Symbol < RVA)
+ continue;
+ for (auto S : D.imported_symbols()) {
+ if (RVA == Reloc.Symbol) {
+ if (auto E = S.getSymbolName(ImportedSymbols[RVA]))
+ reportError(std::move(E), Obj->getFileName());
+ return;
+ }
+ RVA += Obj->is64() ? 8 : 4;
+ }
+ }
+ }();
+ if (auto Ite = ImportedSymbols.find(Reloc.Symbol);
+ Ite != ImportedSymbols.end()) {
+ W.printString("SymbolName", Ite->second);
+ }
+
+ W.printHex("Target", Reloc.Target);
+ W.printNumber("BitWidth", Reloc.BitSize);
+ }
+}
+
void COFFDumper::printCOFFResources() {
ListScope ResourcesD(W, "Resources");
for (const SectionRef &S : Obj->sections()) {
diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h
index 1dc29661f7178..a654078a770ff 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.h
+++ b/llvm/tools/llvm-readobj/ObjDumper.h
@@ -146,6 +146,7 @@ class ObjDumper {
virtual void printCOFFExports() { }
virtual void printCOFFDirectives() { }
virtual void printCOFFBaseReloc() { }
+ virtual void printCOFFPseudoReloc() {}
virtual void printCOFFDebugDirectory() { }
virtual void printCOFFTLSDirectory() {}
virtual void printCOFFResources() {}
diff --git a/llvm/tools/llvm-readobj/Opts.td b/llvm/tools/llvm-readobj/Opts.td
index 48d43cc635a4f..d519e34a72983 100644
--- a/llvm/tools/llvm-readobj/Opts.td
+++ b/llvm/tools/llvm-readobj/Opts.td
@@ -82,6 +82,9 @@ def codeview_ghash : FF<"codeview-ghash", "Enable global hashing for CodeView ty
def codeview_merged_types : FF<"codeview-merged-types", "Display the merged CodeView type stream">, Group<grp_coff>;
def codeview_subsection_bytes : FF<"codeview-subsection-bytes", "Dump raw contents of codeview debug sections and records">, Group<grp_coff>;
def coff_basereloc : FF<"coff-basereloc", "Display .reloc section">, Group<grp_coff>;
+def coff_pseudoreloc
+ : FF<"coff-pseudoreloc", "Display runtime pseudo-relocations">,
+ Group<grp_coff>;
def coff_debug_directory : FF<"coff-debug-directory", "Display debug directory">, Group<grp_coff>;
def coff_directives : FF<"coff-directives", "Display .drectve section">, Group<grp_coff>;
def coff_exports : FF<"coff-exports", "Display export table">, Group<grp_coff>;
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index 4c84ed701bb9a..2b34761b2cc6c 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -154,6 +154,7 @@ static bool CodeViewEnableGHash;
static bool CodeViewMergedTypes;
bool CodeViewSubsectionBytes;
static bool COFFBaseRelocs;
+static bool COFFPseudoRelocs;
static bool COFFDebugDirectory;
static bool COFFDirectives;
static bool COFFExports;
@@ -305,6 +306,7 @@ static void parseOptions(const opt::InputArgList &Args) {
opts::CodeViewMergedTypes = Args.hasArg(OPT_codeview_merged_types);
opts::CodeViewSubsectionBytes = Args.hasArg(OPT_codeview_subsection_bytes);
opts::COFFBaseRelocs = Args.hasArg(OPT_coff_basereloc);
+ opts::COFFPseudoRelocs = Args.hasArg(OPT_coff_pseudoreloc);
opts::COFFDebugDirectory = Args.hasArg(OPT_coff_debug_directory);
opts::COFFDirectives = Args.hasArg(OPT_coff_directives);
opts::COFFExports = Args.hasArg(OPT_coff_exports);
@@ -492,6 +494,8 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
Dumper->printCOFFDirectives();
if (opts::COFFBaseRelocs)
Dumper->printCOFFBaseReloc();
+ if (opts::COFFPseudoRelocs)
+ Dumper->printCOFFPseudoReloc();
if (opts::COFFDebugDirectory)
Dumper->printCOFFDebugDirectory();
if (opts::COFFTLSDirectory)
``````````
</details>
https://github.com/llvm/llvm-project/pull/151816
More information about the llvm-commits
mailing list