[llvm] 92f1028 - [llvm-readobj] Fix printing of Windows ARM unwind opcodes, add tests

Martin Storsjö via llvm-commits llvm-commits at lists.llvm.org
Wed May 18 00:18:46 PDT 2022


Author: Martin Storsjö
Date: 2022-05-18T10:14:33+03:00
New Revision: 92f1028ceb30dc8e7eda3f06a8c7aa8e8082ff65

URL: https://github.com/llvm/llvm-project/commit/92f1028ceb30dc8e7eda3f06a8c7aa8e8082ff65
DIFF: https://github.com/llvm/llvm-project/commit/92f1028ceb30dc8e7eda3f06a8c7aa8e8082ff65.diff

LOG: [llvm-readobj] Fix printing of Windows ARM unwind opcodes, add tests

The existing code was essentially untested; in some cases, it used
too narrow variable types to fit all the bits, in some cases the
bit manipulation operations were incorrect.

For the "ldr lr, [sp], #x" opcode, there's nothing in the documentation
that says it cannot be used in a prologue. (In practice, it would
probably seldom be used there, but technically there's nothing
stopping it from being used.) The documentation only specifies the
operation to replay for unwinding it, but the corresponding mirror
instruction to be printed for a prologue is "str lr, [sp, #-x]!".

Also improve printing of register masks, by aggregating registers
into ranges where possible, and make the printing of the terminating
branches clearer, as "bx <reg>" and "b.w <target>".

Differential Revision: https://reviews.llvm.org/D125643

Added: 
    llvm/test/tools/llvm-readobj/COFF/arm-unwind-opcodes.s

Modified: 
    llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/test/tools/llvm-readobj/COFF/arm-unwind-opcodes.s b/llvm/test/tools/llvm-readobj/COFF/arm-unwind-opcodes.s
