[llvm] 2201164 - [llvm] Win x64 Unwind V2 2/n: Support dumping UOP_Epilog (#110338)

via llvm-commits llvm-commits at lists.llvm.org
Mon Jan 13 16:53:46 PST 2025


Author: Daniel Paoliello
Date: 2025-01-13T16:53:42-08:00
New Revision: 2201164477982c2bd20fa2e925f567585c390805

URL: https://github.com/llvm/llvm-project/commit/2201164477982c2bd20fa2e925f567585c390805
DIFF: https://github.com/llvm/llvm-project/commit/2201164477982c2bd20fa2e925f567585c390805.diff

LOG: [llvm] Win x64 Unwind V2 2/n: Support dumping UOP_Epilog (#110338)

Adds support to objdump and readobj for reading the `UOP_Epilog` entries
of Windows x64 unwind v2.

`UOP_Epilog` has a weird format:

The first `UOP_Epilog` in the unwind data is the "header":
* The least-significant bit of `OpInfo` is the "At End" flag, which
signifies that there is an epilog at the very end of the associated
function.
* `CodeOffset` is the length each epilog described by the current unwind
information (all epilogs have the same length).

Any subsequent `UOP_Epilog` represents another epilog for the current
function, where `OpInfo` and `CodeOffset` are combined to a 12-bit value
which is the offset of the beginning of the epilog from the end of the
current function. If the offset is 0, then this entry is actually
padding and can be ignored.

Added: 
    llvm/test/tools/llvm-objdump/COFF/win64-unwindv2.yaml

Modified: 
    llvm/include/llvm/Support/Win64EH.h
    llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-image.yaml
    llvm/tools/llvm-objdump/COFFDump.cpp
    llvm/tools/llvm-readobj/Win64EHDumper.cpp
    llvm/tools/llvm-readobj/Win64EHDumper.h

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Support/Win64EH.h b/llvm/include/llvm/Support/Win64EH.h
index cf54f49286830a..8a9be153739171 100644
--- a/llvm/include/llvm/Support/Win64EH.h
+++ b/llvm/include/llvm/Support/Win64EH.h
@@ -124,6 +124,11 @@ union UnwindCode {
   uint8_t getOpInfo() const {
     return (u.UnwindOpAndOpInfo >> 4) & 0x0F;
   }
+  /// Gets the offset for an UOP_Epilog unwind code.
+  uint32_t getEpilogOffset() const {
+    assert(getUnwindOp() == UOP_Epilog);
+    return (getOpInfo() << 8) | static_cast<uint32_t>(u.CodeOffset);
+  }
 };
 
 enum {

diff  --git a/llvm/test/tools/llvm-objdump/COFF/win64-unwindv2.yaml b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv2.yaml
new file mode 100644
index 00000000000000..20abcb959120d8
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/COFF/win64-unwindv2.yaml
@@ -0,0 +1,175 @@
+# RUN: yaml2obj %s -o %t.exe
+# RUN: llvm-objdump --unwind-info %t.exe | FileCheck %s
+
+# CHECK-LABEL:  Unwind info:
+# CHECK-EMPTY:
+# CHECK-NEXT:   Function Table:
+# CHECK-NEXT:     Start Address: 0x1010
+# CHECK-NEXT:     End Address: 0x1017
+# CHECK-NEXT:     Unwind Info Address: 0x2000
+# CHECK-NEXT:       Version: 2
+# CHECK-NEXT:       Flags: 0
+# CHECK-NEXT:       Size of prolog: 4
+# CHECK-NEXT:       Number of Codes: 3
+# CHECK-NEXT:       No frame pointer used
+# CHECK-NEXT:       Unwind Codes:
+# CHECK-NEXT:         0x01: UOP_Epilog atend=yes, length=0x1
+# CHECK-NEXT:         0x0b: UOP_Epilog offset=0xB
+# CHECK-NEXT:         0x04: UOP_AllocSmall 72
+# CHECK-EMPTY:
+# CHECK-NEXT:   Function Table:
+# CHECK-NEXT:     Start Address: 0x1020
+# CHECK-NEXT:     End Address: 0x105c
+# CHECK-NEXT:     Unwind Info Address: 0x200c
+# CHECK-NEXT:       Version: 1
+# CHECK-NEXT:       Flags: 3 UNW_ExceptionHandler UNW_TerminateHandler
+# CHECK-NEXT:       Size of prolog: 4
+# CHECK-NEXT:       Number of Codes: 1
+# CHECK-NEXT:       No frame pointer used
+# CHECK-NEXT:       Unwind Codes:
+# CHECK-NEXT:         0x04: UOP_AllocSmall 56
+
+--- !COFF
+OptionalHeader:
+  AddressOfEntryPoint: 4128
+  ImageBase:       5368709120
+  SectionAlignment: 4096
+  FileAlignment:   512
+  MajorOperatingSystemVersion: 6
+  MinorOperatingSystemVersion: 0
+  MajorImageVersion: 0
+  MinorImageVersion: 0
+  MajorSubsystemVersion: 6
+  MinorSubsystemVersion: 0
+  Subsystem:       IMAGE_SUBSYSTEM_WINDOWS_CUI
+  DLLCharacteristics: [ IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA, IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE, IMAGE_DLL_CHARACTERISTICS_NX_COMPAT, IMAGE_DLL_CHARACTERISTICS_TERMINAL_SERVER_AWARE ]
+  SizeOfStackReserve: 1048576
+  SizeOfStackCommit: 4096
+  SizeOfHeapReserve: 1048576
+  SizeOfHeapCommit: 4096
+  ExportTable:
+    RelativeVirtualAddress: 0
+    Size:            0
+  ImportTable:
+    RelativeVirtualAddress: 0
+    Size:            0
+  ResourceTable:
+    RelativeVirtualAddress: 0
+    Size:            0
+  ExceptionTable:
+    RelativeVirtualAddress: 12288
+    Size:            24
+  CertificateTable:
+    RelativeVirtualAddress: 0
+    Size:            0
+  BaseRelocationTable:
+    RelativeVirtualAddress: 0
+    Size:            0
+  Debug:
+    RelativeVirtualAddress: 0
+    Size:            0
+  Architecture:
+    RelativeVirtualAddress: 0
+    Size:            0
+  GlobalPtr:
+    RelativeVirtualAddress: 0
+    Size:            0
+  TlsTable:
+    RelativeVirtualAddress: 0
+    Size:            0
+  LoadConfigTable:
+    RelativeVirtualAddress: 0
+    Size:            0
+  BoundImport:
+    RelativeVirtualAddress: 0
+    Size:            0
+  IAT:
+    RelativeVirtualAddress: 0
+    Size:            0
+  DelayImportDescriptor:
+    RelativeVirtualAddress: 0
+    Size:            0
+  ClrRuntimeHeader:
+    RelativeVirtualAddress: 0
+    Size:            0
+header:
+  Machine:         IMAGE_FILE_MACHINE_AMD64
+  Characteristics: [ IMAGE_FILE_EXECUTABLE_IMAGE, IMAGE_FILE_LARGE_ADDRESS_AWARE ]
+sections:
+  - Name:            .text
+    Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
+    VirtualAddress:  4096
+    VirtualSize:     8
+    SectionData:     00000000
+  - Name:            .xdata
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+    VirtualAddress:  8192
+    VirtualSize:     40
+    SectionData:     0204030001160B0604820000190401000462000070100000FFFF010804051E0009330000
+  - Name:            .pdata
+    Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
+    VirtualAddress:  12288
+    VirtualSize:     24
+    SectionData:     101000001710000000200000201000005C1000000C200000
+symbols:
+  - Name:            .text
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            .xdata
+    Value:           0
+    SectionNumber:   2
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            .pdata
+    Value:           0
+    SectionNumber:   3
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+  - Name:            other
+    Value:           0
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            _ZN4RAIID2Ev
+    Value:           16
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            entry
+    Value:           32
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            _ZN4RAIID1Ev
+    Value:           16
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            _Unwind_Resume
+    Value:           96
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            __gxx_personality_seh0
+    Value:           112
+    SectionNumber:   1
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_FUNCTION
+    StorageClass:    IMAGE_SYM_CLASS_EXTERNAL
+  - Name:            GCC_except_table2
+    Value:           20
+    SectionNumber:   2
+    SimpleType:      IMAGE_SYM_TYPE_NULL
+    ComplexType:     IMAGE_SYM_DTYPE_NULL
+    StorageClass:    IMAGE_SYM_CLASS_STATIC
+...

diff  --git a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-image.yaml b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-image.yaml
index 5780cf7a0467b7..b9d6822a77f4a3 100644
--- a/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-image.yaml
+++ b/llvm/test/tools/llvm-readobj/COFF/unwind-x86_64-image.yaml
@@ -1,26 +1,47 @@
 # RUN: yaml2obj %s -o %t.exe
 # RUN: llvm-readobj --unwind %t.exe | FileCheck %s
 
-# CHECK:         RuntimeFunction {
-# CHECK:          StartAddress: entry (0x140001020)
-# CHECK-NEXT:     EndAddress: (0x14000105C)
-# CHECK-NEXT:     UnwindInfoAddress: (0x140002008)
-# CHECK-NEXT:     UnwindInfo {
-# CHECK-NEXT:       Version: 1
-# CHECK-NEXT:       Flags [ (0x3)
-# CHECK-NEXT:         ExceptionHandler (0x1)
-# CHECK-NEXT:         TerminateHandler (0x2)
-# CHECK-NEXT:       ]
-# CHECK-NEXT:       PrologSize: 4
-# CHECK-NEXT:       FrameRegister: -
-# CHECK-NEXT:       FrameOffset: -
-# CHECK-NEXT:       UnwindCodeCount: 1
-# CHECK-NEXT:       UnwindCodes [
-# CHECK-NEXT:         0x04: ALLOC_SMALL size=56
-# CHECK-NEXT:       ]
-# CHECK-NEXT:       Handler: __gxx_personality_seh0 (0x140001070)
+# CHECK-LABEL:  UnwindInformation [
+# CHECK-NEXT:     RuntimeFunction {
+# CHECK-NEXT:       StartAddress: _ZN4RAIID2Ev (0x140001010)
+# CHECK-NEXT:       EndAddress: (0x140001017)
+# CHECK-NEXT:       UnwindInfoAddress: .xdata (0x140002000)
+# CHECK-NEXT:       UnwindInfo {
+# CHECK-NEXT:         Version: 2
+# CHECK-NEXT:         Flags [ (0x0)
+# CHECK-NEXT:         ]
+# CHECK-NEXT:         PrologSize: 4
+# CHECK-NEXT:         FrameRegister: -
+# CHECK-NEXT:         FrameOffset: -
+# CHECK-NEXT:         UnwindCodeCount: 3
+# CHECK-NEXT:         UnwindCodes [
+# CHECK-NEXT:           0x01: EPILOG atend=yes, length=0x1
+# CHECK-NEXT:           0x0B: EPILOG offset=0xB
+# CHECK-NEXT:           0x04: ALLOC_SMALL size=72
+# CHECK-NEXT:         ]
+# CHECK-NEXT:       }
 # CHECK-NEXT:     }
-# CHECK-NEXT:   }
+# CHECK-NEXT:     RuntimeFunction {
+# CHECK-NEXT:       StartAddress: entry (0x140001020)
+# CHECK-NEXT:       EndAddress: (0x14000105C)
+# CHECK-NEXT:       UnwindInfoAddress: (0x14000200C)
+# CHECK-NEXT:       UnwindInfo {
+# CHECK-NEXT:         Version: 1
+# CHECK-NEXT:         Flags [ (0x3)
+# CHECK-NEXT:           ExceptionHandler (0x1)
+# CHECK-NEXT:           TerminateHandler (0x2)
+# CHECK-NEXT:         ]
+# CHECK-NEXT:         PrologSize: 4
+# CHECK-NEXT:         FrameRegister: -
+# CHECK-NEXT:         FrameOffset: -
+# CHECK-NEXT:         UnwindCodeCount: 1
+# CHECK-NEXT:         UnwindCodes [
+# CHECK-NEXT:           0x04: ALLOC_SMALL size=56
+# CHECK-NEXT:         ]
+# CHECK-NEXT:         Handler: __gxx_personality_seh0 (0x140001070)
+# CHECK-NEXT:       }
+# CHECK-NEXT:     }
+# CHECK-NEXT:   ]
 
 --- !COFF
 OptionalHeader:
@@ -92,18 +113,18 @@ sections:
   - Name:            .text
     Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ]
     VirtualAddress:  4096
-    VirtualSize:     113
-    SectionData:     C3662E0F1F8400000000000F1F4400005048890C2458C3660F1F8400000000004883EC38E8D7FFFFFFE900000000488D4C2430E8D8FFFFFF904883C438C3488944242889542424488D4C2430E8BFFFFFFF488B4C2428E805000000CC0F1F4000C3662E0F1F8400000000000F1F440000C3
-  - Name:            .rdata
+    VirtualSize:     8
+    SectionData:     00000000
+  - Name:            .xdata
     Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
     VirtualAddress:  8192
-    VirtualSize:     32
-    SectionData:     0101010001020000190401000462000070100000FFFF010804051E0009330000
+    VirtualSize:     40
+    SectionData:     0204030001160B0604820000190401000462000070100000FFFF010804051E0009330000
   - Name:            .pdata
     Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ ]
     VirtualAddress:  12288
     VirtualSize:     24
-    SectionData:     101000001710000000200000201000005C10000008200000
+    SectionData:     101000001710000000200000201000005C1000000C200000
 symbols:
   - Name:            .text
     Value:           0

diff  --git a/llvm/tools/llvm-objdump/COFFDump.cpp b/llvm/tools/llvm-objdump/COFFDump.cpp
index 71697fa01e627d..b22c9a4127fc3a 100644
--- a/llvm/tools/llvm-objdump/COFFDump.cpp
+++ b/llvm/tools/llvm-objdump/COFFDump.cpp
@@ -240,10 +240,10 @@ static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) {
   case UOP_AllocSmall:
   case UOP_SetFPReg:
   case UOP_PushMachFrame:
+  case UOP_Epilog:
     return 1;
   case UOP_SaveNonVol:
   case UOP_SaveXMM128:
-  case UOP_Epilog:
     return 2;
   case UOP_SaveNonVolBig:
   case UOP_SaveXMM128Big:
@@ -257,7 +257,7 @@ static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) {
 // Prints one unwind code. Because an unwind code can occupy up to 3 slots in
 // the unwind codes array, this function requires that the correct number of
 // slots is provided.
-static void printUnwindCode(ArrayRef<UnwindCode> UCs) {
+static void printUnwindCode(ArrayRef<UnwindCode> UCs, bool &SeenFirstEpilog) {
   assert(UCs.size() >= getNumUsedSlots(UCs[0]));
   outs() <<  format("      0x%02x: ", unsigned(UCs[0].u.CodeOffset))
          << getUnwindCodeTypeName(UCs[0].getUnwindOp());
@@ -301,11 +301,29 @@ static void printUnwindCode(ArrayRef<UnwindCode> UCs) {
     outs() << " " << (UCs[0].getOpInfo() ? "w/o" : "w")
            << " error code";
     break;
+
+  case UOP_Epilog:
+    if (SeenFirstEpilog) {
+      uint32_t Offset = UCs[0].getEpilogOffset();
+      if (Offset == 0) {
+        outs() << " padding";
+      } else {
+        outs() << " offset=" << format("0x%X", Offset);
+      }
+    } else {
+      SeenFirstEpilog = true;
+      bool AtEnd = (UCs[0].getOpInfo() & 0x1) != 0;
+      uint32_t Length = UCs[0].u.CodeOffset;
+      outs() << " atend=" << (AtEnd ? "yes" : "no")
+             << ", length=" << format("0x%X", Length);
+    }
+    break;
   }
   outs() << "\n";
 }
 
 static void printAllUnwindCodes(ArrayRef<UnwindCode> UCs) {
+  bool SeenFirstEpilog = false;
   for (const UnwindCode *I = UCs.begin(), *E = UCs.end(); I < E; ) {
     unsigned UsedSlots = getNumUsedSlots(*I);
     if (UsedSlots > UCs.size()) {
@@ -316,7 +334,7 @@ static void printAllUnwindCodes(ArrayRef<UnwindCode> UCs) {
              << " remaining in buffer";
       return ;
     }
-    printUnwindCode(ArrayRef(I, E));
+    printUnwindCode(ArrayRef(I, E), SeenFirstEpilog);
     I += UsedSlots;
   }
 }

diff  --git a/llvm/tools/llvm-readobj/Win64EHDumper.cpp b/llvm/tools/llvm-readobj/Win64EHDumper.cpp
index e4bd772191514a..e17a035f1d371d 100644
--- a/llvm/tools/llvm-readobj/Win64EHDumper.cpp
+++ b/llvm/tools/llvm-readobj/Win64EHDumper.cpp
@@ -65,6 +65,8 @@ static StringRef getUnwindCodeTypeName(uint8_t Code) {
   case UOP_SaveXMM128: return "SAVE_XMM128";
   case UOP_SaveXMM128Big: return "SAVE_XMM128_FAR";
   case UOP_PushMachFrame: return "PUSH_MACHFRAME";
+  case UOP_Epilog:
+    return "EPILOG";
   }
 }
 
@@ -99,6 +101,7 @@ static unsigned getNumUsedSlots(const UnwindCode &UnwindCode) {
   case UOP_AllocSmall:
   case UOP_SetFPReg:
   case UOP_PushMachFrame:
+  case UOP_Epilog:
     return 1;
   case UOP_SaveNonVol:
   case UOP_SaveXMM128:
@@ -254,7 +257,8 @@ void Dumper::printRuntimeFunctionEntry(const Context &Ctx,
 // Prints one unwind code. Because an unwind code can occupy up to 3 slots in
 // the unwind codes array, this function requires that the correct number of
 // slots is provided.
-void Dumper::printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC) {
+void Dumper::printUnwindCode(const UnwindInfo &UI, ArrayRef<UnwindCode> UC,
+                             bool &SeenFirstEpilog) {
   assert(UC.size() >= getNumUsedSlots(UC[0]));
 
   SW.startLine() << format("0x%02X: ", unsigned(UC[0].u.CodeOffset))
@@ -306,6 +310,23 @@ void Dumper::printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC) {
   case UOP_PushMachFrame:
     OS << " errcode=" << (UC[0].getOpInfo() == 0 ? "no" : "yes");
     break;
+
+  case UOP_Epilog:
+    if (SeenFirstEpilog) {
+      uint32_t Offset = UC[0].getEpilogOffset();
+      if (Offset == 0) {
+        OS << " padding";
+      } else {
+        OS << " offset=" << format("0x%X", Offset);
+      }
+    } else {
+      SeenFirstEpilog = true;
+      bool AtEnd = (UC[0].getOpInfo() & 0x1) != 0;
+      uint32_t Length = UC[0].u.CodeOffset;
+      OS << " atend=" << (AtEnd ? "yes" : "no")
+         << ", length=" << format("0x%X", Length);
+    }
+    break;
   }
 
   OS << "\n";
@@ -330,6 +351,7 @@ void Dumper::printUnwindInfo(const Context &Ctx, const coff_section *Section,
   {
     ListScope UCS(SW, "UnwindCodes");
     ArrayRef<UnwindCode> UC(&UI.UnwindCodes[0], UI.NumCodes);
+    bool SeenFirstEpilog = false;
     for (const UnwindCode *UCI = UC.begin(), *UCE = UC.end(); UCI < UCE; ++UCI) {
       unsigned UsedSlots = getNumUsedSlots(*UCI);
       if (UsedSlots > UC.size()) {
@@ -337,7 +359,7 @@ void Dumper::printUnwindInfo(const Context &Ctx, const coff_section *Section,
         return;
       }
 
-      printUnwindCode(UI, ArrayRef(UCI, UCE));
+      printUnwindCode(UI, ArrayRef(UCI, UCE), SeenFirstEpilog);
       UCI = UCI + UsedSlots - 1;
     }
   }

diff  --git a/llvm/tools/llvm-readobj/Win64EHDumper.h b/llvm/tools/llvm-readobj/Win64EHDumper.h
index 97458c916bec6f..a23d30be7a113d 100644
--- a/llvm/tools/llvm-readobj/Win64EHDumper.h
+++ b/llvm/tools/llvm-readobj/Win64EHDumper.h
@@ -44,7 +44,8 @@ class Dumper {
                                  const object::coff_section *Section,
                                  uint64_t SectionOffset,
                                  const RuntimeFunction &RF);
-  void printUnwindCode(const UnwindInfo& UI, ArrayRef<UnwindCode> UC);
+  void printUnwindCode(const UnwindInfo &UI, ArrayRef<UnwindCode> UC,
+                       bool &SeenFirstEpilog);
   void printUnwindInfo(const Context &Ctx, const object::coff_section *Section,
                        off_t Offset, const UnwindInfo &UI);
   void printRuntimeFunction(const Context &Ctx,


        


More information about the llvm-commits mailing list