* arm-tdep.c: Include "observer.h".

(arm_prologue_this_id): Use frame PC if get_frame_func returns 0.
	(arm_exidx_data_key): New static variable.
	(struct arm_exidx_entry, arm_exidx_entry_s): New data types.
	(struct arm_exidx_data): Likewise.
	(arm_exidx_data_free): New function.
	(arm_compare_exidx_entries): Likewise.
	(arm_obj_section_from_vma): Likewise.
	(arm_exidx_new_objfile): Likewise.
	(arm_find_exidx_entry): Likewise.
	(arm_exidx_fill_cache): Likewise.
	(arm_exidx_unwind_sniffer): Likewise.
	(arm_exidx_unwind): New global variable.
	(arm_gdbarch_init): Append unwinder arm_exidx_unwind.
	(_initialize_arm_tdep): Attach arm_exidx_new_objfile to new_objfile
	observer.  Register arm_exidx_data_key as objfile data.
This commit is contained in:
Ulrich Weigand 2011-02-02 19:44:44 +00:00
parent 2e9e421ffe
commit 0e9e9abd6f
2 changed files with 770 additions and 0 deletions

View File

@ -1,3 +1,22 @@
2011-02-02 Ulrich Weigand <ulrich.weigand@linaro.org>
* arm-tdep.c: Include "observer.h".
(arm_prologue_this_id): Use frame PC if get_frame_func returns 0.
(arm_exidx_data_key): New static variable.
(struct arm_exidx_entry, arm_exidx_entry_s): New data types.
(struct arm_exidx_data): Likewise.
(arm_exidx_data_free): New function.
(arm_compare_exidx_entries): Likewise.
(arm_obj_section_from_vma): Likewise.
(arm_exidx_new_objfile): Likewise.
(arm_find_exidx_entry): Likewise.
(arm_exidx_fill_cache): Likewise.
(arm_exidx_unwind_sniffer): Likewise.
(arm_exidx_unwind): New global variable.
(arm_gdbarch_init): Append unwinder arm_exidx_unwind.
(_initialize_arm_tdep): Attach arm_exidx_new_objfile to new_objfile
observer. Register arm_exidx_data_key as objfile data.
2011-02-02 Ulrich Weigand <ulrich.weigand@linaro.org>
* arm-tdep.c (arm_analyze_load_stack_chk_guard): Avoid build break

View File

