[llvm] 0b61db4 - [AArch64][Windows] Add llvm-readobj support for save_any_reg unwind opcode.
Eli Friedman via llvm-commits
llvm-commits at lists.llvm.org
Tue Oct 4 18:55:58 PDT 2022
Author: Eli Friedman
Date: 2022-10-04T18:55:01-07:00
New Revision: 0b61db423bfec26f110c27b7282c3f51aa0f3006
URL: https://github.com/llvm/llvm-project/commit/0b61db423bfec26f110c27b7282c3f51aa0f3006
DIFF: https://github.com/llvm/llvm-project/commit/0b61db423bfec26f110c27b7282c3f51aa0f3006.diff
LOG: [AArch64][Windows] Add llvm-readobj support for save_any_reg unwind opcode.
This is primarily used for Arm64EC, but it could be used for other
non-standard calling conventions. The testcase is based on an Arm64EC
thunk generated by MSVC.
The name save_any_reg comes from Microsoft documentation, but the full
encoding isn't specified there; this is reverse-engineered from the
behavior of the unwinder. (Thanks to Martin Storsjö for his example of
how to write simple unwinder testcases by directly calling
RtlVirtualUnwind.)
Differential Revision: https://reviews.llvm.org/D135196
Added:
llvm/test/tools/llvm-readobj/COFF/arm64-unwind-save_any_reg.s
Modified:
llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
llvm/tools/llvm-readobj/ARMWinEHPrinter.h
Removed:
################################################################################
diff --git a/llvm/test/tools/llvm-readobj/COFF/arm64-unwind-save_any_reg.s b/llvm/test/tools/llvm-readobj/COFF/arm64-unwind-save_any_reg.s
new file mode 100644
index 0000000000000..bcf35f05809ec
--- /dev/null
+++ b/llvm/test/tools/llvm-readobj/COFF/arm64-unwind-save_any_reg.s
@@ -0,0 +1,98 @@
+// REQUIRES: aarch64-registered-target
+// RUN: llvm-mc -filetype=obj -triple aarch64-windows %s -o %t.o
+// RUN: llvm-readobj --unwind %t.o | FileCheck --strict-whitespace %s
+
+//CHECK: Prologue [
+//CHECK-NEXT: 0xe1 ; mov fp, sp
+//CHECK-NEXT: 0x83 ; stp x29, x30, [sp, #-32]!
+//CHECK-NEXT: 0xe6 ; save next
+//CHECK-NEXT: 0xe6 ; save next
+//CHECK-NEXT: 0xe6 ; save next
+//CHECK-NEXT: 0xe6 ; save next
+//CHECK-NEXT: 0xe76689 ; stp q6, q7, [sp, #-160]!
+//CHECK-NEXT: 0xe4 ; end
+//CHECK-NEXT: ]
+//CHECK-NEXT: EpilogueScopes [
+//CHECK-NEXT: EpilogueScope {
+//CHECK-NEXT: StartOffset: 12
+//CHECK-NEXT: EpilogueStartIndex: 10
+//CHECK-NEXT: Opcodes [
+//CHECK-NEXT: 0x83 ; ldp x29, x30, [sp], #32
+//CHECK-NEXT: 0xe74e88 ; ldp q14, q15, [sp, #128]
+//CHECK-NEXT: 0xe74c86 ; ldp q12, q13, [sp, #96]
+//CHECK-NEXT: 0xe74a84 ; ldp q10, q11, [sp, #64]
+//CHECK-NEXT: 0xe74882 ; ldp q8, q9, [sp, #32]
+//CHECK-NEXT: 0xe76689 ; ldp q6, q7, [sp], #160
+//CHECK-NEXT: 0xe3 ; nop
+//CHECK-NEXT: 0xe3 ; nop
+//CHECK-NEXT: 0xe4 ; end
+//CHECK-NEXT: ]
+//CHECK-NEXT: }
+//CHECK-NEXT: ]
+
+//CHECK: Prologue [
+//CHECK-NEXT: 0xe70001 ; str x0, [sp, #8]
+//CHECK-NEXT: 0xe70041 ; str d0, [sp, #8]
+//CHECK-NEXT: 0xe70081 ; str q0, [sp, #16]
+//CHECK-NEXT: 0xe72001 ; str x0, [sp, #-32]!
+//CHECK-NEXT: 0xe77d01 ; stp x29, x30, [sp, #-32]!
+//CHECK-NEXT: 0xe4 ; end
+//CHECK-NEXT: ]
+//CHECK-NEXT: EpilogueScopes [
+//CHECK-NEXT: ]
+
+.section .pdata,"dr"
+ .long func at IMGREL
+ .long "$unwind$func"@IMGREL
+ .long func2 at IMGREL
+ .long "$unwind$func2"@IMGREL
+
+ .text
+ .globl func
+func:
+ stp q6, q7, [sp, #-160]!
+ stp q8, q9, [sp, #32]
+ stp q10, q11, [sp, #64]
+ stp q12, q13, [sp, #96]
+ stp q14, q15, [sp, #128]
+ stp x29, x30, [sp, #-32]!
+ mov x29, sp
+ str x0, [sp, #16]
+ str x9, [sp, #24]
+ ldr x0, [sp, #16]
+ ldr x8, [sp, #24]
+ blr x8
+ ldp x29, x30, [sp], #32
+ ldp q14, q15, [sp, #128]
+ ldp q12, q13, [sp, #96]
+ ldp q10, q11, [sp, #64]
+ ldp q8, q9, [sp, #32]
+ ldp q6, q7, [sp], #160
+ nop
+ ldr x16, [x16]
+ br x16
+
+func2:
+ ret
+
+.section .xdata,"dr"
+"$unwind$func":
+.byte 0x15, 0x00, 0x40, 0x40
+.byte 0x0c, 0x00, 0x80, 0x02
+.byte 0xe1, 0x83, 0xe6, 0xe6
+.byte 0xe6, 0xe6, 0xe7, 0x66
+.byte 0x89, 0xe4, 0x83, 0xe7
+.byte 0x4e, 0x88, 0xe7, 0x4c
+.byte 0x86, 0xe7, 0x4a, 0x84
+.byte 0xe7, 0x48, 0x82, 0xe7
+.byte 0x66, 0x89, 0xe3, 0xe3
+.byte 0xe4, 0xe3, 0xe3, 0xe3
+"$unwind$func2":
+.byte 0x15, 0x00, 0x00, 0x40
+.byte 0xe7, 0x00, 0x01
+.byte 0xe7, 0x00, 0x41
+.byte 0xe7, 0x00, 0x81
+.byte 0xe7, 0x20, 0x01
+.byte 0xe7, 0x7d, 0x01
+.byte 0xe4
+.fill 20, 1, 0xe3
diff --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
index fab99c8b7e7d8..188199c6c129d 100644
--- a/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
+++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -143,35 +143,35 @@ const Decoder::RingEntry Decoder::Ring[] = {
{ 0xff, 0xff, 1, &Decoder::opcode_11111111 }, // UOP_END
};
-
// Unwind opcodes for ARM64.
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
const Decoder::RingEntry Decoder::Ring64[] = {
- { 0xe0, 0x00, 1, &Decoder::opcode_alloc_s },
- { 0xe0, 0x20, 1, &Decoder::opcode_save_r19r20_x },
- { 0xc0, 0x40, 1, &Decoder::opcode_save_fplr },
- { 0xc0, 0x80, 1, &Decoder::opcode_save_fplr_x },
- { 0xf8, 0xc0, 2, &Decoder::opcode_alloc_m },
- { 0xfc, 0xc8, 2, &Decoder::opcode_save_regp },
- { 0xfc, 0xcc, 2, &Decoder::opcode_save_regp_x },
- { 0xfc, 0xd0, 2, &Decoder::opcode_save_reg },
- { 0xfe, 0xd4, 2, &Decoder::opcode_save_reg_x },
- { 0xfe, 0xd6, 2, &Decoder::opcode_save_lrpair },
- { 0xfe, 0xd8, 2, &Decoder::opcode_save_fregp },
- { 0xfe, 0xda, 2, &Decoder::opcode_save_fregp_x },
- { 0xfe, 0xdc, 2, &Decoder::opcode_save_freg },
- { 0xff, 0xde, 2, &Decoder::opcode_save_freg_x },
- { 0xff, 0xe0, 4, &Decoder::opcode_alloc_l },
- { 0xff, 0xe1, 1, &Decoder::opcode_setfp },
- { 0xff, 0xe2, 2, &Decoder::opcode_addfp },
- { 0xff, 0xe3, 1, &Decoder::opcode_nop },
- { 0xff, 0xe4, 1, &Decoder::opcode_end },
- { 0xff, 0xe5, 1, &Decoder::opcode_end_c },
- { 0xff, 0xe6, 1, &Decoder::opcode_save_next },
- { 0xff, 0xe8, 1, &Decoder::opcode_trap_frame },
- { 0xff, 0xe9, 1, &Decoder::opcode_machine_frame },
- { 0xff, 0xea, 1, &Decoder::opcode_context },
- { 0xff, 0xec, 1, &Decoder::opcode_clear_unwound_to_call },
+ {0xe0, 0x00, 1, &Decoder::opcode_alloc_s},
+ {0xe0, 0x20, 1, &Decoder::opcode_save_r19r20_x},
+ {0xc0, 0x40, 1, &Decoder::opcode_save_fplr},
+ {0xc0, 0x80, 1, &Decoder::opcode_save_fplr_x},
+ {0xf8, 0xc0, 2, &Decoder::opcode_alloc_m},
+ {0xfc, 0xc8, 2, &Decoder::opcode_save_regp},
+ {0xfc, 0xcc, 2, &Decoder::opcode_save_regp_x},
+ {0xfc, 0xd0, 2, &Decoder::opcode_save_reg},
+ {0xfe, 0xd4, 2, &Decoder::opcode_save_reg_x},
+ {0xfe, 0xd6, 2, &Decoder::opcode_save_lrpair},
+ {0xfe, 0xd8, 2, &Decoder::opcode_save_fregp},
+ {0xfe, 0xda, 2, &Decoder::opcode_save_fregp_x},
+ {0xfe, 0xdc, 2, &Decoder::opcode_save_freg},
+ {0xff, 0xde, 2, &Decoder::opcode_save_freg_x},
+ {0xff, 0xe0, 4, &Decoder::opcode_alloc_l},
+ {0xff, 0xe1, 1, &Decoder::opcode_setfp},
+ {0xff, 0xe2, 2, &Decoder::opcode_addfp},
+ {0xff, 0xe3, 1, &Decoder::opcode_nop},
+ {0xff, 0xe4, 1, &Decoder::opcode_end},
+ {0xff, 0xe5, 1, &Decoder::opcode_end_c},
+ {0xff, 0xe6, 1, &Decoder::opcode_save_next},
+ {0xff, 0xe7, 3, &Decoder::opcode_save_any_reg},
+ {0xff, 0xe8, 1, &Decoder::opcode_trap_frame},
+ {0xff, 0xe9, 1, &Decoder::opcode_machine_frame},
+ {0xff, 0xea, 1, &Decoder::opcode_context},
+ {0xff, 0xec, 1, &Decoder::opcode_clear_unwound_to_call},
};
static void printRange(raw_ostream &OS, ListSeparator &LS, unsigned First,
@@ -869,6 +869,83 @@ bool Decoder::opcode_save_next(const uint8_t *OC, unsigned &Offset,
return false;
}
+bool Decoder::opcode_save_any_reg(const uint8_t *OC, unsigned &Offset,
+ unsigned Length, bool Prologue) {
+ // Whether the instruction has writeback
+ bool Writeback = (OC[Offset + 1] & 0x20) == 0x20;
+ // Whether the instruction is paired. (Paired instructions are required
+ // to save/restore adjacent registers.)
+ bool Paired = (OC[Offset + 1] & 0x40) == 0x40;
+ // The kind of register saved:
+ // - 0 is an x register
+ // - 1 is the low half of a q register
+ // - 2 is a whole q register
+ int RegKind = (OC[Offset + 2] & 0xC0) >> 6;
+ // Encoded register name (0 -> x0/q0, 1 -> x1/q1, etc.)
+ int Reg = OC[Offset + 1] & 0x1F;
+ // Encoded stack offset of load/store instruction; decoding varies by mode.
+ int StackOffset = OC[Offset + 2] & 0x3F;
+ if (Writeback)
+ StackOffset++;
+ if (!Writeback && !Paired && RegKind != 2)
+ StackOffset *= 8;
+ else
+ StackOffset *= 16;
+
+ SW.startLine() << format("0x%02x%02x%02x ; ", OC[Offset],
+ OC[Offset + 1], OC[Offset + 2]);
+
+ // Verify the encoding is in a form we understand. The high bit of the first
+ // byte, and mode 3 for the register kind are apparently reserved. The
+ // encoded register must refer to a valid register.
+ int MaxReg = 0x1F;
+ if (Paired)
+ --MaxReg;
+ if (RegKind == 0)
+ --MaxReg;
+ if ((OC[Offset + 1] & 0x80) == 0x80 || RegKind == 3 || Reg > MaxReg) {
+ SW.getOStream() << "invalid save_any_reg encoding\n";
+ Offset += 3;
+ return false;
+ }
+
+ if (Paired) {
+ if (Prologue)
+ SW.getOStream() << "stp ";
+ else
+ SW.getOStream() << "ldp ";
+ } else {
+ if (Prologue)
+ SW.getOStream() << "str ";
+ else
+ SW.getOStream() << "ldr ";
+ }
+
+ char RegChar = 'x';
+ if (RegKind == 1) {
+ RegChar = 'd';
+ } else if (RegKind == 2) {
+ RegChar = 'q';
+ }
+
+ if (Paired)
+ SW.getOStream() << format("%c%d, %c%d, ", RegChar, Reg, RegChar, Reg + 1);
+ else
+ SW.getOStream() << format("%c%d, ", RegChar, Reg);
+
+ if (Writeback) {
+ if (Prologue)
+ SW.getOStream() << format("[sp, #-%d]!\n", StackOffset);
+ else
+ SW.getOStream() << format("[sp], #%d\n", StackOffset);
+ } else {
+ SW.getOStream() << format("[sp, #%d]\n", StackOffset);
+ }
+
+ Offset += 3;
+ return false;
+}
+
bool Decoder::opcode_trap_frame(const uint8_t *OC, unsigned &Offset,
unsigned Length, bool Prologue) {
SW.startLine() << format("0x%02x ; trap frame\n", OC[Offset]);
diff --git a/llvm/tools/llvm-readobj/ARMWinEHPrinter.h b/llvm/tools/llvm-readobj/ARMWinEHPrinter.h
index ceaa866ff2150..eb2393de8f67d 100644
--- a/llvm/tools/llvm-readobj/ARMWinEHPrinter.h
+++ b/llvm/tools/llvm-readobj/ARMWinEHPrinter.h
@@ -121,6 +121,8 @@ class Decoder {
bool Prologue);
bool opcode_save_next(const uint8_t *Opcodes, unsigned &Offset,
unsigned Length, bool Prologue);
+ bool opcode_save_any_reg(const uint8_t *Opcodes, unsigned &Offset,
+ unsigned Length, bool Prologue);
bool opcode_trap_frame(const uint8_t *Opcodes, unsigned &Offset,
unsigned Length, bool Prologue);
bool opcode_machine_frame(const uint8_t *Opcodes, unsigned &Offset,
More information about the llvm-commits
mailing list