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 000000000000..98faf1b6b380 --- /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 +// 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 +// 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 +// 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 294323a916aa..201cdced8667 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 "; break; case ARM::WinEH::ReturnType::RT_BW: - OS << "b.w target"; + OS << "b.w "; 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 &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 &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 \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 \n", OC[Offset]); ++Offset; return true; } @@ -948,7 +969,7 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF, if (XData.E()) { ArrayRef 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(),