mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-10 18:11:19 +00:00
Add support for arm64 compact unwind tables, used on darwin arm64
systems (ios, tvos, watchos). It's a simple format to use now that I have i386/x86_64 supported already. The unwind instructions are only valid at call sites -- that is, when lldb is unwinding a frame in the middle of the stack. It cannot be used for the currently executing frame; it has no information about prologues/epilogues/etc. <rdar://problem/12062336> llvm-svn: 270658
This commit is contained in:
parent
e66f45c6eb
commit
b667c20222
@ -133,6 +133,9 @@ private:
|
||||
bool
|
||||
CreateUnwindPlan_i386 (Target &target, FunctionInfo &function_info, UnwindPlan &unwind_plan, Address pc_or_function_start);
|
||||
|
||||
bool
|
||||
CreateUnwindPlan_arm64 (Target &target, FunctionInfo &function_info, UnwindPlan &unwind_plan, Address pc_or_function_start);
|
||||
|
||||
ObjectFile &m_objfile;
|
||||
lldb::SectionSP m_section_sp;
|
||||
lldb::DataBufferSP m_section_contents_if_encrypted; // if the binary is encrypted, read the sect contents
|
||||
|
@ -101,6 +101,27 @@ namespace lldb_private {
|
||||
UNWIND_X86_64_REG_R15 = 5,
|
||||
UNWIND_X86_64_REG_RBP = 6,
|
||||
};
|
||||
|
||||
FLAGS_ANONYMOUS_ENUM()
|
||||
{
|
||||
UNWIND_ARM64_MODE_MASK = 0x0F000000,
|
||||
UNWIND_ARM64_MODE_FRAMELESS = 0x02000000,
|
||||
UNWIND_ARM64_MODE_DWARF = 0x03000000,
|
||||
UNWIND_ARM64_MODE_FRAME = 0x04000000,
|
||||
|
||||
UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001,
|
||||
UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002,
|
||||
UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004,
|
||||
UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008,
|
||||
UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010,
|
||||
UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100,
|
||||
UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200,
|
||||
UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400,
|
||||
UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800,
|
||||
|
||||
UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000,
|
||||
UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -195,6 +216,10 @@ CompactUnwindInfo::GetUnwindPlan (Target &target, Address addr, UnwindPlan& unwi
|
||||
{
|
||||
return CreateUnwindPlan_x86_64 (target, function_info, unwind_plan, addr);
|
||||
}
|
||||
if (arch.GetTriple().getArch() == llvm::Triple::aarch64)
|
||||
{
|
||||
return CreateUnwindPlan_arm64 (target, function_info, unwind_plan, addr);
|
||||
}
|
||||
if (arch.GetTriple().getArch() == llvm::Triple::x86)
|
||||
{
|
||||
return CreateUnwindPlan_i386 (target, function_info, unwind_plan, addr);
|
||||
@ -1229,3 +1254,166 @@ CompactUnwindInfo::CreateUnwindPlan_i386 (Target &target, FunctionInfo &function
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// DWARF register numbers from "DWARF for the ARM 64-bit Architecture (AArch64)" doc by ARM
|
||||
|
||||
enum arm64_eh_regnum {
|
||||
x19 = 19,
|
||||
x20 = 20,
|
||||
x21 = 21,
|
||||
x22 = 22,
|
||||
x23 = 23,
|
||||
x24 = 24,
|
||||
x25 = 25,
|
||||
x26 = 26,
|
||||
x27 = 27,
|
||||
x28 = 28,
|
||||
|
||||
fp = 29,
|
||||
ra = 30,
|
||||
sp = 31,
|
||||
pc = 32,
|
||||
|
||||
// Compact unwind encodes d8-d15 but we don't have eh_frame / dwarf reg #'s for the 64-bit
|
||||
// fp regs. Normally in DWARF it's context sensitive - so it knows it is fetching a
|
||||
// 32- or 64-bit quantity from reg v8 to indicate s0 or d0 - but the unwinder is operating
|
||||
// at a lower level and we'd try to fetch 128 bits if we were told that v8 were stored on
|
||||
// the stack...
|
||||
v8 = 72,
|
||||
v9 = 73,
|
||||
v10 = 74,
|
||||
v11 = 75,
|
||||
v12 = 76,
|
||||
v13 = 77,
|
||||
v14 = 78,
|
||||
v15 = 79,
|
||||
};
|
||||
|
||||
bool
|
||||
CompactUnwindInfo::CreateUnwindPlan_arm64 (Target &target, FunctionInfo &function_info, UnwindPlan &unwind_plan, Address pc_or_function_start)
|
||||
{
|
||||
unwind_plan.SetSourceName ("compact unwind info");
|
||||
unwind_plan.SetSourcedFromCompiler (eLazyBoolYes);
|
||||
unwind_plan.SetUnwindPlanValidAtAllInstructions (eLazyBoolNo);
|
||||
unwind_plan.SetRegisterKind (eRegisterKindEHFrame);
|
||||
|
||||
unwind_plan.SetLSDAAddress (function_info.lsda_address);
|
||||
unwind_plan.SetPersonalityFunctionPtr (function_info.personality_ptr_address);
|
||||
|
||||
UnwindPlan::RowSP row (new UnwindPlan::Row);
|
||||
|
||||
const int wordsize = 8;
|
||||
int mode = function_info.encoding & UNWIND_ARM64_MODE_MASK;
|
||||
|
||||
if (mode == UNWIND_ARM64_MODE_DWARF)
|
||||
return false;
|
||||
|
||||
if (mode == UNWIND_ARM64_MODE_FRAMELESS)
|
||||
{
|
||||
row->SetOffset (0);
|
||||
|
||||
uint32_t stack_size = (EXTRACT_BITS (function_info.encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK)) * 16;
|
||||
|
||||
// Our previous Call Frame Address is the stack pointer plus the stack size
|
||||
row->GetCFAValue().SetIsRegisterPlusOffset (arm64_eh_regnum::sp, stack_size);
|
||||
|
||||
// Our previous PC is in the LR
|
||||
row->SetRegisterLocationToRegister(arm64_eh_regnum::pc, arm64_eh_regnum::ra, true);
|
||||
|
||||
unwind_plan.AppendRow (row);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Should not be possible
|
||||
if (mode != UNWIND_ARM64_MODE_FRAME)
|
||||
return false;
|
||||
|
||||
|
||||
// mode == UNWIND_ARM64_MODE_FRAME
|
||||
|
||||
row->GetCFAValue().SetIsRegisterPlusOffset (arm64_eh_regnum::fp , 2 * wordsize);
|
||||
row->SetOffset (0);
|
||||
row->SetRegisterLocationToAtCFAPlusOffset (arm64_eh_regnum::fp, wordsize * -2, true);
|
||||
row->SetRegisterLocationToAtCFAPlusOffset (arm64_eh_regnum::pc, wordsize * -1, true);
|
||||
row->SetRegisterLocationToIsCFAPlusOffset (arm64_eh_regnum::sp, 0, true);
|
||||
|
||||
int reg_pairs_saved_count = 1;
|
||||
|
||||
uint32_t saved_register_bits = function_info.encoding & 0xfff;
|
||||
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_X19_X20_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
row->SetRegisterLocationToAtCFAPlusOffset (arm64_eh_regnum::x19, cfa_offset, true);
|
||||
cfa_offset -= wordsize;
|
||||
row->SetRegisterLocationToAtCFAPlusOffset (arm64_eh_regnum::x20, cfa_offset, true);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_X21_X22_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
row->SetRegisterLocationToAtCFAPlusOffset (arm64_eh_regnum::x21, cfa_offset, true);
|
||||
cfa_offset -= wordsize;
|
||||
row->SetRegisterLocationToAtCFAPlusOffset (arm64_eh_regnum::x22, cfa_offset, true);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_X23_X24_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
row->SetRegisterLocationToAtCFAPlusOffset (arm64_eh_regnum::x23, cfa_offset, true);
|
||||
cfa_offset -= wordsize;
|
||||
row->SetRegisterLocationToAtCFAPlusOffset (arm64_eh_regnum::x24, cfa_offset, true);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_X25_X26_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
row->SetRegisterLocationToAtCFAPlusOffset (arm64_eh_regnum::x25, cfa_offset, true);
|
||||
cfa_offset -= wordsize;
|
||||
row->SetRegisterLocationToAtCFAPlusOffset (arm64_eh_regnum::x26, cfa_offset, true);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_X27_X28_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
row->SetRegisterLocationToAtCFAPlusOffset (arm64_eh_regnum::x27, cfa_offset, true);
|
||||
cfa_offset -= wordsize;
|
||||
row->SetRegisterLocationToAtCFAPlusOffset (arm64_eh_regnum::x28, cfa_offset, true);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
|
||||
// If we use the v8-v15 regnums here, the unwinder will try to grab 128 bits off the stack;
|
||||
// not sure if we have a good way to represent the 64-bitness of these saves.
|
||||
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_D8_D9_PAIR)
|
||||
{
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_D10_D11_PAIR)
|
||||
{
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_D12_D13_PAIR)
|
||||
{
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_D14_D15_PAIR)
|
||||
{
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
|
||||
unwind_plan.AppendRow (row);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,28 @@
|
||||
#include <stdio.h>
|
||||
#include <mach-o/nlist.h>
|
||||
|
||||
|
||||
enum {
|
||||
UNWIND_ARM64_MODE_MASK = 0x0F000000,
|
||||
UNWIND_ARM64_MODE_FRAMELESS = 0x02000000,
|
||||
UNWIND_ARM64_MODE_DWARF = 0x03000000,
|
||||
UNWIND_ARM64_MODE_FRAME = 0x04000000,
|
||||
|
||||
UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001,
|
||||
UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002,
|
||||
UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004,
|
||||
UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008,
|
||||
UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010,
|
||||
UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100,
|
||||
UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200,
|
||||
UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400,
|
||||
UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800,
|
||||
|
||||
UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000,
|
||||
UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF,
|
||||
};
|
||||
|
||||
|
||||
#define EXTRACT_BITS(value, mask) \
|
||||
( (value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask)))-1) )
|
||||
|
||||
@ -901,6 +923,129 @@ print_encoding_i386 (struct baton baton, uint8_t *function_start, uint32_t encod
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
print_encoding_arm64 (struct baton baton, uint8_t *function_start, uint32_t encoding)
|
||||
{
|
||||
const int wordsize = 8;
|
||||
int mode = encoding & UNWIND_ARM64_MODE_MASK;
|
||||
switch (mode)
|
||||
{
|
||||
case UNWIND_ARM64_MODE_FRAME:
|
||||
{
|
||||
printf ("frame func: CFA is fp+%d ", 16);
|
||||
printf (" pc=[CFA-8] fp=[CFA-16]");
|
||||
int reg_pairs_saved_count = 1;
|
||||
uint32_t saved_register_bits = encoding & 0xfff;
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_X19_X20_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
printf (" x19=[CFA%d]", cfa_offset);
|
||||
cfa_offset -= wordsize;
|
||||
printf (" x20=[CFA%d]", cfa_offset);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_X21_X22_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
printf (" x21=[CFA%d]", cfa_offset);
|
||||
cfa_offset -= wordsize;
|
||||
printf (" x22=[CFA%d]", cfa_offset);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_X23_X24_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
printf (" x23=[CFA%d]", cfa_offset);
|
||||
cfa_offset -= wordsize;
|
||||
printf (" x24=[CFA%d]", cfa_offset);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_X25_X26_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
printf (" x25=[CFA%d]", cfa_offset);
|
||||
cfa_offset -= wordsize;
|
||||
printf (" x26=[CFA%d]", cfa_offset);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_X27_X28_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
printf (" x27=[CFA%d]", cfa_offset);
|
||||
cfa_offset -= wordsize;
|
||||
printf (" x28=[CFA%d]", cfa_offset);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_D8_D9_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
printf (" d8=[CFA%d]", cfa_offset);
|
||||
cfa_offset -= wordsize;
|
||||
printf (" d9=[CFA%d]", cfa_offset);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_D10_D11_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
printf (" d10=[CFA%d]", cfa_offset);
|
||||
cfa_offset -= wordsize;
|
||||
printf (" d11=[CFA%d]", cfa_offset);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_D12_D13_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
printf (" d12=[CFA%d]", cfa_offset);
|
||||
cfa_offset -= wordsize;
|
||||
printf (" d13=[CFA%d]", cfa_offset);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
if (saved_register_bits & UNWIND_ARM64_FRAME_D14_D15_PAIR)
|
||||
{
|
||||
int cfa_offset = reg_pairs_saved_count * -2 * wordsize;
|
||||
cfa_offset -= wordsize;
|
||||
printf (" d14=[CFA%d]", cfa_offset);
|
||||
cfa_offset -= wordsize;
|
||||
printf (" d15=[CFA%d]", cfa_offset);
|
||||
reg_pairs_saved_count++;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case UNWIND_ARM64_MODE_FRAMELESS:
|
||||
{
|
||||
uint32_t stack_size = encoding & UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK;
|
||||
printf ("frameless function: stack size %d ", stack_size * 16);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
case UNWIND_ARM64_MODE_DWARF:
|
||||
{
|
||||
uint32_t dwarf_offset = encoding & UNWIND_ARM64_DWARF_SECTION_OFFSET;
|
||||
printf ("DWARF unwind instructions: FDE at offset %d (file address 0x%" PRIx64 ")",
|
||||
dwarf_offset, dwarf_offset + baton.eh_section_file_address);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0:
|
||||
{
|
||||
printf (" no unwind information");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void print_encoding (struct baton baton, uint8_t *function_start, uint32_t encoding)
|
||||
{
|
||||
@ -913,6 +1058,10 @@ void print_encoding (struct baton baton, uint8_t *function_start, uint32_t encod
|
||||
{
|
||||
print_encoding_i386 (baton, function_start, encoding);
|
||||
}
|
||||
else if (baton.cputype == CPU_TYPE_ARM64)
|
||||
{
|
||||
print_encoding_arm64 (baton, function_start, encoding);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf (" -- unsupported encoding arch -- ");
|
||||
|
Loading…
Reference in New Issue
Block a user