forked from OSchip/llvm-project
[ARM64][Windows] Add unwind support to llvm-readobj
This patch adds support for dumping the unwind info from ARM64 COFF object files. Differential Revision: https://reviews.llvm.org/D53264 llvm-svn: 345108
This commit is contained in:
parent
4005f9a860
commit
cd41638292
|
@ -207,6 +207,8 @@ std::pair<uint16_t, uint32_t> SavedRegisterMask(const RuntimeFunction &RF);
|
|||
|
||||
/// ExceptionDataRecord - An entry in the table of exception data (.xdata)
|
||||
///
|
||||
/// The format on ARM is:
|
||||
///
|
||||
/// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
|
||||
/// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
/// +-------+---------+-+-+-+---+-----------------------------------+
|
||||
|
@ -215,6 +217,16 @@ std::pair<uint16_t, uint32_t> SavedRegisterMask(const RuntimeFunction &RF);
|
|||
/// | Reserved |Ex. Code Words| (Extended Epilogue Count) |
|
||||
/// +-------+--------+--------------+-------------------------------+
|
||||
///
|
||||
/// The format on ARM64 is:
|
||||
///
|
||||
/// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
|
||||
/// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
/// +---------+---------+-+-+---+-----------------------------------+
|
||||
/// | C Wrd | Epi Cnt |E|X|Ver| Function Length |
|
||||
/// +---------+------+--'-'-'---'---+-------------------------------+
|
||||
/// | Reserved |Ex. Code Words| (Extended Epilogue Count) |
|
||||
/// +-------+--------+--------------+-------------------------------+
|
||||
///
|
||||
/// Function Length : 18-bit field indicating the total length of the function
|
||||
/// in bytes divided by 2. If a function is larger than
|
||||
/// 512KB, then multiple pdata and xdata records must be used.
|
||||
|
@ -225,7 +237,7 @@ std::pair<uint16_t, uint32_t> SavedRegisterMask(const RuntimeFunction &RF);
|
|||
/// header
|
||||
/// F : 1-bit field indicating that the record describes a function fragment
|
||||
/// (implies that no prologue is present, and prologue processing should be
|
||||
/// skipped)
|
||||
/// skipped) (ARM only)
|
||||
/// Epilogue Count : 5-bit field that differs in meaning based on the E field.
|
||||
///
|
||||
/// If E is set, then this field specifies the index of the
|
||||
|
@ -235,33 +247,43 @@ std::pair<uint16_t, uint32_t> SavedRegisterMask(const RuntimeFunction &RF);
|
|||
/// scopes. If more than 31 scopes exist, then this field and
|
||||
/// the Code Words field must both be set to 0 to indicate that
|
||||
/// an extension word is required.
|
||||
/// Code Words : 4-bit field that species the number of 32-bit words needed to
|
||||
/// contain all the unwind codes. If more than 15 words (63 code
|
||||
/// bytes) are required, then this field and the Epilogue Count
|
||||
/// field must both be set to 0 to indicate that an extension word
|
||||
/// is required.
|
||||
/// Code Words : 4-bit (5-bit on ARM64) field that specifies the number of
|
||||
/// 32-bit words needed to contain all the unwind codes. If more
|
||||
/// than 15 words (31 words on ARM64) are required, then this field
|
||||
/// and the Epilogue Count field must both be set to 0 to indicate
|
||||
/// that an extension word is required.
|
||||
/// Extended Epilogue Count, Extended Code Words :
|
||||
/// Valid only if Epilog Count and Code Words are both
|
||||
/// set to 0. Provides an 8-bit extended code word
|
||||
/// count and 16-bits for epilogue count
|
||||
///
|
||||
/// The epilogue scope format on ARM is:
|
||||
///
|
||||
/// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
|
||||
/// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
/// +----------------+------+---+---+-------------------------------+
|
||||
/// | Ep Start Idx | Cond |Res| Epilogue Start Offset |
|
||||
/// +----------------+------+---+-----------------------------------+
|
||||
///
|
||||
/// The epilogue scope format on ARM64 is:
|
||||
///
|
||||
/// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
|
||||
/// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
/// +-------------------+-------+---+-------------------------------+
|
||||
/// | Ep Start Idx | Res | Epilogue Start Offset |
|
||||
/// +-------------------+-------+-----------------------------------+
|
||||
///
|
||||
/// If the E bit is unset in the header, the header is followed by a series of
|
||||
/// epilogue scopes, which are sorted by their offset.
|
||||
///
|
||||
/// Epilogue Start Offset: 18-bit field encoding the offset of epilogue relative
|
||||
/// to the start of the function in bytes divided by two
|
||||
/// Res : 2-bit field reserved for future expansion (must be set to 0)
|
||||
/// Condition : 4-bit field providing the condition under which the epilogue is
|
||||
/// executed. Unconditional epilogues should set this field to 0xe.
|
||||
/// Epilogues must be entirely conditional or unconditional, and in
|
||||
/// Thumb-2 mode. The epilogue beings with the first instruction
|
||||
/// after the IT opcode.
|
||||
/// Condition : (ARM only) 4-bit field providing the condition under which the
|
||||
/// epilogue is executed. Unconditional epilogues should set this
|
||||
/// field to 0xe. Epilogues must be entirely conditional or
|
||||
/// unconditional, and in Thumb-2 mode. The epilogue begins with
|
||||
/// the first instruction after the IT opcode.
|
||||
/// Epilogue Start Index : 8-bit field indicating the byte index of the first
|
||||
/// unwind code describing the epilogue
|
||||
///
|
||||
|
@ -293,18 +315,33 @@ struct EpilogueScope {
|
|||
const support::ulittle32_t ES;
|
||||
|
||||
EpilogueScope(const support::ulittle32_t Data) : ES(Data) {}
|
||||
// Same for both ARM and AArch64.
|
||||
uint32_t EpilogueStartOffset() const {
|
||||
return (ES & 0x0003ffff);
|
||||
}
|
||||
uint8_t Res() const {
|
||||
|
||||
// Different implementations for ARM and AArch64.
|
||||
uint8_t ResARM() const {
|
||||
return ((ES & 0x000c0000) >> 18);
|
||||
}
|
||||
|
||||
uint8_t ResAArch64() const {
|
||||
return ((ES & 0x000f0000) >> 18);
|
||||
}
|
||||
|
||||
// Condition is only applicable to ARM.
|
||||
uint8_t Condition() const {
|
||||
return ((ES & 0x00f00000) >> 20);
|
||||
}
|
||||
uint8_t EpilogueStartIndex() const {
|
||||
|
||||
// Different implementations for ARM and AArch64.
|
||||
uint8_t EpilogueStartIndexARM() const {
|
||||
return ((ES & 0xff000000) >> 24);
|
||||
}
|
||||
|
||||
uint8_t EpilogueStartIndexAArch64() const {
|
||||
return ((ES & 0xffc00000) >> 22);
|
||||
}
|
||||
};
|
||||
|
||||
struct ExceptionDataRecord;
|
||||
|
@ -312,13 +349,23 @@ inline size_t HeaderWords(const ExceptionDataRecord &XR);
|
|||
|
||||
struct ExceptionDataRecord {
|
||||
const support::ulittle32_t *Data;
|
||||
bool isAArch64;
|
||||
|
||||
ExceptionDataRecord(const support::ulittle32_t *Data) : Data(Data) {}
|
||||
ExceptionDataRecord(const support::ulittle32_t *Data, bool isAArch64) :
|
||||
Data(Data), isAArch64(isAArch64) {}
|
||||
|
||||
uint32_t FunctionLength() const {
|
||||
return (Data[0] & 0x0003ffff);
|
||||
}
|
||||
|
||||
uint32_t FunctionLengthInBytesARM() const {
|
||||
return FunctionLength() << 1;
|
||||
}
|
||||
|
||||
uint32_t FunctionLengthInBytesAArch64() const {
|
||||
return FunctionLength() << 2;
|
||||
}
|
||||
|
||||
uint8_t Vers() const {
|
||||
return (Data[0] & 0x000C0000) >> 18;
|
||||
}
|
||||
|
@ -332,18 +379,25 @@ struct ExceptionDataRecord {
|
|||
}
|
||||
|
||||
bool F() const {
|
||||
assert(!isAArch64 && "Fragments are only supported on ARMv7 WinEH");
|
||||
return ((Data[0] & 0x00400000) >> 22);
|
||||
}
|
||||
|
||||
uint8_t EpilogueCount() const {
|
||||
if (HeaderWords(*this) == 1)
|
||||
if (HeaderWords(*this) == 1) {
|
||||
if (isAArch64)
|
||||
return (Data[0] & 0x07C00000) >> 22;
|
||||
return (Data[0] & 0x0f800000) >> 23;
|
||||
}
|
||||
return Data[1] & 0x0000ffff;
|
||||
}
|
||||
|
||||
uint8_t CodeWords() const {
|
||||
if (HeaderWords(*this) == 1)
|
||||
if (HeaderWords(*this) == 1) {
|
||||
if (isAArch64)
|
||||
return (Data[0] & 0xf8000000) >> 27;
|
||||
return (Data[0] & 0xf0000000) >> 28;
|
||||
}
|
||||
return (Data[1] & 0x00ff0000) >> 16;
|
||||
}
|
||||
|
||||
|
@ -373,6 +427,8 @@ struct ExceptionDataRecord {
|
|||
};
|
||||
|
||||
inline size_t HeaderWords(const ExceptionDataRecord &XR) {
|
||||
if (XR.isAArch64)
|
||||
return (XR.Data[0] & 0xffc0000) ? 1 : 2;
|
||||
return (XR.Data[0] & 0xff800000) ? 1 : 2;
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,53 @@
|
|||
## Check that error handling for bad opcodes works.
|
||||
## .xdata below contains the bad opcode 0xdf in the 4th word of .xdata.
|
||||
|
||||
// REQUIRES: aarch64-registered-target
|
||||
// RUN: llvm-mc -filetype=obj -triple aarch64-windows %s -o - \
|
||||
// RUN: | llvm-readobj -unwind - | FileCheck %s
|
||||
|
||||
// CHECK: Prologue [
|
||||
// CHECK: 0xdf ; Bad opcode!
|
||||
// CHECK: 0xd600 ; stp x19, lr, [sp, #0]
|
||||
// CHECK: 0x01 ; sub sp, #16
|
||||
// CHECK: 0xe4 ; end
|
||||
// CHECK: ]
|
||||
|
||||
.text
|
||||
.globl "?func@@YAHXZ"
|
||||
.p2align 3
|
||||
"?func@@YAHXZ":
|
||||
sub sp,sp,#0x10
|
||||
stp x19,lr,[sp]
|
||||
sub sp,sp,#0x1F0
|
||||
mov w19,w0
|
||||
bl "?func2@@YAXXZ"
|
||||
cmp w19,#2
|
||||
ble .LBB0_1
|
||||
bl "?func2@@YAHXZ"
|
||||
add sp,sp,#0x1F0
|
||||
ldp x19,lr,[sp]
|
||||
add sp,sp,#0x10
|
||||
ret
|
||||
.LBB0_1:
|
||||
mov x0,sp
|
||||
bl "?func3@@YAHPEAH@Z"
|
||||
add sp,sp,#0x1F0
|
||||
ldp x19,lr,[sp]
|
||||
add sp,sp,#0x10
|
||||
ret
|
||||
|
||||
|
||||
.section .pdata,"dr"
|
||||
.long "?func@@YAHXZ"@IMGREL
|
||||
.long "$unwind$func@@YAHXZ"@IMGREL
|
||||
|
||||
|
||||
.section .xdata,"dr"
|
||||
"$unwind$func@@YAHXZ":
|
||||
.p2align 3
|
||||
.long 0x10800012
|
||||
.long 0x8
|
||||
.long 0xe
|
||||
.long 0x100d6df
|
||||
.long 0xe3e3e3e4
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
## Check that the sanity check for an inconsistent header works.
|
||||
## The first word contains the bad value for CodeWords, 0xf, which indicates
|
||||
## that we need 0x11110 << 2 = 120 bytes of space for the unwind codes.
|
||||
## It follows that the .xdata section is badly formed as only 8 bytes are
|
||||
## allocated for the unwind codes.
|
||||
|
||||
// REQUIRES: aarch64-registered-target
|
||||
// RUN: llvm-mc -filetype=obj -triple aarch64-windows %s -o - \
|
||||
// RUN: | not llvm-readobj -unwind - 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: LLVM ERROR: Malformed unwind data
|
||||
|
||||
.text
|
||||
.globl "?func@@YAHXZ"
|
||||
.p2align 3
|
||||
"?func@@YAHXZ":
|
||||
sub sp,sp,#0x10
|
||||
stp x19,lr,[sp]
|
||||
sub sp,sp,#0x1F0
|
||||
mov w19,w0
|
||||
bl "?func2@@YAXXZ"
|
||||
cmp w19,#2
|
||||
ble .LBB0_1
|
||||
bl "?func2@@YAHXZ"
|
||||
add sp,sp,#0x1F0
|
||||
ldp x19,lr,[sp]
|
||||
add sp,sp,#0x10
|
||||
ret
|
||||
.LBB0_1:
|
||||
mov x0,sp
|
||||
bl "?func3@@YAHPEAH@Z"
|
||||
add sp,sp,#0x1F0
|
||||
ldp x19,lr,[sp]
|
||||
add sp,sp,#0x10
|
||||
ret
|
||||
|
||||
.section .pdata,"dr"
|
||||
.long "?func@@YAHXZ"@IMGREL
|
||||
.long "$unwind$func@@YAHXZ"@IMGREL
|
||||
|
||||
|
||||
.section .xdata,"dr"
|
||||
"$unwind$func@@YAHXZ":
|
||||
.p2align 3
|
||||
.long 0xf0800012
|
||||
.long 0x8
|
||||
.long 0xe
|
||||
.long 0x100d61f
|
||||
.long 0xe3e3e3e4
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
## Check that error handling for going past the unwind data works.
|
||||
## .xdata below contains bad opcodes in the last word. The last byte, 0xe0,
|
||||
## indicates that we have come across alloc_l, which requires 4 bytes. In this
|
||||
## case, unwind code processing will go past the allocated unwind data.
|
||||
|
||||
// REQUIRES: aarch64-registered-target
|
||||
// RUN: llvm-mc -filetype=obj -triple aarch64-windows %s -o - \
|
||||
// RUN: | llvm-readobj -unwind - | FileCheck %s
|
||||
|
||||
// CHECK: Prologue [
|
||||
// CHECK: Opcode 0xe0 goes past the unwind data
|
||||
|
||||
.text
|
||||
.globl "?func@@YAHXZ"
|
||||
.p2align 3
|
||||
"?func@@YAHXZ":
|
||||
sub sp,sp,#0x10
|
||||
stp x19,lr,[sp]
|
||||
sub sp,sp,#0x1F0
|
||||
mov w19,w0
|
||||
bl "?func2@@YAXXZ"
|
||||
cmp w19,#2
|
||||
ble .LBB0_1
|
||||
bl "?func2@@YAHXZ"
|
||||
add sp,sp,#0x1F0
|
||||
ldp x19,lr,[sp]
|
||||
add sp,sp,#0x10
|
||||
ret
|
||||
.LBB0_1:
|
||||
mov x0,sp
|
||||
bl "?func3@@YAHPEAH@Z"
|
||||
add sp,sp,#0x1F0
|
||||
ldp x19,lr,[sp]
|
||||
add sp,sp,#0x10
|
||||
ret
|
||||
|
||||
|
||||
.section .pdata,"dr"
|
||||
.long "?func@@YAHXZ"@IMGREL
|
||||
.long "$unwind$func@@YAHXZ"@IMGREL
|
||||
|
||||
|
||||
.section .xdata,"dr"
|
||||
"$unwind$func@@YAHXZ":
|
||||
.p2align 3
|
||||
.long 0x10800012
|
||||
.long 0x8
|
||||
.long 0xe
|
||||
.long 0x100d61f
|
||||
.long 0xe0000000
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
RUN: llvm-readobj -unwind %p/Inputs/arm64-win1.obj | FileCheck %s -check-prefix=UNWIND1
|
||||
RUN: llvm-readobj -unwind %p/Inputs/arm64-win2.obj | FileCheck %s -check-prefix=UNWIND2
|
||||
|
||||
UNWIND1: ExceptionData {
|
||||
UNWIND1-NEXT: FunctionLength: 340
|
||||
UNWIND1-NEXT: Version: 0
|
||||
UNWIND1-NEXT: ExceptionData: No
|
||||
UNWIND1-NEXT: EpiloguePacked: Yes
|
||||
UNWIND1-NEXT: EpilogueOffset: 15
|
||||
UNWIND1-NEXT: ByteCodeLength: 28
|
||||
UNWIND1-NEXT: Prologue [
|
||||
UNWIND1-NEXT: 0xe002dac8 ; sub sp, #2993280
|
||||
UNWIND1-NEXT: 0xe3 ; nop
|
||||
UNWIND1-NEXT: 0xe3 ; nop
|
||||
UNWIND1-NEXT: 0xe3 ; nop
|
||||
UNWIND1-NEXT: 0xd885 ; stp d10, d11, [sp, #40]
|
||||
UNWIND1-NEXT: 0xd803 ; stp d8, d9, [sp, #24]
|
||||
UNWIND1-NEXT: 0xd2c2 ; str x30, [sp, #16]
|
||||
UNWIND1-NEXT: 0x28 ; stp x19, x20, [sp, #-64]!
|
||||
UNWIND1-NEXT: 0xe4 ; end
|
||||
UNWIND1-NEXT: ]
|
||||
UNWIND1-NEXT: Epilogue [
|
||||
UNWIND1-NEXT: 0xe002dac8 ; add sp, #2993280
|
||||
UNWIND1-NEXT: 0xd885 ; ldp d10, d11, [sp, #40]
|
||||
UNWIND1-NEXT: 0xd803 ; ldp d8, d9, [sp, #24]
|
||||
UNWIND1-NEXT: 0xd2c2 ; ldr x30, [sp, #16]
|
||||
UNWIND1-NEXT: 0x28 ; ldp x19, x20, [sp], #64
|
||||
UNWIND1-NEXT: 0xe4 ; end
|
||||
UNWIND1-NEXT: ]
|
||||
UNWIND1_NEXT: }
|
||||
|
||||
|
||||
UNWIND2: ExceptionData {
|
||||
UNWIND2-NEXT: FunctionLength: 72
|
||||
UNWIND2-NEXT: Version: 0
|
||||
UNWIND2-NEXT: ExceptionData: No
|
||||
UNWIND2-NEXT: EpiloguePacked: No
|
||||
UNWIND2-NEXT: EpilogueScopes: 2
|
||||
UNWIND2-NEXT: ByteCodeLength: 8
|
||||
UNWIND2-NEXT: Prologue [
|
||||
UNWIND2-NEXT: 0x1f ; sub sp, #496
|
||||
UNWIND2-NEXT: 0xd600 ; stp x19, lr, [sp, #0]
|
||||
UNWIND2-NEXT: 0x01 ; sub sp, #16
|
||||
UNWIND2-NEXT: 0xe4 ; end
|
||||
UNWIND2-NEXT: ]
|
||||
UNWIND2-NEXT: EpilogueScopes [
|
||||
UNWIND2-NEXT: EpilogueScope {
|
||||
UNWIND2-NEXT: StartOffset: 8
|
||||
UNWIND2-NEXT: EpilogueStartIndex: 0
|
||||
UNWIND2-NEXT: Opcodes [
|
||||
UNWIND2-NEXT: 0x1f ; add sp, #496
|
||||
UNWIND2-NEXT: 0xd600 ; ldp x19, lr, [sp, #0]
|
||||
UNWIND2-NEXT: 0x01 ; add sp, #16
|
||||
UNWIND2-NEXT: 0xe4 ; end
|
||||
UNWIND2-NEXT: ]
|
||||
UNWIND2-NEXT: }
|
||||
UNWIND2-NEXT: EpilogueScope {
|
||||
UNWIND2-NEXT: StartOffset: 14
|
||||
UNWIND2-NEXT: EpilogueStartIndex: 0
|
||||
UNWIND2-NEXT: Opcodes [
|
||||
UNWIND2-NEXT: 0x1f ; add sp, #496
|
||||
UNWIND2-NEXT: 0xd600 ; ldp x19, lr, [sp, #0]
|
||||
UNWIND2-NEXT: 0x01 ; add sp, #16
|
||||
UNWIND2-NEXT: 0xe4 ; end
|
||||
UNWIND2-NEXT: ]
|
||||
UNWIND2-NEXT: }
|
||||
UNWIND2-NEXT: ]
|
||||
UNWIND2-NEXT: }
|
||||
|
|
@ -118,31 +118,57 @@ const size_t Decoder::PDataEntrySize = sizeof(RuntimeFunction);
|
|||
|
||||
// TODO name the uops more appropriately
|
||||
const Decoder::RingEntry Decoder::Ring[] = {
|
||||
{ 0x80, 0x00, &Decoder::opcode_0xxxxxxx }, // UOP_STACK_FREE (16-bit)
|
||||
{ 0xc0, 0x80, &Decoder::opcode_10Lxxxxx }, // UOP_POP (32-bit)
|
||||
{ 0xf0, 0xc0, &Decoder::opcode_1100xxxx }, // UOP_STACK_SAVE (16-bit)
|
||||
{ 0xf8, 0xd0, &Decoder::opcode_11010Lxx }, // UOP_POP (16-bit)
|
||||
{ 0xf8, 0xd8, &Decoder::opcode_11011Lxx }, // UOP_POP (32-bit)
|
||||
{ 0xf8, 0xe0, &Decoder::opcode_11100xxx }, // UOP_VPOP (32-bit)
|
||||
{ 0xfc, 0xe8, &Decoder::opcode_111010xx }, // UOP_STACK_FREE (32-bit)
|
||||
{ 0xfe, 0xec, &Decoder::opcode_1110110L }, // UOP_POP (16-bit)
|
||||
{ 0xff, 0xee, &Decoder::opcode_11101110 }, // UOP_MICROSOFT_SPECIFIC (16-bit)
|
||||
{ 0x80, 0x00, 1, &Decoder::opcode_0xxxxxxx }, // UOP_STACK_FREE (16-bit)
|
||||
{ 0xc0, 0x80, 2, &Decoder::opcode_10Lxxxxx }, // UOP_POP (32-bit)
|
||||
{ 0xf0, 0xc0, 1, &Decoder::opcode_1100xxxx }, // UOP_STACK_SAVE (16-bit)
|
||||
{ 0xf8, 0xd0, 1, &Decoder::opcode_11010Lxx }, // UOP_POP (16-bit)
|
||||
{ 0xf8, 0xd8, 1, &Decoder::opcode_11011Lxx }, // UOP_POP (32-bit)
|
||||
{ 0xf8, 0xe0, 1, &Decoder::opcode_11100xxx }, // UOP_VPOP (32-bit)
|
||||
{ 0xfc, 0xe8, 2, &Decoder::opcode_111010xx }, // UOP_STACK_FREE (32-bit)
|
||||
{ 0xfe, 0xec, 2, &Decoder::opcode_1110110L }, // UOP_POP (16-bit)
|
||||
{ 0xff, 0xee, 2, &Decoder::opcode_11101110 }, // UOP_MICROSOFT_SPECIFIC (16-bit)
|
||||
// UOP_PUSH_MACHINE_FRAME
|
||||
// UOP_PUSH_CONTEXT
|
||||
// UOP_PUSH_TRAP_FRAME
|
||||
// UOP_REDZONE_RESTORE_LR
|
||||
{ 0xff, 0xef, &Decoder::opcode_11101111 }, // UOP_LDRPC_POSTINC (32-bit)
|
||||
{ 0xff, 0xf5, &Decoder::opcode_11110101 }, // UOP_VPOP (32-bit)
|
||||
{ 0xff, 0xf6, &Decoder::opcode_11110110 }, // UOP_VPOP (32-bit)
|
||||
{ 0xff, 0xf7, &Decoder::opcode_11110111 }, // UOP_STACK_RESTORE (16-bit)
|
||||
{ 0xff, 0xf8, &Decoder::opcode_11111000 }, // UOP_STACK_RESTORE (16-bit)
|
||||
{ 0xff, 0xf9, &Decoder::opcode_11111001 }, // UOP_STACK_RESTORE (32-bit)
|
||||
{ 0xff, 0xfa, &Decoder::opcode_11111010 }, // UOP_STACK_RESTORE (32-bit)
|
||||
{ 0xff, 0xfb, &Decoder::opcode_11111011 }, // UOP_NOP (16-bit)
|
||||
{ 0xff, 0xfc, &Decoder::opcode_11111100 }, // UOP_NOP (32-bit)
|
||||
{ 0xff, 0xfd, &Decoder::opcode_11111101 }, // UOP_NOP (16-bit) / END
|
||||
{ 0xff, 0xfe, &Decoder::opcode_11111110 }, // UOP_NOP (32-bit) / END
|
||||
{ 0xff, 0xff, &Decoder::opcode_11111111 }, // UOP_END
|
||||
{ 0xff, 0xef, 2, &Decoder::opcode_11101111 }, // UOP_LDRPC_POSTINC (32-bit)
|
||||
{ 0xff, 0xf5, 2, &Decoder::opcode_11110101 }, // UOP_VPOP (32-bit)
|
||||
{ 0xff, 0xf6, 2, &Decoder::opcode_11110110 }, // UOP_VPOP (32-bit)
|
||||
{ 0xff, 0xf7, 3, &Decoder::opcode_11110111 }, // UOP_STACK_RESTORE (16-bit)
|
||||
{ 0xff, 0xf8, 4, &Decoder::opcode_11111000 }, // UOP_STACK_RESTORE (16-bit)
|
||||
{ 0xff, 0xf9, 3, &Decoder::opcode_11111001 }, // UOP_STACK_RESTORE (32-bit)
|
||||
{ 0xff, 0xfa, 4, &Decoder::opcode_11111010 }, // UOP_STACK_RESTORE (32-bit)
|
||||
{ 0xff, 0xfb, 1, &Decoder::opcode_11111011 }, // UOP_NOP (16-bit)
|
||||
{ 0xff, 0xfc, 1, &Decoder::opcode_11111100 }, // UOP_NOP (32-bit)
|
||||
{ 0xff, 0xfd, 1, &Decoder::opcode_11111101 }, // UOP_NOP (16-bit) / END
|
||||
{ 0xff, 0xfe, 1, &Decoder::opcode_11111110 }, // UOP_NOP (32-bit) / END
|
||||
{ 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 },
|
||||
};
|
||||
|
||||
void Decoder::printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask) {
|
||||
|
@ -493,18 +519,291 @@ bool Decoder::opcode_11111111(const uint8_t *OC, unsigned &Offset,
|
|||
return true;
|
||||
}
|
||||
|
||||
// ARM64 unwind codes start here.
|
||||
bool Decoder::opcode_alloc_s(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t NumBytes = (OC[Offset] & 0x1F) << 4;
|
||||
SW.startLine() << format("0x%02x ; %s sp, #%u\n", OC[Offset],
|
||||
static_cast<const char *>(Prologue ? "sub" : "add"),
|
||||
NumBytes);
|
||||
++Offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_r19r20_x(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t Off = (OC[Offset] & 0x1F) << 3;
|
||||
if (Prologue)
|
||||
SW.startLine() << format(
|
||||
"0x%02x ; stp x19, x20, [sp, #-%u]!\n", OC[Offset], Off);
|
||||
else
|
||||
SW.startLine() << format(
|
||||
"0x%02x ; ldp x19, x20, [sp], #%u\n", OC[Offset], Off);
|
||||
++Offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_fplr(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t Off = (OC[Offset] & 0x3F) << 3;
|
||||
SW.startLine() << format(
|
||||
"0x%02x ; %s x29, x30, [sp, #%u]\n", OC[Offset],
|
||||
static_cast<const char *>(Prologue ? "stp" : "ldp"), Off);
|
||||
++Offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_fplr_x(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t Off = ((OC[Offset] & 0x3F) + 1) << 3;
|
||||
if (Prologue)
|
||||
SW.startLine() << format(
|
||||
"0x%02x ; stp x29, x30, [sp, #-%u]!\n", OC[Offset], Off);
|
||||
else
|
||||
SW.startLine() << format(
|
||||
"0x%02x ; ldp x29, x30, [sp], #%u\n", OC[Offset], Off);
|
||||
++Offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_alloc_m(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t NumBytes = ((OC[Offset] & 0x07) << 8);
|
||||
NumBytes |= (OC[Offset + 1] & 0xFF);
|
||||
NumBytes <<= 4;
|
||||
SW.startLine() << format("0x%02x%02x ; %s sp, #%u\n",
|
||||
OC[Offset], OC[Offset + 1],
|
||||
static_cast<const char *>(Prologue ? "sub" : "add"),
|
||||
NumBytes);
|
||||
Offset += 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_regp(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t Reg = ((OC[Offset] & 0x03) << 8);
|
||||
Reg |= (OC[Offset + 1] & 0xC0);
|
||||
Reg >>= 6;
|
||||
Reg += 19;
|
||||
uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
|
||||
SW.startLine() << format(
|
||||
"0x%02x%02x ; %s x%u, x%u, [sp, #%u]\n",
|
||||
OC[Offset], OC[Offset + 1],
|
||||
static_cast<const char *>(Prologue ? "stp" : "ldp"), Reg, Reg + 1, Off);
|
||||
Offset += 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_regp_x(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t Reg = ((OC[Offset] & 0x03) << 8);
|
||||
Reg |= (OC[Offset + 1] & 0xC0);
|
||||
Reg >>= 6;
|
||||
Reg += 19;
|
||||
uint32_t Off = ((OC[Offset + 1] & 0x3F) + 1) << 3;
|
||||
if (Prologue)
|
||||
SW.startLine() << format(
|
||||
"0x%02x%02x ; stp x%u, x%u, [sp, #-%u]!\n",
|
||||
OC[Offset], OC[Offset + 1], Reg,
|
||||
Reg + 1, Off);
|
||||
else
|
||||
SW.startLine() << format(
|
||||
"0x%02x%02x ; ldp x%u, x%u, [sp], #%u\n",
|
||||
OC[Offset], OC[Offset + 1], Reg,
|
||||
Reg + 1, Off);
|
||||
Offset += 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_reg(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t Reg = (OC[Offset] & 0x03) << 8;
|
||||
Reg |= (OC[Offset + 1] & 0xC0);
|
||||
Reg >>= 6;
|
||||
Reg += 19;
|
||||
uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
|
||||
SW.startLine() << format("0x%02x%02x ; %s x%u, [sp, #%u]\n",
|
||||
OC[Offset], OC[Offset + 1],
|
||||
static_cast<const char *>(Prologue ? "str" : "ldr"),
|
||||
Reg, Off);
|
||||
Offset += 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_reg_x(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t Reg = (OC[Offset] & 0x01) << 8;
|
||||
Reg |= (OC[Offset + 1] & 0xE0);
|
||||
Reg >>= 5;
|
||||
Reg += 19;
|
||||
uint32_t Off = ((OC[Offset + 1] & 0x1F) + 1) << 3;
|
||||
if (Prologue)
|
||||
SW.startLine() << format("0x%02x%02x ; str x%u, [sp, #%u]!\n",
|
||||
OC[Offset], OC[Offset + 1], Reg, Off);
|
||||
else
|
||||
SW.startLine() << format("0x%02x%02x ; ldr x%u, [sp], #%u\n",
|
||||
OC[Offset], OC[Offset + 1], Reg, Off);
|
||||
Offset += 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_lrpair(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t Reg = (OC[Offset] & 0x01) << 8;
|
||||
Reg |= (OC[Offset + 1] & 0xC0);
|
||||
Reg >>= 6;
|
||||
Reg *= 2;
|
||||
Reg += 19;
|
||||
uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
|
||||
SW.startLine() << format("0x%02x%02x ; %s x%u, lr, [sp, #%u]\n",
|
||||
OC[Offset], OC[Offset + 1],
|
||||
static_cast<const char *>(Prologue ? "stp" : "ldp"),
|
||||
Reg, Off);
|
||||
Offset += 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_fregp(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t Reg = (OC[Offset] & 0x01) << 8;
|
||||
Reg |= (OC[Offset + 1] & 0xC0);
|
||||
Reg >>= 6;
|
||||
Reg += 8;
|
||||
uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
|
||||
SW.startLine() << format("0x%02x%02x ; %s d%u, d%u, [sp, #%u]\n",
|
||||
OC[Offset], OC[Offset + 1],
|
||||
static_cast<const char *>(Prologue ? "stp" : "ldp"),
|
||||
Reg, Reg + 1, Off);
|
||||
Offset += 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_fregp_x(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t Reg = (OC[Offset] & 0x01) << 8;
|
||||
Reg |= (OC[Offset + 1] & 0xC0);
|
||||
Reg >>= 6;
|
||||
Reg += 8;
|
||||
uint32_t Off = ((OC[Offset + 1] & 0x3F) + 1) << 3;
|
||||
if (Prologue)
|
||||
SW.startLine() << format(
|
||||
"0x%02x%02x ; stp d%u, d%u, [sp, #-%u]!\n", OC[Offset],
|
||||
OC[Offset + 1], Reg, Reg + 1, Off);
|
||||
else
|
||||
SW.startLine() << format(
|
||||
"0x%02x%02x ; ldp d%u, d%u, [sp], #%u\n", OC[Offset],
|
||||
OC[Offset + 1], Reg, Reg + 1, Off);
|
||||
Offset += 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_freg(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t Reg = (OC[Offset] & 0x01) << 8;
|
||||
Reg |= (OC[Offset + 1] & 0xC0);
|
||||
Reg >>= 6;
|
||||
Reg += 8;
|
||||
uint32_t Off = (OC[Offset + 1] & 0x3F) << 3;
|
||||
SW.startLine() << format("0x%02x%02x ; %s d%u, [sp, #%u]\n",
|
||||
OC[Offset], OC[Offset + 1],
|
||||
static_cast<const char *>(Prologue ? "str" : "ldr"),
|
||||
Reg, Off);
|
||||
Offset += 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_save_freg_x(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
uint32_t Reg = ((OC[Offset + 1] & 0xE0) >> 5) + 8;
|
||||
uint32_t Off = ((OC[Offset + 1] & 0x1F) + 1) << 3;
|
||||
if (Prologue)
|
||||
SW.startLine() << format(
|
||||
"0x%02x%02x ; str d%u, [sp, #-%u]!\n", OC[Offset],
|
||||
OC[Offset + 1], Reg, Off);
|
||||
else
|
||||
SW.startLine() << format(
|
||||
"0x%02x%02x ; ldr d%u, [sp], #%u\n", OC[Offset],
|
||||
OC[Offset + 1], Reg, Off);
|
||||
Offset += 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_alloc_l(const uint8_t *OC, unsigned &Offset,
|
||||
unsigned Length, bool Prologue) {
|
||||
unsigned Off =
|
||||
(OC[Offset + 1] << 16) | (OC[Offset + 2] << 8) | (OC[Offset + 3] << 0);
|
||||
Off <<= 4;
|
||||
SW.startLine() << format(
|
||||
"0x%02x%02x%02x%02x ; %s sp, #%u\n", OC[Offset], OC[Offset + 1],
|
||||
OC[Offset + 2], OC[Offset + 3],
|
||||
static_cast<const char *>(Prologue ? "sub" : "add"), Off);
|
||||
Offset += 4;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_setfp(const uint8_t *OC, unsigned &Offset, unsigned Length,
|
||||
bool Prologue) {
|
||||
SW.startLine() << format("0x%02x ; mov fp, sp\n", OC[Offset]);
|
||||
++Offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_addfp(const uint8_t *OC, unsigned &Offset, unsigned Length,
|
||||
bool Prologue) {
|
||||
unsigned NumBytes = OC[Offset + 1] << 3;
|
||||
SW.startLine() << format("0x%02x%02x ; add fp, sp, #%u\n",
|
||||
OC[Offset], OC[Offset + 1], NumBytes);
|
||||
Offset += 2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_nop(const uint8_t *OC, unsigned &Offset, unsigned Length,
|
||||
bool Prologue) {
|
||||
SW.startLine() << format("0x%02x ; nop\n", OC[Offset]);
|
||||
++Offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_end(const uint8_t *OC, unsigned &Offset, unsigned Length,
|
||||
bool Prologue) {
|
||||
SW.startLine() << format("0x%02x ; end\n", OC[Offset]);
|
||||
++Offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Decoder::opcode_end_c(const uint8_t *OC, unsigned &Offset, unsigned Length,
|
||||
bool Prologue) {
|
||||
SW.startLine() << format("0x%02x ; end_c\n", OC[Offset]);
|
||||
++Offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Decoder::decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset,
|
||||
bool Prologue) {
|
||||
assert((!Prologue || Offset == 0) && "prologue should always use offset 0");
|
||||
|
||||
const RingEntry* DecodeRing = isAArch64 ? Ring64 : Ring;
|
||||
bool Terminated = false;
|
||||
for (unsigned OI = Offset, OE = Opcodes.size(); !Terminated && OI < OE; ) {
|
||||
for (unsigned DI = 0;; ++DI) {
|
||||
if ((Opcodes[OI] & Ring[DI].Mask) == Ring[DI].Value) {
|
||||
Terminated = (this->*Ring[DI].Routine)(Opcodes.data(), OI, 0, Prologue);
|
||||
if ((isAArch64 && (DI >= array_lengthof(Ring64))) ||
|
||||
(!isAArch64 && (DI >= array_lengthof(Ring)))) {
|
||||
SW.startLine() << format("0x%02x ; Bad opcode!\n",
|
||||
Opcodes.data()[Offset]);
|
||||
++OI;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((Opcodes[OI] & DecodeRing[DI].Mask) == DecodeRing[DI].Value) {
|
||||
if (OI + DecodeRing[DI].Length > OE) {
|
||||
SW.startLine() << format("Opcode 0x%02x goes past the unwind data\n",
|
||||
Opcodes[OI]);
|
||||
OI += DecodeRing[DI].Length;
|
||||
break;
|
||||
}
|
||||
Terminated =
|
||||
(this->*DecodeRing[DI].Routine)(Opcodes.data(), OI, 0, Prologue);
|
||||
break;
|
||||
}
|
||||
assert(DI < array_lengthof(Ring) && "unhandled opcode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -520,22 +819,36 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF,
|
|||
uint64_t Offset = VA - SectionVA;
|
||||
const ulittle32_t *Data =
|
||||
reinterpret_cast<const ulittle32_t *>(Contents.data() + Offset);
|
||||
const ExceptionDataRecord XData(Data);
|
||||
|
||||
// Sanity check to ensure that the .xdata header is present.
|
||||
// A header is one or two words, followed by at least one word to describe
|
||||
// the unwind codes. Applicable to both ARM and AArch64.
|
||||
if (Contents.size() - Offset < 8)
|
||||
report_fatal_error(".xdata must be at least 8 bytes in size");
|
||||
|
||||
const ExceptionDataRecord XData(Data, isAArch64);
|
||||
DictScope XRS(SW, "ExceptionData");
|
||||
SW.printNumber("FunctionLength", XData.FunctionLength() << 1);
|
||||
SW.printNumber("FunctionLength",
|
||||
isAArch64 ? XData.FunctionLengthInBytesAArch64() :
|
||||
XData.FunctionLengthInBytesARM());
|
||||
SW.printNumber("Version", XData.Vers());
|
||||
SW.printBoolean("ExceptionData", XData.X());
|
||||
SW.printBoolean("EpiloguePacked", XData.E());
|
||||
SW.printBoolean("Fragment", XData.F());
|
||||
if (!isAArch64)
|
||||
SW.printBoolean("Fragment", XData.F());
|
||||
SW.printNumber(XData.E() ? "EpilogueOffset" : "EpilogueScopes",
|
||||
XData.EpilogueCount());
|
||||
SW.printNumber("ByteCodeLength",
|
||||
static_cast<uint64_t>(XData.CodeWords() * sizeof(uint32_t)));
|
||||
uint64_t ByteCodeLength = XData.CodeWords() * sizeof(uint32_t);
|
||||
SW.printNumber("ByteCodeLength", ByteCodeLength);
|
||||
|
||||
if ((int64_t)(Contents.size() - Offset - 4 * HeaderWords(XData) -
|
||||
(XData.E() ? 0 : XData.EpilogueCount() * 4) -
|
||||
(XData.X() ? 8 : 0)) < (int64_t)ByteCodeLength)
|
||||
report_fatal_error("Malformed unwind data");
|
||||
|
||||
if (XData.E()) {
|
||||
ArrayRef<uint8_t> UC = XData.UnwindByteCode();
|
||||
if (!XData.F()) {
|
||||
if (isAArch64 || !XData.F()) {
|
||||
ListScope PS(SW, "Prologue");
|
||||
decodeOpcodes(UC, 0, /*Prologue=*/true);
|
||||
}
|
||||
|
@ -544,16 +857,25 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF,
|
|||
decodeOpcodes(UC, XData.EpilogueCount(), /*Prologue=*/false);
|
||||
}
|
||||
} else {
|
||||
{
|
||||
ListScope PS(SW, "Prologue");
|
||||
decodeOpcodes(XData.UnwindByteCode(), 0, /*Prologue=*/true);
|
||||
}
|
||||
ArrayRef<ulittle32_t> EpilogueScopes = XData.EpilogueScopes();
|
||||
ListScope ESS(SW, "EpilogueScopes");
|
||||
for (const EpilogueScope ES : EpilogueScopes) {
|
||||
DictScope ESES(SW, "EpilogueScope");
|
||||
SW.printNumber("StartOffset", ES.EpilogueStartOffset());
|
||||
SW.printNumber("Condition", ES.Condition());
|
||||
SW.printNumber("EpilogueStartIndex", ES.EpilogueStartIndex());
|
||||
if (!isAArch64)
|
||||
SW.printNumber("Condition", ES.Condition());
|
||||
SW.printNumber("EpilogueStartIndex",
|
||||
isAArch64 ? ES.EpilogueStartIndexAArch64()
|
||||
: ES.EpilogueStartIndexARM());
|
||||
|
||||
ListScope Opcodes(SW, "Opcodes");
|
||||
decodeOpcodes(XData.UnwindByteCode(), ES.EpilogueStartIndex(),
|
||||
decodeOpcodes(XData.UnwindByteCode(),
|
||||
isAArch64 ? ES.EpilogueStartIndexAArch64()
|
||||
: ES.EpilogueStartIndexARM(),
|
||||
/*Prologue=*/false);
|
||||
}
|
||||
}
|
||||
|
@ -725,8 +1047,9 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF,
|
|||
}
|
||||
|
||||
SW.printString("Function", formatSymbol(FunctionName, FunctionAddress));
|
||||
SW.printBoolean("Fragment",
|
||||
RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment);
|
||||
if (!isAArch64)
|
||||
SW.printBoolean("Fragment",
|
||||
RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment);
|
||||
SW.printNumber("FunctionLength", RF.FunctionLength());
|
||||
SW.startLine() << "ReturnType: " << RF.Ret() << '\n';
|
||||
SW.printBoolean("HomedParameters", RF.H());
|
||||
|
@ -749,6 +1072,10 @@ bool Decoder::dumpProcedureDataEntry(const COFFObjectFile &COFF,
|
|||
DictScope RFS(SW, "RuntimeFunction");
|
||||
if (Entry.Flag() == RuntimeFunctionFlag::RFF_Unpacked)
|
||||
return dumpUnpackedEntry(COFF, Section, Offset, Index, Entry);
|
||||
if (isAArch64) {
|
||||
llvm::errs() << "Packed unwind data not yet supported for ARM64\n";
|
||||
return false;
|
||||
}
|
||||
return dumpPackedEntry(COFF, Section, Offset, Index, Entry);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,13 +24,16 @@ class Decoder {
|
|||
|
||||
ScopedPrinter &SW;
|
||||
raw_ostream &OS;
|
||||
bool isAArch64;
|
||||
|
||||
struct RingEntry {
|
||||
uint8_t Mask;
|
||||
uint8_t Value;
|
||||
uint8_t Length;
|
||||
bool (Decoder::*Routine)(const uint8_t *, unsigned &, unsigned, bool);
|
||||
};
|
||||
static const RingEntry Ring[];
|
||||
static const RingEntry Ring64[];
|
||||
|
||||
bool opcode_0xxxxxxx(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
|
@ -75,6 +78,50 @@ class Decoder {
|
|||
bool opcode_11111111(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
|
||||
// ARM64 unwind codes start here.
|
||||
bool opcode_alloc_s(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
|
||||
bool Prologue);
|
||||
bool opcode_save_r19r20_x(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
bool opcode_save_fplr(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
bool opcode_save_fplr_x(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
bool opcode_alloc_m(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
|
||||
bool Prologue);
|
||||
bool opcode_save_regp(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
bool opcode_save_regp_x(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
bool opcode_save_reg(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
bool opcode_save_reg_x(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
bool opcode_save_lrpair(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
bool opcode_save_fregp(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
bool opcode_save_fregp_x(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
bool opcode_save_freg(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
bool opcode_save_freg_x(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
bool opcode_alloc_l(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
|
||||
bool Prologue);
|
||||
bool opcode_setfp(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
|
||||
bool Prologue);
|
||||
bool opcode_addfp(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
|
||||
bool Prologue);
|
||||
bool opcode_nop(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
|
||||
bool Prologue);
|
||||
bool opcode_end(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
|
||||
bool Prologue);
|
||||
bool opcode_end_c(const uint8_t *Opcodes, unsigned &Offset, unsigned Length,
|
||||
bool Prologue);
|
||||
bool opcode_save_next(const uint8_t *Opcodes, unsigned &Offset,
|
||||
unsigned Length, bool Prologue);
|
||||
|
||||
void decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset,
|
||||
bool Prologue);
|
||||
|
||||
|
@ -107,7 +154,9 @@ class Decoder {
|
|||
const object::SectionRef Section);
|
||||
|
||||
public:
|
||||
Decoder(ScopedPrinter &SW) : SW(SW), OS(SW.getOStream()) {}
|
||||
Decoder(ScopedPrinter &SW, bool isAArch64) : SW(SW),
|
||||
OS(SW.getOStream()),
|
||||
isAArch64(isAArch64) {}
|
||||
std::error_code dumpProcedureData(const object::COFFObjectFile &COFF);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1549,8 +1549,10 @@ void COFFDumper::printUnwindInfo() {
|
|||
Dumper.printData(Ctx);
|
||||
break;
|
||||
}
|
||||
case COFF::IMAGE_FILE_MACHINE_ARM64:
|
||||
case COFF::IMAGE_FILE_MACHINE_ARMNT: {
|
||||
ARM::WinEH::Decoder Decoder(W);
|
||||
ARM::WinEH::Decoder Decoder(W, Obj->getMachine() ==
|
||||
COFF::IMAGE_FILE_MACHINE_ARM64);
|
||||
Decoder.dumpProcedureData(*Obj);
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue