+ /* SP is generally not saved to the stack, but this frame is
+ identified by the next frame's stack pointer at the time of the call.
+ The value was already reconstructed into PREV_SP. */
+ if (prev_regnum == ARM_SP_REGNUM)
+ return frame_unwind_got_constant (this_frame, prev_regnum, cache->prev_sp);
+
+ /* The CPSR may have been changed by the call instruction and by the
+ called function. The only bit we can reconstruct is the T bit,
+ by checking the low bit of LR as of the call. This is a reliable
+ indicator of Thumb-ness except for some ARM v4T pre-interworking
+ Thumb code, which could get away with a clear low bit as long as
+ the called function did not use bx. Guess that all other
+ bits are unchanged; the condition flags are presumably lost,
+ but the processor status is likely valid. */
+ if (prev_regnum == ARM_PS_REGNUM)
+ {
+ CORE_ADDR lr, cpsr;
+ ULONGEST t_bit = arm_psr_thumb_bit (gdbarch);
+
+ cpsr = get_frame_register_unsigned (this_frame, prev_regnum);
+ lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM);
+ if (IS_THUMB_ADDR (lr))
+ cpsr |= t_bit;
+ else
+ cpsr &= ~t_bit;
+ return frame_unwind_got_constant (this_frame, prev_regnum, cpsr);
+ }
+
+ return trad_frame_get_prev_register (this_frame, cache->saved_regs,
+ prev_regnum);
+}
+
+struct frame_unwind arm_prologue_unwind = {
+ NORMAL_FRAME,
+ arm_prologue_this_id,
+ arm_prologue_prev_register,
+ NULL,
+ 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. */