mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2024-11-25 08:37:38 +01:00
Rework the lookup of the ARM specific unwind info
Implement routines for finding the proc_info and searching the unwind table for the ARM backend. Signed-off-by: Ken Werner <ken.werner@linaro.org>
This commit is contained in:
parent
0b9f591823
commit
545023c207
9 changed files with 183 additions and 133 deletions
|
@ -285,14 +285,6 @@ struct arm_exidx_entry {
|
|||
uint32_t data;
|
||||
};
|
||||
|
||||
struct arm_exidx_table {
|
||||
const char *name;
|
||||
struct arm_exidx_entry *start;
|
||||
struct arm_exidx_entry *end;
|
||||
unw_word_t start_addr;
|
||||
unw_word_t end_addr;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* no arm-specific auxiliary proc-info */
|
||||
|
|
|
@ -75,7 +75,8 @@ typedef enum
|
|||
{
|
||||
UNW_INFO_FORMAT_DYNAMIC, /* unw_dyn_proc_info_t */
|
||||
UNW_INFO_FORMAT_TABLE, /* unw_dyn_table_t */
|
||||
UNW_INFO_FORMAT_REMOTE_TABLE /* unw_dyn_remote_table_t */
|
||||
UNW_INFO_FORMAT_REMOTE_TABLE, /* unw_dyn_remote_table_t */
|
||||
UNW_INFO_FORMAT_ARM_EXIDX /* ARM specific unwind info */
|
||||
}
|
||||
unw_dyn_info_format_t;
|
||||
|
||||
|
|
|
@ -44,20 +44,10 @@ struct arm_exbuf_data
|
|||
uint32_t data;
|
||||
};
|
||||
|
||||
#define arm_exidx_init_local UNW_OBJ(arm_exidx_init_local)
|
||||
#define arm_exidx_table_add UNW_OBJ(arm_exidx_table_add)
|
||||
#define arm_exidx_table_find UNW_OBJ(arm_exidx_table_find)
|
||||
#define arm_exidx_table_lookup UNW_OBJ(arm_exidx_table_lookup)
|
||||
#define arm_exidx_extract UNW_OBJ(arm_exidx_extract)
|
||||
#define arm_exidx_decode UNW_OBJ(arm_exidx_decode)
|
||||
#define arm_exidx_apply_cmd UNW_OBJ(arm_exidx_apply_cmd)
|
||||
|
||||
int arm_exidx_init_local (void);
|
||||
int arm_exidx_table_add (const char *name, struct arm_exidx_entry *start,
|
||||
struct arm_exidx_entry *end);
|
||||
struct arm_exidx_table *arm_exidx_table_find (unw_word_t pc);
|
||||
struct arm_exidx_entry *arm_exidx_table_lookup (struct arm_exidx_table *table,
|
||||
unw_word_t pc);
|
||||
int arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf);
|
||||
int arm_exidx_decode (const uint8_t *buf, uint8_t len, struct dwarf_cursor *c);
|
||||
int arm_exidx_apply_cmd (struct arm_exbuf_data *edata, struct dwarf_cursor *c);
|
||||
|
|
|
@ -229,9 +229,11 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
|
|||
#define tdep_getcontext_trace unw_getcontext
|
||||
#define tdep_needs_initialization UNW_OBJ(needs_initialization)
|
||||
#define tdep_init UNW_OBJ(init)
|
||||
#define arm_find_proc_info UNW_OBJ(find_proc_info)
|
||||
#define arm_put_unwind_info UNW_OBJ(put_unwind_info)
|
||||
/* Platforms that support UNW_INFO_FORMAT_TABLE need to define
|
||||
tdep_search_unwind_table. */
|
||||
#define tdep_search_unwind_table dwarf_search_unwind_table
|
||||
#define tdep_search_unwind_table UNW_OBJ(search_unwind_table)
|
||||
#define tdep_uc_addr UNW_ARCH_OBJ(uc_addr)
|
||||
#define tdep_get_elf_image UNW_ARCH_OBJ(get_elf_image)
|
||||
#define tdep_access_reg UNW_OBJ(access_reg)
|
||||
|
@ -244,10 +246,10 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
|
|||
|
||||
#ifdef UNW_LOCAL_ONLY
|
||||
# define tdep_find_proc_info(c,ip,n) \
|
||||
dwarf_find_proc_info((c)->as, (ip), &(c)->pi, (n), \
|
||||
arm_find_proc_info((c)->as, (ip), &(c)->pi, (n), \
|
||||
(c)->as_arg)
|
||||
# define tdep_put_unwind_info(as,pi,arg) \
|
||||
dwarf_put_unwind_info((as), (pi), (arg))
|
||||
arm_put_unwind_info((as), (pi), (arg))
|
||||
#else
|
||||
# define tdep_find_proc_info(c,ip,n) \
|
||||
(*(c)->as->acc.find_proc_info)((c)->as, (ip), &(c)->pi, (n), \
|
||||
|
@ -264,6 +266,11 @@ dwarf_put (struct dwarf_cursor *c, dwarf_loc_t loc, unw_word_t val)
|
|||
extern int tdep_needs_initialization;
|
||||
|
||||
extern void tdep_init (void);
|
||||
extern int arm_find_proc_info (unw_addr_space_t as, unw_word_t ip,
|
||||
unw_proc_info_t *pi, int need_unwind_info,
|
||||
void *arg);
|
||||
extern void arm_put_unwind_info (unw_addr_space_t as,
|
||||
unw_proc_info_t *pi, void *arg);
|
||||
extern int tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
|
||||
unw_dyn_info_t *di, unw_proc_info_t *pi,
|
||||
int need_unwind_info, void *arg);
|
||||
|
|
|
@ -33,21 +33,19 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|||
|
||||
#define ARM_EXTBL_OP_FINISH 0xb0
|
||||
|
||||
#define ARM_EXIDX_TABLE_LIMIT 32
|
||||
|
||||
#define arm_exidx_tables UNW_OBJ(arm_exidx_tables)
|
||||
#define arm_exidx_table_count UNW_OBJ(arm_exidx_table_count)
|
||||
#define arm_exidx_table_find UNW_OBJ(arm_exidx_table_find)
|
||||
#define arm_exidx_table_reset_all UNW_OBJ(arm_exidx_table_reset_all)
|
||||
#define arm_exidx_init_local_cb UNW_OBJ(arm_exidx_init_local_cb)
|
||||
|
||||
enum arm_exbuf_cmd_flags {
|
||||
ARM_EXIDX_VFP_SHIFT_16 = 1 << 16,
|
||||
ARM_EXIDX_VFP_DOUBLE = 1 << 17,
|
||||
};
|
||||
|
||||
static struct arm_exidx_table arm_exidx_tables[ARM_EXIDX_TABLE_LIMIT];
|
||||
static unsigned arm_exidx_table_count = 0;
|
||||
struct arm_cb_data
|
||||
{
|
||||
/* in: */
|
||||
unw_word_t ip; /* instruction-pointer we're looking for */
|
||||
unw_proc_info_t *pi; /* proc-info pointer */
|
||||
/* out: */
|
||||
unw_dyn_info_t di; /* info about the ARM exidx segment */
|
||||
};
|
||||
|
||||
static inline uint32_t
|
||||
prel31_read (uint32_t prel31)
|
||||
|
@ -62,68 +60,6 @@ prel31_to_addr (uint32_t *addr)
|
|||
return (uint32_t)addr + offset;
|
||||
}
|
||||
|
||||
static void
|
||||
arm_exidx_table_reset_all (void)
|
||||
{
|
||||
arm_exidx_table_count = 0;
|
||||
}
|
||||
|
||||
HIDDEN int
|
||||
arm_exidx_table_add (const char *name,
|
||||
struct arm_exidx_entry *start, struct arm_exidx_entry *end)
|
||||
{
|
||||
if (arm_exidx_table_count >= ARM_EXIDX_TABLE_LIMIT)
|
||||
return -1;
|
||||
struct arm_exidx_table *table = &arm_exidx_tables[arm_exidx_table_count++];
|
||||
table->name = name;
|
||||
table->start = start;
|
||||
table->end = end;
|
||||
table->start_addr = prel31_to_addr (&start->addr);
|
||||
table->end_addr = prel31_to_addr (&(end - 1)->addr);
|
||||
Debug (2, "name=%s, range=%p-%p, addr=%p-%p\n",
|
||||
name, start, end, (void *)table->start_addr, (void *)table->end_addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locate the appropriate unwind table from the given PC.
|
||||
*/
|
||||
HIDDEN struct arm_exidx_table *
|
||||
arm_exidx_table_find (unw_word_t pc)
|
||||
{
|
||||
struct arm_exidx_table *table;
|
||||
unsigned i;
|
||||
for (i = 0; i < arm_exidx_table_count; i++)
|
||||
{
|
||||
table = &arm_exidx_tables[i];
|
||||
if (pc >= table->start_addr && pc < table->end_addr)
|
||||
return table;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the corresponding arm_exidx_entry from a given index table and PC.
|
||||
*/
|
||||
HIDDEN struct arm_exidx_entry *
|
||||
arm_exidx_table_lookup (struct arm_exidx_table *table, unw_word_t pc)
|
||||
{
|
||||
struct arm_exidx_entry *first = table->start, *last = table->end - 1;
|
||||
if (pc < prel31_to_addr (&first->addr))
|
||||
return NULL;
|
||||
else if (pc >= prel31_to_addr (&last->addr))
|
||||
return last;
|
||||
while (first < last - 1)
|
||||
{
|
||||
struct arm_exidx_entry *mid = first + ((last - first + 1) >> 1);
|
||||
if (pc < prel31_to_addr (&mid->addr))
|
||||
last = mid;
|
||||
else
|
||||
first = mid;
|
||||
}
|
||||
return first;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given command onto the new state to the given dwarf_cursor.
|
||||
*/
|
||||
|
@ -401,39 +337,169 @@ arm_exidx_extract (struct arm_exidx_entry *entry, uint8_t *buf)
|
|||
return nbuf;
|
||||
}
|
||||
|
||||
PROTECTED int
|
||||
tdep_search_unwind_table (unw_addr_space_t as, unw_word_t ip,
|
||||
unw_dyn_info_t *di, unw_proc_info_t *pi,
|
||||
int need_unwind_info, void *arg)
|
||||
{
|
||||
if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX)
|
||||
&& di->format == UNW_INFO_FORMAT_ARM_EXIDX)
|
||||
{
|
||||
struct arm_exidx_entry *first =
|
||||
(struct arm_exidx_entry *) di->u.rti.table_data;
|
||||
struct arm_exidx_entry *last =
|
||||
((struct arm_exidx_entry *) (di->u.rti.table_data +
|
||||
di->u.rti.table_len)) - 1;
|
||||
struct arm_exidx_entry *entry;
|
||||
|
||||
if (ip < prel31_to_addr (&first->addr))
|
||||
return -UNW_ENOINFO;
|
||||
else if (ip >= prel31_to_addr (&last->addr))
|
||||
{
|
||||
entry = last;
|
||||
pi->start_ip = prel31_to_addr (&last->addr);
|
||||
pi->end_ip = di->end_ip - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (first < last - 1)
|
||||
{
|
||||
entry = first + ((last - first + 1) >> 1);
|
||||
if (ip < prel31_to_addr (&entry->addr))
|
||||
last = entry;
|
||||
else
|
||||
first = entry;
|
||||
}
|
||||
|
||||
entry = first;
|
||||
pi->start_ip = prel31_to_addr (&entry->addr);
|
||||
pi->end_ip = prel31_to_addr (&(entry + 1)->addr) - 1;
|
||||
}
|
||||
|
||||
if (need_unwind_info)
|
||||
{
|
||||
pi->unwind_info_size = 8;
|
||||
pi->unwind_info = entry;
|
||||
pi->format = UNW_INFO_FORMAT_ARM_EXIDX;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)
|
||||
&& di->format != UNW_INFO_FORMAT_ARM_EXIDX)
|
||||
return dwarf_search_unwind_table (as, ip, di, pi, need_unwind_info, arg);
|
||||
|
||||
return -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to dl_iterate_phdr to find the unwind tables.
|
||||
* If found, calls arm_exidx_table_add to remember it for later lookups.
|
||||
* Callback to dl_iterate_phdr to find infos about the ARM exidx segment.
|
||||
*/
|
||||
static int
|
||||
arm_exidx_init_local_cb (struct dl_phdr_info *info, size_t size, void *data)
|
||||
arm_phdr_cb (struct dl_phdr_info *info, size_t size, void *data)
|
||||
{
|
||||
unsigned i;
|
||||
struct arm_cb_data *cb_data = data;
|
||||
const Elf_W(Phdr) *p_text = NULL;
|
||||
const Elf_W(Phdr) *p_arm_exidx = NULL;
|
||||
const Elf_W(Phdr) *phdr = info->dlpi_phdr;
|
||||
long n;
|
||||
|
||||
for (i = 0; i < info->dlpi_phnum; i++)
|
||||
for (n = info->dlpi_phnum; --n >= 0; phdr++)
|
||||
{
|
||||
const ElfW (Phdr) *phdr = info->dlpi_phdr + i;
|
||||
if (phdr->p_type != PT_ARM_EXIDX)
|
||||
continue;
|
||||
switch (phdr->p_type)
|
||||
{
|
||||
case PT_LOAD:
|
||||
if (cb_data->ip >= phdr->p_vaddr + info->dlpi_addr &&
|
||||
cb_data->ip < phdr->p_vaddr + info->dlpi_addr + phdr->p_memsz)
|
||||
p_text = phdr;
|
||||
break;
|
||||
|
||||
ElfW (Addr) addr = info->dlpi_addr + phdr->p_vaddr;
|
||||
ElfW (Word) size = phdr->p_filesz;
|
||||
case PT_ARM_EXIDX:
|
||||
p_arm_exidx = phdr;
|
||||
break;
|
||||
|
||||
arm_exidx_table_add (info->dlpi_name,
|
||||
(struct arm_exidx_entry *)addr,
|
||||
(struct arm_exidx_entry *)(addr + size));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p_text && p_arm_exidx)
|
||||
{
|
||||
cb_data->di.format = UNW_INFO_FORMAT_ARM_EXIDX;
|
||||
cb_data->di.start_ip = p_text->p_vaddr + info->dlpi_addr;
|
||||
cb_data->di.end_ip = cb_data->di.start_ip + p_text->p_memsz;
|
||||
cb_data->di.u.rti.name_ptr = (unw_word_t) info->dlpi_name;
|
||||
cb_data->di.u.rti.table_data = p_arm_exidx->p_vaddr + info->dlpi_addr;
|
||||
cb_data->di.u.rti.table_len = p_arm_exidx->p_memsz;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverse the program headers of the executable and its loaded
|
||||
* shared objects to collect the unwind tables.
|
||||
*/
|
||||
HIDDEN int
|
||||
arm_exidx_init_local (void)
|
||||
arm_find_proc_info (unw_addr_space_t as, unw_word_t ip,
|
||||
unw_proc_info_t *pi, int need_unwind_info, void *arg)
|
||||
{
|
||||
arm_exidx_table_reset_all();
|
||||
return dl_iterate_phdr (&arm_exidx_init_local_cb, NULL);
|
||||
int ret = 0;
|
||||
intrmask_t saved_mask;
|
||||
|
||||
Debug (14, "looking for IP=0x%lx\n", (long) ip);
|
||||
|
||||
if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
|
||||
{
|
||||
struct arm_cb_data cb_data;
|
||||
|
||||
memset (&cb_data, 0, sizeof (cb_data));
|
||||
cb_data.ip = ip;
|
||||
cb_data.pi = pi;
|
||||
cb_data.di.format = -1;
|
||||
|
||||
SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask);
|
||||
ret = dl_iterate_phdr (arm_phdr_cb, &cb_data);
|
||||
SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL);
|
||||
|
||||
if (cb_data.di.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &cb_data.di, pi,
|
||||
need_unwind_info, arg);
|
||||
else
|
||||
ret = -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
if (ret < 0 && UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
|
||||
{
|
||||
struct dwarf_callback_data cb_data;
|
||||
|
||||
memset (&cb_data, 0, sizeof (cb_data));
|
||||
cb_data.ip = ip;
|
||||
cb_data.pi = pi;
|
||||
cb_data.need_unwind_info = need_unwind_info;
|
||||
cb_data.di.format = -1;
|
||||
cb_data.di_debug.format = -1;
|
||||
|
||||
SIGPROCMASK (SIG_SETMASK, &unwi_full_mask, &saved_mask);
|
||||
ret = dl_iterate_phdr (dwarf_callback, &cb_data);
|
||||
SIGPROCMASK (SIG_SETMASK, &saved_mask, NULL);
|
||||
|
||||
if (cb_data.single_fde)
|
||||
/* already got the result in *pi */
|
||||
return 0;
|
||||
|
||||
if (cb_data.di_debug.format != -1)
|
||||
ret = tdep_search_unwind_table (as, ip, &cb_data.di_debug, pi,
|
||||
need_unwind_info, arg);
|
||||
else
|
||||
ret = -UNW_ENOINFO;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
Debug (14, "IP=0x%lx not found\n", (long) ip);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
HIDDEN void
|
||||
arm_put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
|
||||
{
|
||||
/* it's a no-op */
|
||||
}
|
||||
|
||||
|
|
|
@ -85,12 +85,6 @@ HIDDEN unw_dyn_info_list_t _U_dyn_info_list;
|
|||
unwind-table entry. Perhaps something similar can be done with
|
||||
DWARF2 unwind info. */
|
||||
|
||||
static void
|
||||
put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
|
||||
{
|
||||
/* it's a no-op */
|
||||
}
|
||||
|
||||
static int
|
||||
get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
|
||||
void *arg)
|
||||
|
@ -193,8 +187,8 @@ arm_local_addr_space_init (void)
|
|||
{
|
||||
memset (&local_addr_space, 0, sizeof (local_addr_space));
|
||||
local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
|
||||
local_addr_space.acc.find_proc_info = dwarf_find_proc_info;
|
||||
local_addr_space.acc.put_unwind_info = put_unwind_info;
|
||||
local_addr_space.acc.find_proc_info = arm_find_proc_info;
|
||||
local_addr_space.acc.put_unwind_info = arm_put_unwind_info;
|
||||
local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
|
||||
local_addr_space.acc.access_mem = access_mem;
|
||||
local_addr_space.acc.access_reg = access_reg;
|
||||
|
|
|
@ -49,9 +49,6 @@ unw_init_local (unw_cursor_t *cursor, ucontext_t *uc)
|
|||
c->dwarf.as = unw_local_addr_space;
|
||||
c->dwarf.as_arg = uc;
|
||||
|
||||
if (UNW_TRY_METHOD (UNW_ARM_METHOD_EXIDX))
|
||||
arm_exidx_init_local ();
|
||||
|
||||
return common_init (c, 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,6 @@ static inline int
|
|||
arm_exidx_step (struct cursor *c)
|
||||
{
|
||||
unw_word_t old_ip, old_cfa;
|
||||
struct arm_exidx_table *table;
|
||||
struct arm_exidx_entry *entry;
|
||||
uint8_t buf[32];
|
||||
int ret;
|
||||
|
@ -46,14 +45,13 @@ arm_exidx_step (struct cursor *c)
|
|||
/* mark PC unsaved */
|
||||
c->dwarf.loc[UNW_ARM_R15] = DWARF_NULL_LOC;
|
||||
|
||||
table = arm_exidx_table_find (c->dwarf.ip);
|
||||
if (NULL == table)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
entry = arm_exidx_table_lookup (table, c->dwarf.ip);
|
||||
if (NULL == entry)
|
||||
if ((ret = tdep_find_proc_info (&c->dwarf, c->dwarf.ip, 1)) < 0)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
if (c->dwarf.pi.format != UNW_INFO_FORMAT_ARM_EXIDX)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
entry = (struct arm_exidx_entry *)c->dwarf.pi.unwind_info;
|
||||
ret = arm_exidx_extract (entry, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
|
@ -423,6 +423,11 @@ fetch_proc_info (struct dwarf_cursor *c, unw_word_t ip, int need_unwind_info)
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (c->pi.format != UNW_INFO_FORMAT_DYNAMIC
|
||||
&& c->pi.format != UNW_INFO_FORMAT_TABLE
|
||||
&& c->pi.format != UNW_INFO_FORMAT_REMOTE_TABLE)
|
||||
return -UNW_ENOINFO;
|
||||
|
||||
c->pi_valid = 1;
|
||||
c->pi_is_dynamic = dynamic;
|
||||
|
||||
|
|
Loading…
Reference in a new issue