forked from OSchip/llvm-project
[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
This commit is contained in:
parent
d81064949f
commit
92f1028ceb
|
@ -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
|
|
@ -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(),
|
||||
|
|
Loading…
Reference in New Issue