#include "dwarf_i.h"
#include "ucontext_i.h"
#include "unwind_i.h"
static const int WSIZE = sizeof (unw_word_t);
static int
_is_plt_entry (struct dwarf_cursor *c)
{
unw_word_t w0 = 0, w1 = 0;
unw_accessors_t *a;
if (c->as->big_endian)
{
return 0;
}
struct instruction_entry
{
uint32_t pattern;
uint32_t mask;
} instructions[4] =
{
{0x90000010,0x9f00001f}, {0xf9400211,0xffc003ff}, {0x91000210,0xff8003ff}, {0xd61f0220,0xffffffff}, };
a = unw_get_accessors (c->as);
if ((*a->access_mem) (c->as, c->ip, &w0, 0, c->as_arg) < 0)
{
return 0;
}
if ((w0 & instructions[0].mask) == instructions[0].pattern &&
((w0>>32) & instructions[1].mask) == instructions[1].pattern)
{
if ((*a->access_mem) (c->as, c->ip+8, &w1, 0, c->as_arg) >= 0 &&
(w1 & instructions[2].mask) == instructions[2].pattern &&
((w1>>32) & instructions[3].mask) == instructions[3].pattern)
{
return 1;
}
else
{
return 0;
}
}
else if ((w0 & instructions[2].mask) == instructions[2].pattern &&
((w0>>32) & instructions[3].mask) == instructions[3].pattern)
{
w1 = w0;
if ((*a->access_mem) (c->as, c->ip-8, &w0, 0, c->as_arg) >= 0 &&
(w0 & instructions[0].mask) == instructions[0].pattern &&
((w0>>32) & instructions[1].mask) == instructions[1].pattern)
{
return 1;
}
else
{
return 0;
}
}
else if ((w0 & instructions[1].mask) == instructions[1].pattern &&
((w0>>32) & instructions[2].mask) == instructions[2].pattern)
{
if ((*a->access_mem) (c->as, c->ip-4, &w0, 0, c->as_arg) < 0 ||
(*a->access_mem) (c->as, c->ip+4, &w1, 0, c->as_arg) < 0)
{
return 0;
}
}
else if ((w0 & instructions[3].mask) == instructions[3].pattern)
{
if ((*a->access_mem) (c->as, c->ip-12, &w0, 0, c->as_arg) < 0 ||
(*a->access_mem) (c->as, c->ip-4, &w1, 0, c->as_arg) < 0)
{
return 0;
}
}
if ((w0 & instructions[0].mask) == instructions[0].pattern &&
((w0>>32) & instructions[1].mask) == instructions[1].pattern &&
(w1 & instructions[2].mask) == instructions[2].pattern &&
((w1>>32) & instructions[3].mask) == instructions[3].pattern)
{
return 1;
}
else
{
return 0;
}
}
int
unw_is_plt_entry (unw_cursor_t *uc)
{
return _is_plt_entry (&((struct cursor *)uc)->dwarf);
}
typedef enum frame_record_location
{
NONE,
AT_SP_OFFSET,
AT_FP,
NO_PROC_INFO,
} frame_record_location_t;
typedef struct frame_state
{
frame_record_location_t loc;
int32_t offset;
} frame_state_t;
static frame_state_t
get_frame_state (unw_cursor_t *cursor)
{
struct cursor *c = (struct cursor *) cursor;
unw_accessors_t *a = unw_get_accessors (c->dwarf.as);
unw_word_t w, start_ip, ip, offp;
frame_state_t fs;
fs.loc = NO_PROC_INFO;
fs.offset = 0;
if (_is_plt_entry (&c->dwarf))
return fs;
char name[128];
if (((*a->get_proc_name) (c->dwarf.as, c->dwarf.ip, name, sizeof(name), &offp, c->dwarf.as_arg)) != 0)
return fs;
fs.loc = NONE;
start_ip = c->dwarf.ip - offp;
frame_state_t saved_fs;
saved_fs.loc = NONE;
saved_fs.offset = 0;
for (ip = start_ip; ip < c->dwarf.ip; ip += WSIZE)
{
if ((*a->access_mem) (c->dwarf.as, ip, &w, 0, c->dwarf.as_arg) < 0)
return fs;
if (fs.loc == NONE)
{
if ((((w & 0xfe407fff00000000) == 0xa8007bfd00000000) && (c->dwarf.ip > ip + 4))
|| (((w & 0x00000000fe407fff) == 0x00000000a8007bfd) && (c->dwarf.ip > ip)))
{
fs.loc = AT_SP_OFFSET;
if (((w & 0xffc07fff00000000) == 0xa9007bfd00000000) && (c->dwarf.ip > ip + 4))
{
int32_t abs_offset = (w & 0x001f800000000000) >> 47;
fs.offset = ((w & 0x0020000000000000)? -abs_offset : abs_offset) * 8;
}
else if (((w & 0x00000000ffc07fff) == 0x00000000a9007bfd) && (c->dwarf.ip > ip))
{
int32_t abs_offset = (w & 0x00000000001f8000) >> 15;
fs.offset = ((w & 0x0000000000200000)? -abs_offset : abs_offset) * 8;
}
else
fs.offset = 0;
Debug (4, "ip=0x%lx => frame record stored at SP+0x%x\n", ip, fs.offset);
}
}
if (fs.loc == AT_SP_OFFSET)
{
if ((((w & 0xff0003ff00000000) == 0x910003fd00000000) && (c->dwarf.ip > ip + 4))
|| (((w & 0x00000000ff0003ff) == 0x00000000910003fd) && (c->dwarf.ip > ip)))
{
fs.loc = AT_FP;
fs.offset = 0;
Debug (4, "ip=0x%lx => frame record stored at FP\n", ip);
}
}
else if (fs.loc == AT_FP)
{
if ((((w & 0xfe407fff00000000) == 0xa8407bfd00000000) && (c->dwarf.ip > ip + 4))
|| (((w & 0x00000000fe407fff) == 0x00000000a8407bfd) && (c->dwarf.ip > ip)))
{
saved_fs = fs;
fs.loc = NONE;
fs.offset = 0;
Debug (4, "ip=0x%lx => frame record has been loaded, use LR\n", ip);
}
}
if (fs.loc == NONE && saved_fs.loc != NONE)
{
if ((((w & 0xffffffff00000000) == 0xd65f03c000000000) && (c->dwarf.ip > ip + 4))
|| (((w & 0x00000000ffffffff) == 0x00000000d65f03c0) && (c->dwarf.ip > ip)))
{
fs = saved_fs;
saved_fs.loc = NONE;
saved_fs.offset = 0;
Debug (4, "ip=0x%lx => past RET, restoring frame state to loc=%d\n", ip, fs.loc);
}
}
}
Debug (3, "[start_ip = 0x%lx, ip=0x%lx) => loc = %d, offset = %d\n",
start_ip, c->dwarf.ip, fs.loc, fs.offset);
return fs;
}
#if defined __QNX__
static bool is_neg_instr(uint32_t instr)
{
return ((instr & 0xffe003e0) == 0xcb0003e0);
}
static bool is_mov_w8_instr(uint32_t instr)
{
return ((instr & 0xffe00008) == 0x52800008);
}
static bool is_svc_instr(uint32_t instr)
{
return instr == 0xd4000a21;
}
static bool
is_qnx_kercall(struct dwarf_cursor *c)
{
unw_word_t w0;
unw_accessors_t *a;
int ret;
unw_word_t proc_start_ip;
unw_word_t proc_end_ip;
a = unw_get_accessors_int (c->as);
if (c->as->big_endian || !a->get_proc_ip_range)
{
return false;
}
ret = (*a->get_proc_ip_range) (c->as, c->ip, &proc_start_ip, &proc_end_ip, c->as_arg);
if (ret < 0)
{
Debug (2, "ip=0x%lx get proc ip range fail, ret = %d\n", c->ip, ret);
return false;
}
unw_word_t ip = proc_start_ip;
while ((ip < proc_end_ip) && (ip + 8 < proc_end_ip))
{
if ((*a->access_mem) (c->as, ip, &w0, 0, c->as_arg) < 0)
{
Debug (14, "access_mem ip=0x%lx fail\n", ip);
return false;
}
uint32_t low32 = w0 & 0xffffffff;
uint32_t high32 = w0 >> 32;
if (is_mov_w8_instr(low32) && is_svc_instr(high32))
{
return true;
}
if (is_neg_instr(low32) && is_neg_instr(high32))
{
ip += 8;
}
else if (is_neg_instr(low32) && is_mov_w8_instr(high32))
{
ip += 4;
}
else
{
return false;
}
}
return false;
}
#endif
#if defined __linux__
static int
get_sve_vl_signal_loc (struct dwarf_cursor* dwarf, unw_word_t sc_addr)
{
uint32_t size;
unw_addr_space_t as = dwarf->as;
unw_accessors_t *a = unw_get_accessors_int (as);
void *arg = dwarf->as_arg;
unw_word_t res_addr = sc_addr + LINUX_SC_RESERVED_OFF;
for(unw_word_t section_off = 0; section_off < 4096; section_off += size)
{
uint32_t magic;
int ret;
unw_word_t item_addr = res_addr + section_off + LINUX_SC_RESERVED_MAGIC_OFF;
if ((ret = dwarf_readu32(as, a, &item_addr, &magic, arg)) < 0)
return ret;
item_addr = res_addr + section_off + LINUX_SC_RESERVED_SIZE_OFF;
if ((ret = dwarf_readu32(as, a, &item_addr, &size, arg)) < 0)
return ret;
switch(magic)
{
case 0:
return size ? -UNW_EUNSPEC : 1;
case 0x53564501:
if (size < LINUX_SC_RESERVED_SVE_VL_OFF + 2)
return -UNW_EUNSPEC;
dwarf->loc[UNW_AARCH64_VG] = DWARF_MEM_LOC(c->dwarf, res_addr + section_off +
LINUX_SC_RESERVED_SVE_VL_OFF);
return 1;
default:
break;
}
}
return 1;
}
#else
static int
get_sve_vl_signal_loc (struct dwarf_cursor* dwarf, unw_word_t sc_addr)
{
return 1;
}
#endif
static int
aarch64_handle_signal_frame (unw_cursor_t *cursor)
{
struct cursor *c = (struct cursor *) cursor;
int i, ret;
unw_word_t sp_addr = c->dwarf.cfa;
unw_word_t sc_addr;
ret = unw_is_signal_frame (cursor);
if (ret > 0)
{
c->sigcontext_format = SCF_FORMAT;
#if defined(__QNXNTO__)
unw_word_t x19;
ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X19], &x19);
if ((ret < 0) || (x19 == 0UL))
{
return -UNW_EUNSPEC;
}
ret = dwarf_get (&c->dwarf,
DWARF_MEM_LOC (c->dwarf, x19 + SI_UCONTEXT_OFF),
&sc_addr);
if (ret < 0)
{
return -UNW_EUNSPEC;
}
sc_addr += UC_MCONTEXT_OFF;
#else
sc_addr = sp_addr + sizeof (siginfo_t) + UC_MCONTEXT_OFF;
#endif
}
else
return -UNW_EUNSPEC;
c->sigcontext_sp = c->dwarf.cfa;
c->sigcontext_pc = c->dwarf.ip;
c->sigcontext_addr = sc_addr;
c->frame_info.frame_type = UNW_AARCH64_FRAME_SIGRETURN;
c->frame_info.cfa_reg_offset = sc_addr - sp_addr;
for (i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
c->dwarf.loc[i] = DWARF_NULL_LOC;
c->dwarf.loc[UNW_AARCH64_X0] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF);
c->dwarf.loc[UNW_AARCH64_X1] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 1);
c->dwarf.loc[UNW_AARCH64_X2] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 2);
c->dwarf.loc[UNW_AARCH64_X3] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 3);
c->dwarf.loc[UNW_AARCH64_X4] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 4);
c->dwarf.loc[UNW_AARCH64_X5] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 5);
c->dwarf.loc[UNW_AARCH64_X6] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 6);
c->dwarf.loc[UNW_AARCH64_X7] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 7);
c->dwarf.loc[UNW_AARCH64_X8] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 8);
c->dwarf.loc[UNW_AARCH64_X9] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 9);
c->dwarf.loc[UNW_AARCH64_X10] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 10);
c->dwarf.loc[UNW_AARCH64_X11] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 11);
c->dwarf.loc[UNW_AARCH64_X12] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 12);
c->dwarf.loc[UNW_AARCH64_X13] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 13);
c->dwarf.loc[UNW_AARCH64_X14] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 14);
c->dwarf.loc[UNW_AARCH64_X15] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 15);
c->dwarf.loc[UNW_AARCH64_X16] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 16);
c->dwarf.loc[UNW_AARCH64_X17] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 17);
c->dwarf.loc[UNW_AARCH64_X18] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 18);
c->dwarf.loc[UNW_AARCH64_X19] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 19);
c->dwarf.loc[UNW_AARCH64_X20] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 20);
c->dwarf.loc[UNW_AARCH64_X21] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 21);
c->dwarf.loc[UNW_AARCH64_X22] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 22);
c->dwarf.loc[UNW_AARCH64_X23] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 23);
c->dwarf.loc[UNW_AARCH64_X24] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 24);
c->dwarf.loc[UNW_AARCH64_X25] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 25);
c->dwarf.loc[UNW_AARCH64_X26] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 26);
c->dwarf.loc[UNW_AARCH64_X27] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 27);
c->dwarf.loc[UNW_AARCH64_X28] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_GPR_OFF + 8 * 28);
c->dwarf.loc[UNW_AARCH64_X29] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_X29_OFF);
c->dwarf.loc[UNW_AARCH64_X30] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_X30_OFF);
c->dwarf.loc[UNW_AARCH64_SP] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_SP_OFF);
c->dwarf.loc[UNW_AARCH64_PC] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_PC_OFF);
c->dwarf.loc[UNW_AARCH64_PSTATE] = DWARF_MEM_LOC (c->dwarf, sc_addr + SC_PSTATE_OFF);
c->dwarf.loc[UNW_AARCH64_VG] = DWARF_NULL_LOC;
dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_SP], &c->dwarf.cfa);
dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_PC], &c->dwarf.ip);
c->dwarf.pi_valid = 0;
c->dwarf.use_prev_instr = 0;
return get_sve_vl_signal_loc (&c->dwarf, sc_addr);
}
int
unw_step (unw_cursor_t *cursor)
{
struct cursor *c = (struct cursor *) cursor;
int validate = c->validate;
unw_word_t fp = 0;
int ret;
Debug (1, "(cursor=%p, ip=0x%016lx, cfa=0x%016lx))\n",
c, c->dwarf.ip, c->dwarf.cfa);
c->validate = 1;
ret = unw_is_signal_frame (cursor);
Debug(1, "unw_is_signal_frame()=%d\n", ret);
if (ret > 0)
return aarch64_handle_signal_frame (cursor);
else if (unlikely (ret < 0))
{
Debug(1, "Invalid address found in the call stack: 0x%lx\n", c->dwarf.ip);
dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X30], &c->dwarf.ip);
}
c->sigcontext_format = AARCH64_SCF_NONE;
ret = dwarf_step (&c->dwarf);
c->dwarf_step_ret = ret;
c->step_method = UNW_STEP_DWARF;
Debug(1, "dwarf_step()=%d\n", ret);
c->validate = validate;
if (unlikely (ret == -UNW_ESTOPUNWIND))
return ret;
if (likely (ret > 0))
{
ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X29], &fp);
if (ret == 0 && fp == 0)
{
c->dwarf.ip = 0;
Debug (2, "NULL frame pointer X29 loc, returning 0\n");
return 0;
}
}
if (unlikely (ret < 0))
{
if (c->dwarf.as == unw_local_addr_space)
{
c->validate = 1;
}
if (_is_plt_entry (&c->dwarf))
{
Debug (2, "found plt entry\n");
c->frame_info.frame_type = UNW_AARCH64_FRAME_STANDARD;
}
#if defined __QNX__
else if (is_qnx_kercall(&c->dwarf))
{
Debug (2, "found qnx kernel call, fallback to use link register\n");
c->frame_info.frame_type = UNW_AARCH64_FRAME_GUESSED;
}
#endif
else
{
c->frame_info.frame_type = UNW_AARCH64_FRAME_GUESSED;
}
frame_state_t fs = get_frame_state(cursor);
if (fs.loc != NONE && fs.loc != NO_PROC_INFO)
{
if (fs.loc == AT_FP)
{
c->step_method = UNW_STEP_FALLBACK_FP;
ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X29], &fp);
if (unlikely (ret == 0))
{
if (fp == 0)
{
c->dwarf.ip = 0;
Debug (2, "NULL frame pointer X29 loc, returning 0\n");
return 0;
}
}
for (int i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
c->dwarf.loc[i] = DWARF_NULL_LOC;
c->dwarf.loc[UNW_AARCH64_X29] = DWARF_MEM_LOC (c->dwarf, fp);
c->dwarf.loc[UNW_AARCH64_X30] = DWARF_MEM_LOC (c->dwarf, fp + 8);
}
else
{
c->step_method = UNW_STEP_FALLBACK_SP;
unw_word_t sp;
ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_SP], &sp);
if (ret < 0)
return ret;
for (int i = 0; i < DWARF_NUM_PRESERVED_REGS; ++i)
c->dwarf.loc[i] = DWARF_NULL_LOC;
c->frame_info.cfa_reg_offset = fs.offset;
c->frame_info.cfa_reg_sp = 1;
c->dwarf.loc[UNW_AARCH64_X29] = DWARF_MEM_LOC (c->dwarf, sp + fs.offset);
c->dwarf.loc[UNW_AARCH64_X30] = DWARF_MEM_LOC (c->dwarf, sp + fs.offset + 8);
}
c->dwarf.loc[UNW_AARCH64_PC] = c->dwarf.loc[UNW_AARCH64_X30];
ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_X29], &c->dwarf.cfa);
if (ret < 0)
return ret;
ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_PC], &c->dwarf.ip);
if (ret == 0)
{
ret = 1;
}
Debug (2, "fallback, CFA = 0x%016lx, IP = 0x%016lx returning %d\n",
c->dwarf.cfa, c->dwarf.ip, ret);
return ret;
}
if (fs.loc == NO_PROC_INFO)
{
c->step_method = UNW_STEP_FALLBACK_LR_NO_PROC_INFO;
}
else
{
c->step_method = UNW_STEP_FALLBACK_LR;
}
c->loc_info = fs.loc;
c->frame_info.cfa_reg_offset = 0;
c->frame_info.cfa_reg_sp = 0;
c->frame_info.fp_cfa_offset = -1;
c->frame_info.lr_cfa_offset = -1;
c->frame_info.sp_cfa_offset = -1;
c->dwarf.loc[UNW_AARCH64_PC] = c->dwarf.loc[UNW_AARCH64_X30];
c->dwarf.loc[UNW_AARCH64_X30] = DWARF_NULL_LOC;
if (!DWARF_IS_NULL_LOC (c->dwarf.loc[UNW_AARCH64_PC]))
{
ret = dwarf_get (&c->dwarf, c->dwarf.loc[UNW_AARCH64_PC], &c->dwarf.ip);
if (ret < 0)
{
Debug (2, "failed to get pc from link register: %d\n", ret);
return ret;
}
Debug (2, "link register (x30) = 0x%016lx\n", c->dwarf.ip);
ret = 1;
}
else
c->dwarf.ip = 0;
}
return (c->dwarf.ip == 0) ? 0 : 1;
}