new file mode 100644
index 0000000000000..98faf1b6b3803
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/arm-unwind-opcodes.s
@@ -0,0 +1,239 @@
+// REQUIRES: arm-registered-target
+// RUN: llvm-mc -filetype=obj -triple thumbv7-windows-gnu %s -o %t.o
+// RUN: llvm-readobj --unwind %t.o | FileCheck --strict-whitespace %s
+
+// CHECK:       RuntimeFunction {
+// CHECK-NEXT:    Function: func0
+// CHECK:           Prologue [
+// CHECK-NEXT:        0xcb                ; mov r11, sp
+// CHECK-NEXT:        0x95 0x00           ; push.w {r8, r10, r12}
+// CHECK-NEXT:        0xf6 0x13           ; vpush {d17-d19}
+// CHECK-NEXT:        0xfc                ; nop.w
+// CHECK-NEXT:        0xf5 0x35           ; vpush {d3-d5}
+// CHECK-NEXT:        0xfb                ; nop
+// CHECK-NEXT:        0xe2                ; vpush {d8-d10}
+// CHECK-NEXT:        0x08                ; sub sp, #(8 * 4)
+// CHECK-NEXT:        0xd6                ; push {r4-r6, lr}
+// CHECK-NEXT:      ]
+// CHECK-NEXT:      EpilogueScopes [
+// CHECK-NEXT:        EpilogueScope {
+// CHECK-NEXT:          StartOffset: 15
+// CHECK-NEXT:          Condition: 14
+// CHECK-NEXT:          EpilogueStartIndex: 13
+// CHECK-NEXT:          Opcodes [
+// CHECK-NEXT:            0xe2                ; vpop {d8-d10}
+// CHECK-NEXT:            0xcb                ; mov sp, r11
+// CHECK-NEXT:            0x08                ; add sp, #(8 * 4)
+// CHECK-NEXT:            0xd6                ; pop {r4-r6, pc}
+// CHECK-NEXT:          ]
+// CHECK-NEXT:        }
+// CHECK-NEXT:      ]
+// CHECK:       RuntimeFunction {
+// CHECK-NEXT:    Function: func1
+// CHECK:           Prologue [
+// CHECK-NEXT:        0xef 0x08           ; str.w lr, [sp, #-32]!
+// CHECK-NEXT:        0xd1                ; push {r4-r5}
+// CHECK-NEXT:        0xfd                ; bx <reg>
+// CHECK-NEXT:      ]
+// CHECK-NEXT:      EpilogueScopes [
+// CHECK-NEXT:        EpilogueScope {
+// CHECK-NEXT:          StartOffset: 4
+// CHECK-NEXT:          Condition: 14
+// CHECK-NEXT:          EpilogueStartIndex: 4
+// CHECK-NEXT:          Opcodes [
+// CHECK-NEXT:            0xef 0x08           ; ldr.w lr, [sp], #32
+// CHECK-NEXT:            0xd1                ; pop {r4-r5}
+// CHECK-NEXT:            0xfd                ; bx <reg>
+// CHECK-NEXT:          ]
+// CHECK-NEXT:        }
+// CHECK-NEXT:      ]
+// CHECK:       RuntimeFunction {
+// CHECK-NEXT:    Function: func2
+// CHECK-NEXT:    ExceptionRecord:
+// CHECK-NEXT:    ExceptionData {
+// CHECK-NEXT:      FunctionLength:
+// CHECK-NEXT:      Version:
+// CHECK-NEXT:      ExceptionData: No
+// CHECK-NEXT:      EpiloguePacked: Yes
+// CHECK-NEXT:      Fragment: No
+// CHECK-NEXT:      EpilogueOffset: 0
+// CHECK-NEXT:      ByteCodeLength:
+// CHECK-NEXT:      Prologue [
+// CHECK-NEXT:        0x04                ; sub sp, #(4 * 4)
+// CHECK-NEXT:        0xec 0x80           ; push {r7}
+// CHECK-NEXT:        0xc7                ; mov r7, sp
+// CHECK-NEXT:        0xfe                ; b.w <target>
+// CHECK-NEXT:      ]
+// CHECK-NEXT:    }
+// CHECK-NEXT:  }
+// CHECK-NEXT:  RuntimeFunction {
+// CHECK-NEXT:    Function: func3
+// CHECK-NEXT:    ExceptionRecord:
+// CHECK-NEXT:    ExceptionData {
+// CHECK-NEXT:      FunctionLength:
+// CHECK-NEXT:      Version:
+// CHECK-NEXT:      ExceptionData: No
+// CHECK-NEXT:      EpiloguePacked: Yes
+// CHECK-NEXT:      Fragment: Yes
+// CHECK-NEXT:      EpilogueOffset: 1
+// CHECK-NEXT:      ByteCodeLength:
+// CHECK-NEXT:      Prologue [
+// CHECK-NEXT:        0x04                ; sub sp, #(4 * 4)
+// CHECK-NEXT:        0xdf                ; push.w {r4-r11, lr}
+// CHECK-NEXT:      ]
+// CHECK-NEXT:      Epilogue [
+// CHECK-NEXT:        0xdf                ; pop.w {r4-r11, pc}
+// CHECK-NEXT:      ]
+// CHECK-NEXT:    }
+// CHECK-NEXT:  }
+// CHECK-NEXT:  RuntimeFunction {
+// CHECK-NEXT:    Function: func4
+// CHECK-NEXT:    ExceptionRecord:
+// CHECK-NEXT:    ExceptionData {
+// CHECK-NEXT:      FunctionLength:
+// CHECK-NEXT:      Version:
+// CHECK-NEXT:      ExceptionData: No
+// CHECK-NEXT:      EpiloguePacked: Yes
+// CHECK-NEXT:      Fragment: No
+// CHECK-NEXT:      EpilogueOffset: 0
+// CHECK-NEXT:      ByteCodeLength:
+// CHECK-NEXT:      Prologue [
+// CHECK-NEXT:        0xec 0x50           ; push {r4, r6}
+// CHECK-NEXT:        0xb5 0x00           ; push.w {r8, r10, r12, lr}
+// CHECK-NEXT:      ]
+// CHECK-NEXT:    }
+// CHECK-NEXT:  }
+// CHECK-NEXT:  RuntimeFunction {
+// CHECK-NEXT:    Function: func5
+// CHECK-NEXT:    ExceptionRecord:
+// CHECK-NEXT:    ExceptionData {
+// CHECK-NEXT:      FunctionLength:
+// CHECK-NEXT:      Version:
+// CHECK-NEXT:      ExceptionData: No
+// CHECK-NEXT:      EpiloguePacked: Yes
+// CHECK-NEXT:      Fragment: No
+// CHECK-NEXT:      EpilogueOffset: 16
+// CHECK-NEXT:      ByteCodeLength:
+// CHECK-NEXT:      Prologue [
+// CHECK-NEXT:        0xfa 0x00 0x00 0x20 ; sub.w sp, sp, #(32 * 4)
+// CHECK-NEXT:        0xf9 0x00 0x10      ; sub.w sp, sp, #(16 * 4)
+// CHECK-NEXT:        0xf8 0x00 0x00 0x08 ; sub sp, sp, #(8 * 4)
+// CHECK-NEXT:        0xf7 0x00 0x04      ; sub sp, sp, #(4 * 4)
+// CHECK-NEXT:        0xe8 0x02           ; sub.w sp, #(2 * 4)
+// CHECK-NEXT:        0xed 0x50           ; push {r4, r6, lr}
+// CHECK-NEXT:      ]
+// CHECK-NEXT:      Epilogue [
+// CHECK-NEXT:        0xed 0x50           ; pop {r4, r6, pc}
+// CHECK-NEXT:      ]
+// CHECK-NEXT:    }
+// CHECK-NEXT:  }
+
+        .thumb
+        .syntax unified
+func0:
+        push {r4-r6, lr}
+        sub sp, sp, #32
+        vpush {d8-d10}
+        nop
+        vpush {d3-d5}
+        nop.w
+        vpush {d17-d19}
+        push {r8, r10, r12}
+        mov r11, sp
+        nop
+        vpop {d8-d10}
+        mov sp, r11
+        add sp, sp, #32
+        pop {r4-r6, pc}
+
+func1:
+        push {r4-r5}
+        str lr, [sp, #-32]!
+        nop
+        ldr lr, [sp], #32
+        pop {r4-r5}
+        bx lr
+
+func2:
+        mov r7, sp
+        push {r7}
+        sub sp, sp, #16
+        nop
+        add sp, sp, #16
+        pop {r7}
+        mov sp, r7
+        b tailcall
+
+func3:
+        nop.w
+        nop
+        nop
+        add sp, sp, #16
+        pop {r4-r11, pc}
+
+func4:
+        push {r8, r10, r12, lr}
+        push {r4, r6}
+        nop
+        pop {r4, r6}
+        pop {r8, r10, r12, pc}
+
+func5:
+        push {r4, r6, lr}
+        subw sp, sp, #8
+        sub sp, sp, #16
+        sub sp, sp, #32
+        subw sp, sp, #64
+        subw sp, sp, #128
+        nop
+        pop {r4, r6, pc}
+
+        .section .pdata,"dr"
+        .rva func0
+        .rva .Lunwind_func0
+        .rva func1
+        .rva .Lunwind_func1
+        .rva func2
+        .rva .Lunwind_func2
+        .rva func3
+        .rva .Lunwind_func3
+        .rva func4
+        .rva .Lunwind_func4
+        .rva func5
+        .rva .Lunwind_func5
+
+        .section .xdata,"dr"
+.Lunwind_func0:
+.byte 0x14, 0x00, 0x80, 0x50
+.byte 0x0f, 0x00, 0xe0, 0x0d
+.byte 0xcb, 0x95, 0x00, 0xf6
+.byte 0x13, 0xfc, 0xf5, 0x35
+.byte 0xfb, 0xe2, 0x08, 0xd6
+.byte 0xff, 0xe2, 0xcb, 0x08
+.byte 0xd6, 0xff, 0x00, 0x00
+
+.Lunwind_func1:
+.byte 0x08, 0x00, 0x00, 0x00
+.byte 0x01, 0x00, 0x02, 0x00
+.byte 0x04, 0x00, 0xe0, 0x04
+.byte 0xef, 0x08, 0xd1, 0xfd
+.byte 0xef, 0x08, 0xd1, 0xfd
+
+.Lunwind_func2:
+.byte 0x09, 0x00, 0x20, 0x20
+.byte 0x04, 0xec, 0x80, 0xc7
+.byte 0xfe, 0x00, 0x00, 0x00
+.Lunwind_func3:
+.byte 0x07, 0x00, 0xe0, 0x10
+.byte 0x04, 0xdf, 0xff, 0x00
+.Lunwind_func4:
+.byte 0x07, 0x00, 0x20, 0x20
+.byte 0xec, 0x50, 0xb5, 0x00
+.byte 0xff, 0x00, 0x00, 0x00
+.Lunwind_func5:
+.byte 0x0b, 0x00, 0x20, 0x58
+.byte 0xfa, 0x00, 0x00, 0x20
+.byte 0xf9, 0x00, 0x10, 0xf8
+.byte 0x00, 0x00, 0x08, 0xf7
+.byte 0x00, 0x04, 0xe8, 0x02
+.byte 0xed, 0x50, 0xff, 0x00

diff  --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
index 294323a916aae..201cdced8667b 100644
--- a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
+++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -78,10 +78,10 @@ raw_ostream &operator<<(raw_ostream &OS, const ARM::WinEH::ReturnType &RT) {
     OS << "pop {pc}";
     break;
   case ARM::WinEH::ReturnType::RT_B:
-    OS << "b target";
+    OS << "bx <reg>";
     break;
   case ARM::WinEH::ReturnType::RT_BW:
-    OS << "b.w target";
+    OS << "b.w <target>";
     break;
   case ARM::WinEH::ReturnType::RT_NoEpilogue:
     OS << "(no epilogue)";
@@ -174,26 +174,45 @@ const Decoder::RingEntry Decoder::Ring64[] = {
   { 0xff, 0xec, 1, &Decoder::opcode_clear_unwound_to_call },
 };
 
-void Decoder::printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask) {
-  static const char * const GPRRegisterNames[16] = {
-    "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
-    "r11", "ip", "sp", "lr", "pc",
-  };
+static void printRange(raw_ostream &OS, ListSeparator &LS, unsigned First,
+                       unsigned Last, char Letter) {
+  if (First == Last)
+    OS << LS << Letter << First;
+  else
+    OS << LS << Letter << First << "-" << Letter << Last;
+}
+
+static void printRange(raw_ostream &OS, uint32_t Mask, ListSeparator &LS,
+                       unsigned Start, unsigned End, char Letter) {
+  int First = -1;
+  for (unsigned RI = Start; RI <= End; ++RI) {
+    if (Mask & (1 << RI)) {
+      if (First < 0)
+        First = RI;
+    } else {
+      if (First >= 0) {
+        printRange(OS, LS, First, RI - 1, Letter);
+        First = -1;
+      }
+    }
+  }
+  if (First >= 0)
+    printRange(OS, LS, First, End, Letter);
+}
 
+void Decoder::printRegisters(
+    const std::pair<uint16_t, uint32_t> &RegisterMask) {
   const uint16_t GPRMask = std::get<0>(RegisterMask);
-  const uint16_t VFPMask = std::get<1>(RegisterMask);
+  const uint32_t VFPMask = std::get<1>(RegisterMask);
 
   OS << '{';
   ListSeparator LS;
-  for (unsigned RI = 0, RE = 11; RI < RE; ++RI)
-    if (GPRMask & (1 << RI))
-      OS << LS << GPRRegisterNames[RI];
-  for (unsigned RI = 0, RE = 32; RI < RE; ++RI)
-    if (VFPMask & (1 << RI))
-      OS << LS << "d" << unsigned(RI);
-  for (unsigned RI = 11, RE = 16; RI < RE; ++RI)
-    if (GPRMask & (1 << RI))
-      OS << LS << GPRRegisterNames[RI];
+  printRange(OS, GPRMask, LS, 0, 12, 'r');
+  printRange(OS, VFPMask, LS, 0, 31, 'd');
+  if (GPRMask & (1 << 14))
+    OS << LS << "lr";
+  if (GPRMask & (1 << 15))
+    OS << LS << "pc";
   OS << '}';
 }
 
@@ -346,7 +365,7 @@ bool Decoder::opcode_1100xxxx(const uint8_t *OC, unsigned &Offset,
 
 bool Decoder::opcode_11010Lxx(const uint8_t *OC, unsigned &Offset,
                               unsigned Length, bool Prologue) {
-  unsigned Link = (OC[Offset] & 0x4) >> 3;
+  unsigned Link = (OC[Offset] & 0x4) >> 2;
   unsigned Count = (OC[Offset] & 0x3);
 
   uint16_t GPRMask = (Link << (Prologue ? 14 : 15))
@@ -407,8 +426,8 @@ bool Decoder::opcode_111010xx(const uint8_t *OC, unsigned &Offset,
 
 bool Decoder::opcode_1110110L(const uint8_t *OC, unsigned &Offset,
                               unsigned Length, bool Prologue) {
-  uint8_t GPRMask = ((OC[Offset + 0] & 0x01) << (Prologue ? 14 : 15))
-                  | ((OC[Offset + 1] & 0xff) << 0);
+  uint16_t GPRMask = ((OC[Offset + 0] & 0x01) << (Prologue ? 14 : 15))
+                   | ((OC[Offset + 1] & 0xff) << 0);
 
   SW.startLine() << format("0x%02x 0x%02x           ; %s ", OC[Offset + 0],
                            OC[Offset + 1], Prologue ? "push" : "pop");
@@ -437,11 +456,13 @@ bool Decoder::opcode_11101110(const uint8_t *OC, unsigned &Offset,
 
 bool Decoder::opcode_11101111(const uint8_t *OC, unsigned &Offset,
                               unsigned Length, bool Prologue) {
-  assert(!Prologue && "may not be used in prologue");
-
   if (OC[Offset + 1] & 0xf0)
     SW.startLine() << format("0x%02x 0x%02x           ; reserved\n",
                              OC[Offset + 0], OC[Offset +  1]);
+  else if (Prologue)
+    SW.startLine()
+      << format("0x%02x 0x%02x           ; str.w lr, [sp, #-%u]!\n",
+                OC[Offset + 0], OC[Offset + 1], OC[Offset + 1] << 2);
   else
     SW.startLine()
       << format("0x%02x 0x%02x           ; ldr.w lr, [sp], #%u\n",
@@ -455,7 +476,7 @@ bool Decoder::opcode_11110101(const uint8_t *OC, unsigned &Offset,
                               unsigned Length, bool Prologue) {
   unsigned Start = (OC[Offset + 1] & 0xf0) >> 4;
   unsigned End = (OC[Offset + 1] & 0x0f) >> 0;
-  uint32_t VFPMask = ((1 << (End - Start)) - 1) << Start;
+  uint32_t VFPMask = ((1 << (End + 1 - Start)) - 1) << Start;
 
   SW.startLine() << format("0x%02x 0x%02x           ; %s ", OC[Offset + 0],
                            OC[Offset + 1], Prologue ? "vpush" : "vpop");
@@ -470,7 +491,7 @@ bool Decoder::opcode_11110110(const uint8_t *OC, unsigned &Offset,
                               unsigned Length, bool Prologue) {
   unsigned Start = (OC[Offset + 1] & 0xf0) >> 4;
   unsigned End = (OC[Offset + 1] & 0x0f) >> 0;
-  uint32_t VFPMask = ((1 << (End - Start)) - 1) << 16;
+  uint32_t VFPMask = ((1 << (End + 1 - Start)) - 1) << (16 + Start);
 
   SW.startLine() << format("0x%02x 0x%02x           ; %s ", OC[Offset + 0],
                            OC[Offset + 1], Prologue ? "vpush" : "vpop");
@@ -553,14 +574,14 @@ bool Decoder::opcode_11111100(const uint8_t *OC, unsigned &Offset,
 
 bool Decoder::opcode_11111101(const uint8_t *OC, unsigned &Offset,
                               unsigned Length, bool Prologue) {
-  SW.startLine() << format("0x%02x                ; b\n", OC[Offset]);
+  SW.startLine() << format("0x%02x                ; bx <reg>\n", OC[Offset]);
   ++Offset;
   return true;
 }
 
 bool Decoder::opcode_11111110(const uint8_t *OC, unsigned &Offset,
                               unsigned Length, bool Prologue) {
-  SW.startLine() << format("0x%02x                ; b.w\n", OC[Offset]);
+  SW.startLine() << format("0x%02x                ; b.w <target>\n", OC[Offset]);
   ++Offset;
   return true;
 }
@@ -948,7 +969,7 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF,
 
   if (XData.E()) {
     ArrayRef<uint8_t> UC = XData.UnwindByteCode();
-    if (isAArch64 || !XData.F()) {
+    {
       ListScope PS(SW, "Prologue");
       decodeOpcodes(UC, 0, /*Prologue=*/true);
     }
@@ -971,8 +992,9 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF,
       SW.printNumber("EpilogueStartIndex",
                      isAArch64 ? ES.EpilogueStartIndexAArch64()
                                : ES.EpilogueStartIndexARM());
-      if (ES.ES & ~0xffc3ffff)
-        SW.printNumber("ReservedBits", (ES.ES >> 18) & 0xF);
+      unsigned ReservedMask = isAArch64 ? 0xF : 0x3;
+      if ((ES.ES >> 18) & ReservedMask)
+        SW.printNumber("ReservedBits", (ES.ES >> 18) & ReservedMask);
 
       ListScope Opcodes(SW, "Opcodes");
       decodeOpcodes(XData.UnwindByteCode(),


        


More information about the llvm-commits mailing list