[llvm] [llvm-readobj][COFF] Dump hybrid object for ARM64X files. (PR #102245)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 6 16:03:22 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-binary-utilities

Author: Jacek Caban (cjacek)

<details>
<summary>Changes</summary>

Hybrid ARM64X files may theoretically change anything in the object (it may change both the PE headers and each sections), so I just print everything for both the primary and hybrid object. In practice, changing things like section table would be questionable (and would probably fail at runtime), so I considered skipping it for the hybrid objects, but decided against it for consistency.

My primary reason to implement it was to be able to write proper tests for lld-link, but since then it has proven to be neat tool when working with ARM64X files.

---
Full diff: https://github.com/llvm/llvm-project/pull/102245.diff


7 Files Affected:

- (modified) llvm/include/llvm/Object/COFF.h (+1) 
- (modified) llvm/lib/Object/COFFObjectFile.cpp (+48) 
- (modified) llvm/test/tools/llvm-readobj/COFF/arm64x-reloc.yaml (+144) 
- (modified) llvm/tools/llvm-readobj/COFFDumper.cpp (+13) 
- (modified) llvm/tools/llvm-readobj/ObjDumper.cpp (+4-2) 
- (modified) llvm/tools/llvm-readobj/ObjDumper.h (+5) 
- (modified) llvm/tools/llvm-readobj/llvm-readobj.cpp (+8-5) 


``````````diff
diff --git a/llvm/include/llvm/Object/COFF.h b/llvm/include/llvm/Object/COFF.h
index b084754c5f8fb..dc43c87c4125c 100644
--- a/llvm/include/llvm/Object/COFF.h
+++ b/llvm/include/llvm/Object/COFF.h
@@ -1085,6 +1085,7 @@ class COFFObjectFile : public ObjectFile {
   Expected<SubtargetFeatures> getFeatures() const override {
     return SubtargetFeatures();
   }
+  std::unique_ptr<MemoryBuffer> getHybridObjectView() const;
 
   import_directory_iterator import_directory_begin() const;
   import_directory_iterator import_directory_end() const;
diff --git a/llvm/lib/Object/COFFObjectFile.cpp b/llvm/lib/Object/COFFObjectFile.cpp
index 34347e5681358..f8a177879b319 100644
--- a/llvm/lib/Object/COFFObjectFile.cpp
+++ b/llvm/lib/Object/COFFObjectFile.cpp
@@ -1489,6 +1489,54 @@ StringRef COFFObjectFile::mapDebugSectionName(StringRef Name) const {
       .Default(Name);
 }
 
+std::unique_ptr<MemoryBuffer> COFFObjectFile::getHybridObjectView() const {
+  if (getMachine() != COFF::IMAGE_FILE_MACHINE_ARM64X)
+    return nullptr;
+
+  std::unique_ptr<WritableMemoryBuffer> HybridView;
+
+  for (auto DynReloc : dynamic_relocs()) {
+    if (DynReloc.getType() != COFF::IMAGE_DYNAMIC_RELOCATION_ARM64X)
+      continue;
+
+    for (auto reloc : DynReloc.arm64x_relocs()) {
+      if (!HybridView) {
+        HybridView =
+            WritableMemoryBuffer::getNewUninitMemBuffer(Data.getBufferSize());
+        memcpy(HybridView->getBufferStart(), Data.getBufferStart(),
+               Data.getBufferSize());
+      }
+
+      uint32_t RVA = reloc.getRVA();
+      void *Ptr;
+      uintptr_t IntPtr;
+      if (RVA & ~0xfff) {
+        cantFail(getRvaPtr(RVA, IntPtr));
+        Ptr = HybridView->getBufferStart() + IntPtr -
+              reinterpret_cast<uintptr_t>(base());
+      } else {
+        // PE header relocation.
+        Ptr = HybridView->getBufferStart() + RVA;
+      }
+
+      switch (reloc.getType()) {
+      case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL:
+        memset(Ptr, 0, reloc.getSize());
+        break;
+      case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE: {
+        auto Value = static_cast<ulittle64_t>(reloc.getValue());
+        memcpy(Ptr, &Value, reloc.getSize());
+        break;
+      }
+      case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA:
+        *reinterpret_cast<ulittle32_t *>(Ptr) += reloc.getValue();
+        break;
+      }
+    }
+  }
+  return HybridView;
+}
+
 bool ImportDirectoryEntryRef::
 operator==(const ImportDirectoryEntryRef &Other) const {
   return ImportTable == Other.ImportTable && Index == Other.Index;
diff --git a/llvm/test/tools/llvm-readobj/COFF/arm64x-reloc.yaml b/llvm/test/tools/llvm-readobj/COFF/arm64x-reloc.yaml
index df2d83db3a6a1..851dba17879c4 100644
--- a/llvm/test/tools/llvm-readobj/COFF/arm64x-reloc.yaml
+++ b/llvm/test/tools/llvm-readobj/COFF/arm64x-reloc.yaml
@@ -86,6 +86,32 @@
 # CHECK-NEXT:   ]
 # CHECK-NEXT: ]
 
+# RUN: llvm-readobj --hex-dump=.test %t.dll | FileCheck --check-prefix=HEX %s
+# HEX:      Format: COFF-ARM64X
+# HEX-NEXT: Arch: aarch64
+# HEX-NEXT: AddressSize: 64bit
+# HEX-EMPTY:
+# HEX-NEXT: Hex dump of section '.test':
+# HEX-NEXT: 0x180006000 11112222 33334444 55556666 77778888 ..""33DDUUffww..
+# HEX-NEXT: 0x180006010 9999aaaa bbbbcccc ddddeeee ffff0000 ................
+# HEX-NEXT: 0x180006020 00000000 00000000 00000000 00000000 ................
+# HEX-NEXT: 0x180006030 00000000 00000000 00000000 00000000 ................
+# HEX-NEXT: 0x180006040 10101010 20202020 30303030 40404040 ....    0000@@@@
+# HEX-NEXT: 0x180006050 50505050 60606060 70707070 80808080 PPPP````pppp....
+# HEX-NEXT: HybridObject {
+# HEX-NEXT:   Format: COFF-ARM64EC
+# HEX-NEXT:   Arch: aarch64
+# HEX-NEXT:   AddressSize: 64bit
+# HEX-EMPTY:
+# HEX-NEXT:   Hex dump of section '.test':
+# HEX-NEXT:   0x180006000 00002222 00000000 55556666 77778888 ..""....UUffww..
+# HEX-NEXT:   0x180006010 00000000 00000000 ddddeeee ffff0000 ................
+# HEX-NEXT:   0x180006020 12340000 23456789 11223344 55667788 .4..#Eg.."3DUfw.
+# HEX-NEXT:   0x180006030 00000000 00000000 00000000 00000000 ................
+# HEX-NEXT:   0x180006040 941c1110 28392220 20303030 20404040 ....(9"  000 @@@
+# HEX-NEXT:   0x180006050 50505050 60606060 70707070 80808080 PPPP````pppp....
+# HEX-NEXT: }
+
 
 --- !COFF
 OptionalHeader:
@@ -203,6 +229,7 @@ symbols:         []
 
 # RUN: yaml2obj %s --docnum=2 -o %t2.dll
 # RUN: llvm-readobj --coff-load-config %t2.dll | FileCheck  --check-prefixes=CHECK,V2 %s
+# RUN: llvm-readobj --hex-dump=.test %t2.dll | FileCheck --check-prefix=HEX %s
 
 --- !COFF
 OptionalHeader:
@@ -320,3 +347,120 @@ sections:
       - Binary: 0000      # terminator
 symbols:         []
 ...
+
+# RUN: yaml2obj %s --docnum=3 -o %t3.dll
+# RUN: llvm-readobj --coff-exports %t3.dll | FileCheck  --check-prefix=EXP %s
+
+# EXP:      Format: COFF-ARM64X
+# EXP-NEXT: Arch: aarch64
+# EXP-NEXT: AddressSize: 64bit
+# EXP-NEXT: Export {
+# EXP-NEXT:   Ordinal: 1
+# EXP-NEXT:   Name: test
+# EXP-NEXT:   RVA: 0x2000
+# EXP-NEXT: }
+# EXP-NEXT: HybridObject {
+# EXP-NEXT:   Format: COFF-ARM64EC
+# EXP-NEXT:   Arch: aarch64
+# EXP-NEXT:   AddressSize: 64bit
+# EXP-NEXT:   Export {
+# EXP-NEXT:     Ordinal: 1
+# EXP-NEXT:     Name: test
+# EXP-NEXT:     RVA: 0x2004
+# EXP-NEXT:   }
+# EXP-NEXT: }
+
+--- !COFF
+OptionalHeader:
+  ImageBase:        0x180000000
+  SectionAlignment: 4096
+  FileAlignment:    512
+  DLLCharacteristics: [ ]
+  AddressOfEntryPoint: 0
+  ExportTable:
+    RelativeVirtualAddress: 0x1000
+    Size:                   64
+  LoadConfigTable:
+    RelativeVirtualAddress: 0x3000
+    Size:                   320
+header:
+  Machine:         IMAGE_FILE_MACHINE_ARM64
+  Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE, IMAGE_FILE_DLL ]
+sections:
+  - Name:            .rdata
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+    VirtualAddress:  0x1000
+    VirtualSize:     64
+    StructuredData:
+      - UInt32: 0       # ExportFlags
+      - UInt32: 0       # TimeDateStamp
+      - UInt32: 0       # Version
+      - UInt32: 0x1028  # NameRVA
+      - UInt32: 1       # OrdinalBase
+      - UInt32: 1       # AddressTableEntries
+      - UInt32: 1       # NumberOfNamePointers
+      - UInt32: 0x1030  # ExportAddressTableRVA
+      - UInt32: 0x1034  # NamePointerRVA
+      - UInt32: 0x1038  # OrdinalTableRVA
+      - Binary: 7473742E646C6C00  # "tst.dll"
+      - UInt32: 0x2000  # export RVA
+      - UInt32: 0x103A  # name RVA
+      - Binary: 0000    # ordinal
+      - Binary: 7465737400 # "test"
+  - Name:            .data
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ]
+    VirtualAddress:  0x2000
+    VirtualSize:     80
+    StructuredData:
+      - UInt32: 1       # Version
+      - UInt32: 0       # CodeMap
+      - UInt32: 0       # CodeMapCount
+      - UInt32: 0       # CodeRangesToEntryPoints
+      - UInt32: 0       # RedirectionMetadata
+      - UInt32: 0
+      - UInt32: 0
+      - UInt32: 0
+      - UInt32: 0
+      - UInt32: 0
+      - UInt32: 0
+      - UInt32: 0
+      - UInt32: 0       # CodeRangesToEntryPointsCount
+      - UInt32: 0       # RedirectionMetadataCount
+      - UInt32: 0
+      - UInt32: 0
+      - UInt32: 0
+      - UInt32: 0
+      - UInt32: 0
+      - UInt32: 0
+  - Name:            .cfg
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+    VirtualAddress:  0x3000
+    VirtualSize:     328
+    StructuredData:
+      - LoadConfig:
+          CHPEMetadataPointer: 0x180002000
+          DynamicValueRelocTableOffset: 0
+          DynamicValueRelocTableSection: 4
+  - Name:            .arm64x
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_DISCARDABLE ]
+    VirtualAddress:  0x4000
+    VirtualSize:     56
+    StructuredData:
+      - UInt32: 2         # coff_dynamic_reloc_table.Version
+      - UInt32: 48        # coff_dynamic_reloc_table.Size
+      - UInt32: 24        # coff_dynamic_relocation64_v2.HeaderSize
+      - UInt32: 24        # coff_dynamic_relocation64_v2.FixupInfoSize
+      - UInt32: 6         # coff_dynamic_relocation64_v2.Symbol(low) = IMAGE_DYNAMIC_RELOCATION_ARM64X
+      - UInt32: 0         # coff_dynamic_relocation64_v2.Symbol(high)
+      - UInt32: 0         # coff_dynamic_relocation64_v2.SymbolGroup
+      - UInt32: 0         # coff_dynamic_relocation64_v2.Flags
+      - UInt32: 0         # coff_base_reloc_block_header[0].PageRVA
+      - UInt32: 12        # coff_base_reloc_block_header[0].BlockSize
+      - Binary: 8450      # VALUE offset 0x84 (PE header Machine), size 2
+      - Binary: 6486      # IMAGE_FILE_MACHINE_AMD64
+      - UInt32: 0x1000    # coff_base_reloc_block_header[1].PageRVA
+      - UInt32: 12        # coff_base_reloc_block_header[1].BlockSize
+      - Binary: 3020      # DELTA offset 0x30, mul 4
+      - Binary: 0100
+symbols:         []
+...
diff --git a/llvm/tools/llvm-readobj/COFFDumper.cpp b/llvm/tools/llvm-readobj/COFFDumper.cpp
index 65d67d29a5aa3..cb8750f377416 100644
--- a/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -99,6 +99,7 @@ class COFFDumper : public ObjDumper {
   void printCOFFTLSDirectory() override;
   void printCOFFResources() override;
   void printCOFFLoadConfig() override;
+  void printCOFFHybridObject() override;
   void printCodeViewDebugInfo() override;
   void mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs,
                           llvm::codeview::MergingTypeTableBuilder &CVTypes,
@@ -2271,3 +2272,15 @@ void COFFDumper::printCOFFTLSDirectory(
                ArrayRef(ImageSectionCharacteristics),
                COFF::SectionCharacteristics(COFF::IMAGE_SCN_ALIGN_MASK));
 }