@ -43,6 +43,7 @@
#include "prologue-value.h"
#include "target-descriptions.h"
#include "user-regs.h"
#include "observer.h"
#include "arm-tdep.h"
#include "gdb/sim-arm.h"
@ -2042,7 +2043,13 @@ arm_prologue_this_id (struct frame_info *this_frame,
if (cache->prev_sp == 0)
return;
/* Use function start address as part of the frame ID. If we cannot
identify the start address (due to missing symbol information),
fall back to just using the current PC. */
func = get_frame_func (this_frame);
if (!func)
func = pc;
id = frame_id_build (cache->prev_sp, func);
*this_id = id;
}
@ -2113,6 +2120,744 @@ struct frame_unwind arm_prologue_unwind = {
default_frame_sniffer
};
/* Maintain a list of ARM exception table entries per objfile, similar to the
list of mapping symbols. We only cache entries for standard ARM-defined
personality routines; the cache will contain only the frame unwinding
instructions associated with the entry (not the descriptors). */
static const struct objfile_data *arm_exidx_data_key;
struct arm_exidx_entry
{
bfd_vma addr;
gdb_byte *entry;
};
typedef struct arm_exidx_entry arm_exidx_entry_s;
DEF_VEC_O(arm_exidx_entry_s);
struct arm_exidx_data
{
VEC(arm_exidx_entry_s) **section_maps;
};
static void
arm_exidx_data_free (struct objfile *objfile, void *arg)
{
struct arm_exidx_data *data = arg;
unsigned int i;
for (i = 0; i < objfile->obfd->section_count; i++)
VEC_free (arm_exidx_entry_s, data->section_maps[i]);
}
static inline int
arm_compare_exidx_entries (const struct arm_exidx_entry *lhs,
const struct arm_exidx_entry *rhs)
{
return lhs->addr < rhs->addr;
}
static struct obj_section *
arm_obj_section_from_vma (struct objfile *objfile, bfd_vma vma)
{
struct obj_section *osect;
ALL_OBJFILE_OSECTIONS (objfile, osect)
if (bfd_get_section_flags (objfile->obfd,
osect->the_bfd_section) & SEC_ALLOC)
{
bfd_vma start, size;
start = bfd_get_section_vma (objfile->obfd, osect->the_bfd_section);
size = bfd_get_section_size (osect->the_bfd_section);
if (start <= vma && vma < start + size)
return osect;
}
return NULL;
}
/* Parse contents of exception table and exception index sections
of OBJFILE, and fill in the exception table entry cache.
For each entry that refers to a standard ARM-defined personality
routine, extract the frame unwinding instructions (from either
the index or the table section). The unwinding instructions
are normalized by:
- extracting them from the rest of the table data
- converting to host endianness
- appending the implicit 0xb0 ("Finish") code
The extracted and normalized instructions are stored for later
retrieval by the arm_find_exidx_entry routine. */
static void
arm_exidx_new_objfile (struct objfile *objfile)
{
struct cleanup *cleanups = make_cleanup (null_cleanup, NULL);
struct arm_exidx_data *data;
asection *exidx, *extab;
bfd_vma exidx_vma = 0, extab_vma = 0;
bfd_size_type exidx_size = 0, extab_size = 0;
gdb_byte *exidx_data = NULL, *extab_data = NULL;
LONGEST i;
/* If we've already touched this file, do nothing. */
if (!objfile || objfile_data (objfile, arm_exidx_data_key) != NULL)
return;
/* Read contents of exception table and index. */
exidx = bfd_get_section_by_name (objfile->obfd, ".ARM.exidx");
if (exidx)
{
exidx_vma = bfd_section_vma (objfile->obfd, exidx);
exidx_size = bfd_get_section_size (exidx);
exidx_data = xmalloc (exidx_size);
make_cleanup (xfree, exidx_data);
if (!bfd_get_section_contents (objfile->obfd, exidx,
exidx_data, 0, exidx_size))
{
do_cleanups (cleanups);
return;
}
}
extab = bfd_get_section_by_name (objfile->obfd, ".ARM.extab");
if (extab)
{
extab_vma = bfd_section_vma (objfile->obfd, extab);
extab_size = bfd_get_section_size (extab);
extab_data = xmalloc (extab_size);
make_cleanup (xfree, extab_data);
if (!bfd_get_section_contents (objfile->obfd, extab,
extab_data, 0, extab_size))
{
do_cleanups (cleanups);
return;
}
}
/* Allocate exception table data structure. */
data = OBSTACK_ZALLOC (&objfile->objfile_obstack, struct arm_exidx_data);
set_objfile_data (objfile, arm_exidx_data_key, data);
data->section_maps = OBSTACK_CALLOC (&objfile->objfile_obstack,
objfile->obfd->section_count,
VEC(arm_exidx_entry_s) *);
/* Fill in exception table. */
for (i = 0; i < exidx_size / 8; i++)
{
struct arm_exidx_entry new_exidx_entry;
bfd_vma idx = bfd_h_get_32 (objfile->obfd, exidx_data + i * 8);
bfd_vma val = bfd_h_get_32 (objfile->obfd, exidx_data + i * 8 + 4);
bfd_vma addr = 0, word = 0;
int n_bytes = 0, n_words = 0;
struct obj_section *sec;
gdb_byte *entry = NULL;
/* Extract address of start of function. */
idx = ((idx & 0x7fffffff) ^ 0x40000000) - 0x40000000;
idx += exidx_vma + i * 8;
/* Find section containing function and compute section offset. */
sec = arm_obj_section_from_vma (objfile, idx);
if (sec == NULL)
continue;
idx -= bfd_get_section_vma (objfile->obfd, sec->the_bfd_section);
/* Determine address of exception table entry. */
if (val == 1)
{
/* EXIDX_CANTUNWIND -- no exception table entry present. */
}
else if ((val & 0xff000000) == 0x80000000)
{
/* Exception table entry embedded in .ARM.exidx
-- must be short form. */
word = val;
n_bytes = 3;
}
else if (!(val & 0x80000000))
{
/* Exception table entry in .ARM.extab. */
addr = ((val & 0x7fffffff) ^ 0x40000000) - 0x40000000;
addr += exidx_vma + i * 8 + 4;
if (addr >= extab_vma && addr + 4 <= extab_vma + extab_size)
{
word = bfd_h_get_32 (objfile->obfd,
extab_data + addr - extab_vma);
addr += 4;
if ((word & 0xff000000) == 0x80000000)
{
/* Short form. */
n_bytes = 3;
}
else if ((word & 0xff000000) == 0x81000000
|| (word & 0xff000000) == 0x82000000)
{
/* Long form. */
n_bytes = 2;
n_words = ((word >> 16) & 0xff);
}
else if (!(word & 0x80000000))
{
bfd_vma pers;
struct obj_section *pers_sec;
int gnu_personality = 0;
/* Custom personality routine. */
pers = ((word & 0x7fffffff) ^ 0x40000000) - 0x40000000;
pers = UNMAKE_THUMB_ADDR (pers + addr - 4);
/* Check whether we've got one of the variants of the
GNU personality routines. */
pers_sec = arm_obj_section_from_vma (objfile, pers);
if (pers_sec)
{
static const char *personality[] =
{
"__gcc_personality_v0",
"__gxx_personality_v0",
"__gcj_personality_v0",
"__gnu_objc_personality_v0",
NULL
};
CORE_ADDR pc = pers + obj_section_offset (pers_sec);
int k;
for (k = 0; personality[k]; k++)
if (lookup_minimal_symbol_by_pc_name
(pc, personality[k], objfile))
{
gnu_personality = 1;
break;
}
}
/* If so, the next word contains a word count in the high
byte, followed by the same unwind instructions as the
pre-defined forms. */
if (gnu_personality
&& addr + 4 <= extab_vma + extab_size)
{
word = bfd_h_get_32 (objfile->obfd,
extab_data + addr - extab_vma);
addr += 4;
n_bytes = 3;
n_words = ((word >> 24) & 0xff);
}
}
}
}
/* Sanity check address. */
if (n_words)
if (addr < extab_vma || addr + 4 * n_words > extab_vma + extab_size)
n_words = n_bytes = 0;
/* The unwind instructions reside in WORD (only the N_BYTES least
significant bytes are valid), followed by N_WORDS words in the
extab section starting at ADDR. */
if (n_bytes || n_words)
{
gdb_byte *p = entry = obstack_alloc (&objfile->objfile_obstack,
n_bytes + n_words * 4 + 1);
while (n_bytes--)
*p++ = (gdb_byte) ((word >> (8 * n_bytes)) & 0xff);
while (n_words--)
{
word = bfd_h_get_32 (objfile->obfd,
extab_data + addr - extab_vma);
addr += 4;
*p++ = (gdb_byte) ((word >> 24) & 0xff);
*p++ = (gdb_byte) ((word >> 16) & 0xff);
*p++ = (gdb_byte) ((word >> 8) & 0xff);
*p++ = (gdb_byte) (word & 0xff);
}
/* Implied "Finish" to terminate the list. */
*p++ = 0xb0;
}
/* Push entry onto vector. They are guaranteed to always
appear in order of increasing addresses. */
new_exidx_entry.addr = idx;
new_exidx_entry.entry = entry;
VEC_safe_push (arm_exidx_entry_s,
data->section_maps[sec->the_bfd_section->index],
&new_exidx_entry);
}
do_cleanups (cleanups);
}
/* Search for the exception table entry covering MEMADDR. If one is found,
return a pointer to its data. Otherwise, return 0. If START is non-NULL,
set *START to the start of the region covered by this entry. */
static gdb_byte *
arm_find_exidx_entry (CORE_ADDR memaddr, CORE_ADDR *start)
{
struct obj_section *sec;
sec = find_pc_section (memaddr);
if (sec != NULL)
{
struct arm_exidx_data *data;
VEC(arm_exidx_entry_s) *map;
struct arm_exidx_entry map_key = { memaddr - obj_section_addr (sec), 0 };
unsigned int idx;
data = objfile_data (sec->objfile, arm_exidx_data_key);
if (data != NULL)
{
map = data->section_maps[sec->the_bfd_section->index];
if (!VEC_empty (arm_exidx_entry_s, map))
{
struct arm_exidx_entry *map_sym;
idx = VEC_lower_bound (arm_exidx_entry_s, map, &map_key,
arm_compare_exidx_entries);
/* VEC_lower_bound finds the earliest ordered insertion
point. If the following symbol starts at this exact
address, we use that; otherwise, the preceding
exception table entry covers this address. */
if (idx < VEC_length (arm_exidx_entry_s, map))
{
map_sym = VEC_index (arm_exidx_entry_s, map, idx);
if (map_sym->addr == map_key.addr)
{
if (start)
*start = map_sym->addr + obj_section_addr (sec);
return map_sym->entry;
}
}
if (idx > 0)
{
map_sym = VEC_index (arm_exidx_entry_s, map, idx - 1);
if (start)
*start = map_sym->addr + obj_section_addr (sec);
return map_sym->entry;
}
}
}
}
return NULL;
}
/* Given the current frame THIS_FRAME, and its associated frame unwinding
instruction list from the ARM exception table entry ENTRY, allocate and
return a prologue cache structure describing how to unwind this frame.
Return NULL if the unwinding instruction list contains a "spare",
"reserved" or "refuse to unwind" instruction as defined in section
"9.3 Frame unwinding instructions" of the "Exception Handling ABI
for the ARM Architecture" document. */
static struct arm_prologue_cache *
arm_exidx_fill_cache (struct frame_info *this_frame, gdb_byte *entry)
{
CORE_ADDR vsp = 0;
int vsp_valid = 0;
struct arm_prologue_cache *cache;
cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
for (;;)
{
gdb_byte insn;
/* Whenever we reload SP, we actually have to retrieve its
actual value in the current frame. */
if (!vsp_valid)
{
if (trad_frame_realreg_p (cache->saved_regs, ARM_SP_REGNUM))
{
int reg = cache->saved_regs[ARM_SP_REGNUM].realreg;
vsp = get_frame_register_unsigned (this_frame, reg);
}
else
{
CORE_ADDR addr = cache->saved_regs[ARM_SP_REGNUM].addr;
vsp = get_frame_memory_unsigned (this_frame, addr, 4);
}
vsp_valid = 1;
}
/* Decode next unwind instruction. */
insn = *entry++;
if ((insn & 0xc0) == 0)
{
int offset = insn & 0x3f;
vsp += (offset << 2) + 4;
}
else if ((insn & 0xc0) == 0x40)
{
int offset = insn & 0x3f;
vsp -= (offset << 2) + 4;
}
else if ((insn & 0xf0) == 0x80)
{
int mask = ((insn & 0xf) << 8) | *entry++;
int i;
/* The special case of an all-zero mask identifies
"Refuse to unwind". We return NULL to fall back
to the prologue analyzer. */
if (mask == 0)
return NULL;
/* Pop registers r4..r15 under mask. */
for (i = 0; i < 12; i++)
if (mask & (1 << i))
{
cache->saved_regs[4 + i].addr = vsp;
vsp += 4;
}
/* Special-case popping SP -- we need to reload vsp. */
if (mask & (1 << (ARM_SP_REGNUM - 4)))
vsp_valid = 0;
}
else if ((insn & 0xf0) == 0x90)
{
int reg = insn & 0xf;
/* Reserved cases. */
if (reg == ARM_SP_REGNUM || reg == ARM_PC_REGNUM)
return NULL;
/* Set SP from another register and mark VSP for reload. */
cache->saved_regs[ARM_SP_REGNUM] = cache->saved_regs[reg];
vsp_valid = 0;
}
else if ((insn & 0xf0) == 0xa0)
{
int count = insn & 0x7;
int pop_lr = (insn & 0x8) != 0;
int i;
/* Pop r4..r[4+count]. */
for (i = 0; i <= count; i++)
{
cache->saved_regs[4 + i].addr = vsp;
vsp += 4;
}
/* If indicated by flag, pop LR as well. */
if (pop_lr)
{
cache->saved_regs[ARM_LR_REGNUM].addr = vsp;
vsp += 4;
}
}
else if (insn == 0xb0)
{
/* We could only have updated PC by popping into it; if so, it
will show up as address. Otherwise, copy LR into PC. */
if (!trad_frame_addr_p (cache->saved_regs, ARM_PC_REGNUM))
cache->saved_regs[ARM_PC_REGNUM]
= cache->saved_regs[ARM_LR_REGNUM];
/* We're done. */
break;
}
else if (insn == 0xb1)
{
int mask = *entry++;
int i;
/* All-zero mask and mask >= 16 is "spare". */
if (mask == 0 || mask >= 16)
return NULL;
/* Pop r0..r3 under mask. */
for (i = 0; i < 4; i++)
if (mask & (1 << i))
{
cache->saved_regs[i].addr = vsp;
vsp += 4;
}
}
else if (insn == 0xb2)
{
ULONGEST offset = 0;
unsigned shift = 0;
do
{
offset |= (*entry & 0x7f) << shift;
shift += 7;
}
while (*entry++ & 0x80);
vsp += 0x204 + (offset << 2);
}
else if (insn == 0xb3)
{
int start = *entry >> 4;
int count = (*entry++) & 0xf;
int i;
/* Only registers D0..D15 are valid here. */
if (start + count >= 16)
return NULL;
/* Pop VFP double-precision registers D[start]..D[start+count]. */
for (i = 0; i <= count; i++)
{
cache->saved_regs[ARM_D0_REGNUM + start + i].addr = vsp;
vsp += 8;
}
/* Add an extra 4 bytes for FSTMFDX-style stack. */
vsp += 4;
}
else if ((insn & 0xf8) == 0xb8)
{
int count = insn & 0x7;
int i;
/* Pop VFP double-precision registers D[8]..D[8+count]. */
for (i = 0; i <= count; i++)
{
cache->saved_regs[ARM_D0_REGNUM + 8 + i].addr = vsp;
vsp += 8;
}
/* Add an extra 4 bytes for FSTMFDX-style stack. */
vsp += 4;
}
else if (insn == 0xc6)
{
int start = *entry >> 4;
int count = (*entry++) & 0xf;
int i;
/* Only registers WR0..WR15 are valid. */
if (start + count >= 16)
return NULL;
/* Pop iwmmx registers WR[start]..WR[start+count]. */
for (i = 0; i <= count; i++)
{
cache->saved_regs[ARM_WR0_REGNUM + start + i].addr = vsp;
vsp += 8;
}
}
else if (insn == 0xc7)
{
int mask = *entry++;
int i;
/* All-zero mask and mask >= 16 is "spare". */
if (mask == 0 || mask >= 16)
return NULL;
/* Pop iwmmx general-purpose registers WCGR0..WCGR3 under mask. */
for (i = 0; i < 4; i++)
if (mask & (1 << i))
{
cache->saved_regs[ARM_WCGR0_REGNUM + i].addr = vsp;
vsp += 4;
}
}
else if ((insn & 0xf8) == 0xc0)
{
int count = insn & 0x7;
int i;
/* Pop iwmmx registers WR[10]..WR[10+count]. */
for (i = 0; i <= count; i++)
{
cache->saved_regs[ARM_WR0_REGNUM + 10 + i].addr = vsp;
vsp += 8;
}
}
else if (insn == 0xc8)
{
int start = *entry >> 4;
int count = (*entry++) & 0xf;
int i;
/* Only registers D0..D31 are valid. */
if (start + count >= 16)
return NULL;
/* Pop VFP double-precision registers
D[16+start]..D[16+start+count]. */
for (i = 0; i <= count; i++)
{
cache->saved_regs[ARM_D0_REGNUM + 16 + start + i].addr = vsp;
vsp += 8;
}
}
else if (insn == 0xc9)
{
int start = *entry >> 4;
int count = (*entry++) & 0xf;
int i;
/* Pop VFP double-precision registers D[start]..D[start+count]. */
for (i = 0; i <= count; i++)
{
cache->saved_regs[ARM_D0_REGNUM + start + i].addr = vsp;
vsp += 8;
}
}
else if ((insn & 0xf8) == 0xd0)
{
int count = insn & 0x7;
int i;
/* Pop VFP double-precision registers D[8]..D[8+count]. */
for (i = 0; i <= count; i++)
{
cache->saved_regs[ARM_D0_REGNUM + 8 + i].addr = vsp;
vsp += 8;
}
}
else
{
/* Everything else is "spare". */
return NULL;
}
}
/* If we restore SP from a register, assume this was the frame register.
Otherwise just fall back to SP as frame register. */
if (trad_frame_realreg_p (cache->saved_regs, ARM_SP_REGNUM))
cache->framereg = cache->saved_regs[ARM_SP_REGNUM].realreg;
else
cache->framereg = ARM_SP_REGNUM;
/* Determine offset to previous frame. */
cache->framesize
= vsp - get_frame_register_unsigned (this_frame, cache->framereg);
/* We already got the previous SP. */
cache->prev_sp = vsp;
return cache;
}
/* Unwinding via ARM exception table entries. Note that the sniffer
already computes a filled-in prologue cache, which is then used
with the same arm_prologue_this_id and arm_prologue_prev_register
routines also used for prologue-parsing based unwinding. */
static int
arm_exidx_unwind_sniffer (const struct frame_unwind *self,
struct frame_info *this_frame,
void **this_prologue_cache)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch);
CORE_ADDR addr_in_block, exidx_region, func_start;
struct arm_prologue_cache *cache;
gdb_byte *entry;
/* See if we have an ARM exception table entry covering this address. */
addr_in_block = get_frame_address_in_block (this_frame);
entry = arm_find_exidx_entry (addr_in_block, &exidx_region);
if (!entry)
return 0;
/* The ARM exception table does not describe unwind information
for arbitrary PC values, but is guaranteed to be correct only
at call sites. We have to decide here whether we want to use
ARM exception table information for this frame, or fall back
to using prologue parsing. (Note that if we have DWARF CFI,
this sniffer isn't even called -- CFI is always preferred.)
Before we make this decision, however, we check whether we
actually have *symbol* information for the current frame.
If not, prologue parsing would not work anyway, so we might
as well use the exception table and hope for the best. */
if (find_pc_partial_function (addr_in_block, NULL, &func_start, NULL))
{
int exc_valid = 0;
/* If the next frame is "normal", we are at a call site in this
frame, so exception information is guaranteed to be valid. */
if (get_next_frame (this_frame)
&& get_frame_type (get_next_frame (this_frame)) == NORMAL_FRAME)
exc_valid = 1;
/* We also assume exception information is valid if we're currently
blocked in a system call. The system library is supposed to
ensure this, so that e.g. pthread cancellation works. */
if (arm_frame_is_thumb (this_frame))
{
LONGEST insn;
if (safe_read_memory_integer (get_frame_pc (this_frame) - 2, 2,
byte_order_for_code, &insn)
&& (insn & 0xff00) == 0xdf00 /* svc */)
exc_valid = 1;
}
else
{
LONGEST insn;
if (safe_read_memory_integer (get_frame_pc (this_frame) - 4, 4,
byte_order_for_code, &insn)
&& (insn & 0x0f000000) == 0x0f000000 /* svc */)
exc_valid = 1;
}
/* Bail out if we don't know that exception information is valid. */
if (!exc_valid)
return 0;
/* The ARM exception index does not mark the *end* of the region
covered by the entry, and some functions will not have any entry.
To correctly recognize the end of the covered region, the linker
should have inserted dummy records with a CANTUNWIND marker.
Unfortunately, current versions of GNU ld do not reliably do
this, and thus we may have found an incorrect entry above.
As a (temporary) sanity check, we only use the entry if it
lies *within* the bounds of the function. Note that this check
might reject perfectly valid entries that just happen to cover
multiple functions; therefore this check ought to be removed
once the linker is fixed. */
if (func_start > exidx_region)
return 0;
}
/* Decode the list of unwinding instructions into a prologue cache.
Note that this may fail due to e.g. a "refuse to unwind" code. */
cache = arm_exidx_fill_cache (this_frame, entry);
if (!cache)
return 0;
*this_prologue_cache = cache;
return 1;
}
struct frame_unwind arm_exidx_unwind = {
NORMAL_FRAME,
arm_prologue_this_id,
arm_prologue_prev_register,
NULL,
arm_exidx_unwind_sniffer
};
static struct arm_prologue_cache *
arm_make_stub_cache (struct frame_info *this_frame)
{
@ -7751,6 +8496,7 @@ arm_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
/* Add some default predicates. */
frame_unwind_append_unwinder (gdbarch, &arm_stub_unwind);
dwarf2_append_unwinders (gdbarch);
frame_unwind_append_unwinder (gdbarch, &arm_exidx_unwind);
frame_unwind_append_unwinder (gdbarch, &arm_prologue_unwind);
/* Now we have tuned the configuration, set a few final things,
@ -7853,6 +8599,11 @@ _initialize_arm_tdep (void)
arm_objfile_data_key
= register_objfile_data_with_cleanup (NULL, arm_objfile_data_free);
/* Add ourselves to objfile event chain. */
observer_attach_new_objfile (arm_exidx_new_objfile);
arm_exidx_data_key
= register_objfile_data_with_cleanup (NULL, arm_exidx_data_free);
/* Register an ELF OS ABI sniffer for ARM binaries. */
gdbarch_register_osabi_sniffer (bfd_arch_arm,
bfd_target_elf_flavour,