+
+void COFFDumper::printCOFFHybridObject() {
+  std::unique_ptr<MemoryBuffer> HybridView = Obj->getHybridObjectView();
+  if (!HybridView)
+    return;
+  Expected<std::unique_ptr<COFFObjectFile>> HybridObjOrErr =
+      COFFObjectFile::create(*HybridView);
+  if (!HybridObjOrErr)
+    reportError(HybridObjOrErr.takeError(), Obj->getFileName().str());
+  DictScope D(Writer, "HybridObject");
+  ObjDumper::dumpObject(**HybridObjOrErr, Writer, nullptr, true);
+}
diff --git a/llvm/tools/llvm-readobj/ObjDumper.cpp b/llvm/tools/llvm-readobj/ObjDumper.cpp
index 20e99d9d97f3a..d02d1e543d31d 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.cpp
+++ b/llvm/tools/llvm-readobj/ObjDumper.cpp
@@ -91,8 +91,10 @@ void ObjDumper::printAsStringList(StringRef StringContent,
 void ObjDumper::printFileSummary(StringRef FileStr, object::ObjectFile &Obj,
                                  ArrayRef<std::string> InputFilenames,
                                  const object::Archive *A) {
-  W.getOStream() << "\n";
-  W.printString("File", FileStr);
+  if (!FileStr.empty()) {
+    W.getOStream() << "\n";
+    W.printString("File", FileStr);
+  }
   W.printString("Format", Obj.getFileFormatName());
   W.printString("Arch", Triple::getArchTypeName(Obj.getArch()));
   W.printString("AddressSize",
diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h
index cd744e3bbfb71..ffe6797711c8b 100644
--- a/llvm/tools/llvm-readobj/ObjDumper.h
+++ b/llvm/tools/llvm-readobj/ObjDumper.h
@@ -149,6 +149,7 @@ class ObjDumper {
   virtual void printCOFFTLSDirectory() {}
   virtual void printCOFFResources() {}
   virtual void printCOFFLoadConfig() { }
+  virtual void printCOFFHybridObject() {}
   virtual void printCodeViewDebugInfo() { }
   virtual void
   mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs,
@@ -185,6 +186,10 @@ class ObjDumper {
   void reportUniqueWarning(Error Err) const;
   void reportUniqueWarning(const Twine &Msg) const;
 
+  static void dumpObject(object::ObjectFile &Obj, ScopedPrinter &Writer,
+                         const object::Archive *A = nullptr,
+                         bool IsHybrid = false);
+
 protected:
   ScopedPrinter &W;
 
diff --git a/llvm/tools/llvm-readobj/llvm-readobj.cpp b/llvm/tools/llvm-readobj/llvm-readobj.cpp
index 15d838617063b..b3df584a410d9 100644
--- a/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -360,8 +360,8 @@ createDumper(const ObjectFile &Obj, ScopedPrinter &Writer) {
 }
 
 /// Dumps the specified object file.
-static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
-                       const Archive *A = nullptr) {
+void ObjDumper::dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
+                           const Archive *A, bool IsHybrid) {
   std::string FileStr =
       A ? Twine(A->getFileName() + "(" + Obj.getFileName() + ")").str()
         : Obj.getFileName().str();
@@ -538,6 +538,9 @@ static void dumpObject(ObjectFile &Obj, ScopedPrinter &Writer,
     Dumper->printStackMap();
   if (opts::PrintStackSizes)
     Dumper->printStackSizes();
+
+  if (!IsHybrid && Obj.isCOFF())
+    Dumper->printCOFFHybridObject();
 }
 
 /// Dumps each object file in \a Arc;
@@ -553,7 +556,7 @@ static void dumpArchive(const Archive *Arc, ScopedPrinter &Writer) {
 
     Binary *Bin = ChildOrErr->get();
     if (ObjectFile *Obj = dyn_cast<ObjectFile>(Bin))
-      dumpObject(*Obj, Writer, Arc);
+      ObjDumper::dumpObject(*Obj, Writer, Arc);
     else if (COFFImportFile *Imp = dyn_cast<COFFImportFile>(Bin))
       dumpCOFFImportFile(Imp, Writer);
     else
@@ -572,7 +575,7 @@ static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary,
   for (const MachOUniversalBinary::ObjectForArch &Obj : UBinary->objects()) {
     Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = Obj.getAsObjectFile();
     if (ObjOrErr)
-      dumpObject(*ObjOrErr.get(), Writer);
+      ObjDumper::dumpObject(*ObjOrErr.get(), Writer);
     else if (auto E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError()))
       reportError(ObjOrErr.takeError(), UBinary->getFileName());
     else if (Expected<std::unique_ptr<Archive>> AOrErr = Obj.getAsArchive())
@@ -618,7 +621,7 @@ static void dumpInput(StringRef File, ScopedPrinter &Writer) {
                dyn_cast<MachOUniversalBinary>(Bin.get()))
     dumpMachOUniversalBinary(UBinary, Writer);
   else if (ObjectFile *Obj = dyn_cast<ObjectFile>(Bin.get()))
-    dumpObject(*Obj, Writer);
+    ObjDumper::dumpObject(*Obj, Writer);
   else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(Bin.get()))
     dumpCOFFImportFile(Import, Writer);
   else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(Bin.get()))

``````````

</details>


https://github.com/llvm/llvm-project/pull/102245


More information about the llvm-commits mailing list