/* Common target dependent code for GDB on ARM systems.
Copyright (C) 1988, 1989, 1991, 1992, 1993, 1995, 1996, 1998, 1999, 2000,
- 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
+ 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ Free Software Foundation, Inc.
This file is part of GDB.
#include "elf/arm.h"
#include "gdb_assert.h"
+#include "vec.h"
static int arm_debug;
#define MSYMBOL_IS_SPECIAL(msym) \
(((long) MSYMBOL_INFO (msym) & 0x80000000) != 0)
+/* Per-objfile data used for mapping symbols. */
+static const struct objfile_data *arm_objfile_data_key;
+
+struct arm_mapping_symbol
+{
+ bfd_vma value;
+ char type;
+};
+typedef struct arm_mapping_symbol arm_mapping_symbol_s;
+DEF_VEC_O(arm_mapping_symbol_s);
+
+struct arm_per_objfile
+{
+ VEC(arm_mapping_symbol_s) **section_maps;
+};
+
/* The list of available "set arm ..." and "show arm ..." commands. */
static struct cmd_list_element *setarmcmdlist = NULL;
static struct cmd_list_element *showarmcmdlist = NULL;
static enum arm_abi_kind arm_abi_global = ARM_ABI_AUTO;
static const char *arm_abi_string = "auto";
+/* The execution mode to assume. */
+static const char *arm_mode_strings[] =
+ {
+ "auto",
+ "arm",
+ "thumb"
+ };
+
+static const char *arm_fallback_mode_string = "auto";
+static const char *arm_force_mode_string = "auto";
+
/* Number of different reg name sets (options). */
static int num_disassembly_options;
static void set_disassembly_style (void);
static void convert_from_extended (const struct floatformat *, const void *,
- void *);
+ void *, int);
static void convert_to_extended (const struct floatformat *, void *,
- const void *);
+ const void *, int);
struct arm_prologue_cache
{
to identify this frame. */
CORE_ADDR prev_sp;
- /* The frame base for this frame is just prev_sp + frame offset -
- frame size. FRAMESIZE is the size of this stack frame, and
- FRAMEOFFSET if the initial offset from the stack pointer (this
- frame's stack pointer, not PREV_SP) to the frame base. */
+ /* The frame base for this frame is just prev_sp - frame size.
+ FRAMESIZE is the distance from the frame pointer to the
+ initial stack pointer. */
int framesize;
- int frameoffset;
/* The register used to hold the frame pointer for this frame. */
int framereg;
int arm_apcs_32 = 1;
+/* Determine if FRAME is executing in Thumb mode. */
+
+static int
+arm_frame_is_thumb (struct frame_info *frame)
+{
+ CORE_ADDR cpsr;
+
+ /* Every ARM frame unwinder can unwind the T bit of the CPSR, either
+ directly (from a signal frame or dummy frame) or by interpreting
+ the saved LR (from a prologue or DWARF frame). So consult it and
+ trust the unwinders. */
+ cpsr = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
+
+ return (cpsr & CPSR_T) != 0;
+}
+
+/* Callback for VEC_lower_bound. */
+
+static inline int
+arm_compare_mapping_symbols (const struct arm_mapping_symbol *lhs,
+ const struct arm_mapping_symbol *rhs)
+{
+ return lhs->value < rhs->value;
+}
+
/* Determine if the program counter specified in MEMADDR is in a Thumb
- function. */
+ function. This function should be called for addresses unrelated to
+ any executing frame; otherwise, prefer arm_frame_is_thumb. */
static int
arm_pc_is_thumb (CORE_ADDR memaddr)
{
+ struct obj_section *sec;
struct minimal_symbol *sym;
/* If bit 0 of the address is set, assume this is a Thumb address. */
if (IS_THUMB_ADDR (memaddr))
return 1;
+ /* If the user wants to override the symbol table, let him. */
+ if (strcmp (arm_force_mode_string, "arm") == 0)
+ return 0;
+ if (strcmp (arm_force_mode_string, "thumb") == 0)
+ return 1;
+
+ /* If there are mapping symbols, consult them. */
+ sec = find_pc_section (memaddr);
+ if (sec != NULL)
+ {
+ struct arm_per_objfile *data;
+ VEC(arm_mapping_symbol_s) *map;
+ struct arm_mapping_symbol map_key = { memaddr - sec->addr, 0 };
+ unsigned int idx;
+
+ data = objfile_data (sec->objfile, arm_objfile_data_key);
+ if (data != NULL)
+ {
+ map = data->section_maps[sec->the_bfd_section->index];
+ if (!VEC_empty (arm_mapping_symbol_s, map))
+ {
+ struct arm_mapping_symbol *map_sym;
+
+ idx = VEC_lower_bound (arm_mapping_symbol_s, map, &map_key,
+ arm_compare_mapping_symbols);
+
+ /* VEC_lower_bound finds the earliest ordered insertion
+ point. If the following symbol starts at this exact
+ address, we use that; otherwise, the preceding
+ mapping symbol covers this address. */
+ if (idx < VEC_length (arm_mapping_symbol_s, map))
+ {
+ map_sym = VEC_index (arm_mapping_symbol_s, map, idx);
+ if (map_sym->value == map_key.value)
+ return map_sym->type == 't';
+ }
+
+ if (idx > 0)
+ {
+ map_sym = VEC_index (arm_mapping_symbol_s, map, idx - 1);
+ return map_sym->type == 't';
+ }
+ }
+ }
+ }
+
/* Thumb functions have a "special" bit set in minimal symbols. */
sym = lookup_minimal_symbol_by_pc (memaddr);
if (sym)
- {
- return (MSYMBOL_IS_SPECIAL (sym));
- }
- else
- {
- return 0;
- }
+ return (MSYMBOL_IS_SPECIAL (sym));
+
+ /* If the user wants to override the fallback mode, let them. */
+ if (strcmp (arm_fallback_mode_string, "arm") == 0)
+ return 0;
+ if (strcmp (arm_fallback_mode_string, "thumb") == 0)
+ return 1;
+
+ /* If we couldn't find any symbol, but we're talking to a running
+ target, then trust the current value of $cpsr. This lets
+ "display/i $pc" always show the correct mode (though if there is
+ a symbol table we will not reach here, so it still may not be
+ displayed in the mode it will be executed). */
+ if (target_has_registers)
+ return arm_frame_is_thumb (get_current_frame ());
+
+ /* Otherwise we're out of luck; we assume ARM. */
+ return 0;
}
/* Remove useless bits from addresses in a running program. */
arm_addr_bits_remove (CORE_ADDR val)
{
if (arm_apcs_32)
- return (val & (arm_pc_is_thumb (val) ? 0xfffffffe : 0xfffffffc));
+ return UNMAKE_THUMB_ADDR (val);
else
return (val & 0x03fffffc);
}
stack = make_pv_area (ARM_SP_REGNUM);
back_to = make_cleanup_free_pv_area (stack);
- /* The call instruction saved PC in LR, and the current PC is not
- interesting. Due to this file's conventions, we want the value
- of LR at this function's entry, not at the call site, so we do
- not record the save of the PC - when the ARM prologue analyzer
- has also been converted to the pv mechanism, we could record the
- save here and remove the hack in prev_register. */
- regs[ARM_PC_REGNUM] = pv_unknown ();
-
while (start < limit)
{
unsigned short insn;
{
int regno;
int mask;
- int stop = 0;
+
+ if (pv_area_store_would_trash (stack, regs[ARM_SP_REGNUM]))
+ break;
/* Bits 0-7 contain a mask for registers R0-R7. Bit 8 says
whether to save LR (R14). */
for (regno = ARM_LR_REGNUM; regno >= 0; regno--)
if (mask & (1 << regno))
{
- if (pv_area_store_would_trash (stack, regs[ARM_SP_REGNUM]))
- {
- stop = 1;
- break;
- }
-
regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM],
-4);
pv_area_store (stack, regs[ARM_SP_REGNUM], 4, regs[regno]);
}
-
- if (stop)
- break;
}
else if ((insn & 0xff00) == 0xb000) /* add sp, #simm OR
sub sp, #simm */
return start;
}
- /* frameoffset is unused for this unwinder. */
- cache->frameoffset = 0;
-
if (pv_is_register (regs[ARM_FP_REGNUM], ARM_SP_REGNUM))
{
/* Frame pointer is fp. Frame size is constant. */
sub fp, ip, #nn @@ nn == 20 or 4 depending on second insn */
static CORE_ADDR
-arm_skip_prologue (CORE_ADDR pc)
+arm_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
{
unsigned long inst;
CORE_ADDR skip_pc;
struct symbol *sym;
/* Found a function. */
- sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL, NULL);
+ sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL);
if (sym && SYMBOL_LANGUAGE (sym) != language_asm)
{
/* Don't use this trick for assembly source files. */
/* Check if this is Thumb code. */
if (arm_pc_is_thumb (pc))
- return thumb_analyze_prologue (current_gdbarch, pc, func_end, NULL);
+ return thumb_analyze_prologue (gdbarch, pc, func_end, NULL);
for (skip_pc = pc; skip_pc < func_end; skip_pc += 4)
{
/* *INDENT-ON* */
static void
-thumb_scan_prologue (CORE_ADDR prev_pc, struct arm_prologue_cache *cache)
+thumb_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR prev_pc,
+ CORE_ADDR block_addr, struct arm_prologue_cache *cache)
{
CORE_ADDR prologue_start;
CORE_ADDR prologue_end;
CORE_ADDR current_pc;
- /* Which register has been copied to register n? */
- int saved_reg[16];
- /* findmask:
- bit 0 - push { rlist }
- bit 1 - mov r7, sp OR add r7, sp, #imm (setting of r7)
- bit 2 - sub sp, #simm OR add sp, #simm (adjusting of sp)
- */
- int findmask = 0;
- int i;
- if (find_pc_partial_function (prev_pc, NULL, &prologue_start, &prologue_end))
+ if (find_pc_partial_function (block_addr, NULL, &prologue_start,
+ &prologue_end))
{
struct symtab_and_line sal = find_pc_line (prologue_start, 0);
prologue_end = min (prologue_end, prev_pc);
- thumb_analyze_prologue (current_gdbarch, prologue_start, prologue_end,
- cache);
+ thumb_analyze_prologue (gdbarch, prologue_start, prologue_end, cache);
}
/* This function decodes an ARM function prologue to determine:
*/
static void
-arm_scan_prologue (struct frame_info *next_frame,
+arm_scan_prologue (struct frame_info *this_frame,
struct arm_prologue_cache *cache)
{
- struct gdbarch *gdbarch = get_frame_arch (next_frame);
- int regno, sp_offset, fp_offset, ip_offset;
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+ int regno;
CORE_ADDR prologue_start, prologue_end, current_pc;
- CORE_ADDR prev_pc = frame_pc_unwind (next_frame);
+ CORE_ADDR prev_pc = get_frame_pc (this_frame);
+ CORE_ADDR block_addr = get_frame_address_in_block (this_frame);
+ pv_t regs[ARM_FPS_REGNUM];
+ struct pv_area *stack;
+ struct cleanup *back_to;
+ CORE_ADDR offset;
/* Assume there is no frame until proven otherwise. */
cache->framereg = ARM_SP_REGNUM;
cache->framesize = 0;
- cache->frameoffset = 0;
/* Check for Thumb prologue. */
- if (arm_pc_is_thumb (prev_pc))
+ if (arm_frame_is_thumb (this_frame))
{
- thumb_scan_prologue (prev_pc, cache);
+ thumb_scan_prologue (gdbarch, prev_pc, block_addr, cache);
return;
}
/* Find the function prologue. If we can't find the function in
the symbol table, peek in the stack frame to find the PC. */
- if (find_pc_partial_function (prev_pc, NULL, &prologue_start, &prologue_end))
+ if (find_pc_partial_function (block_addr, NULL, &prologue_start,
+ &prologue_end))
{
/* One way to find the end of the prologue (which works well
for unoptimized code) is to do the following:
CORE_ADDR frame_loc;
LONGEST return_value;
- frame_loc = frame_unwind_register_unsigned (next_frame, ARM_FP_REGNUM);
+ frame_loc = get_frame_register_unsigned (this_frame, ARM_FP_REGNUM);
if (!safe_read_memory_integer (frame_loc, 4, &return_value))
return;
else
in which case it is often (but not always) replaced by
"str lr, [sp, #-4]!". - Michael Snyder, 2002-04-23] */
- sp_offset = fp_offset = ip_offset = 0;
+ for (regno = 0; regno < ARM_FPS_REGNUM; regno++)
+ regs[regno] = pv_register (regno, 0);
+ stack = make_pv_area (ARM_SP_REGNUM);
+ back_to = make_cleanup_free_pv_area (stack);
for (current_pc = prologue_start;
current_pc < prologue_end;
if (insn == 0xe1a0c00d) /* mov ip, sp */
{
- ip_offset = 0;
+ regs[ARM_IP_REGNUM] = regs[ARM_SP_REGNUM];
continue;
}
else if ((insn & 0xfffff000) == 0xe28dc000) /* add ip, sp #n */
unsigned imm = insn & 0xff; /* immediate value */
unsigned rot = (insn & 0xf00) >> 7; /* rotate amount */
imm = (imm >> rot) | (imm << (32 - rot));
- ip_offset = imm;
+ regs[ARM_IP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], imm);
continue;
}
else if ((insn & 0xfffff000) == 0xe24dc000) /* sub ip, sp #n */
unsigned imm = insn & 0xff; /* immediate value */
unsigned rot = (insn & 0xf00) >> 7; /* rotate amount */
imm = (imm >> rot) | (imm << (32 - rot));
- ip_offset = -imm;
+ regs[ARM_IP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], -imm);
continue;
}
else if (insn == 0xe52de004) /* str lr, [sp, #-4]! */
{
- sp_offset -= 4;
- cache->saved_regs[ARM_LR_REGNUM].addr = sp_offset;
+ if (pv_area_store_would_trash (stack, regs[ARM_SP_REGNUM]))
+ break;
+ regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], -4);
+ pv_area_store (stack, regs[ARM_SP_REGNUM], 4, regs[ARM_LR_REGNUM]);
continue;
}
else if ((insn & 0xffff0000) == 0xe92d0000)
{
int mask = insn & 0xffff;
+ if (pv_area_store_would_trash (stack, regs[ARM_SP_REGNUM]))
+ break;
+
/* Calculate offsets of saved registers. */
for (regno = ARM_PC_REGNUM; regno >= 0; regno--)
if (mask & (1 << regno))
{
- sp_offset -= 4;
- cache->saved_regs[regno].addr = sp_offset;
+ regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], -4);
+ pv_area_store (stack, regs[ARM_SP_REGNUM], 4, regs[regno]);
}
}
else if ((insn & 0xffffc000) == 0xe54b0000 || /* strb rx,[r11,#-n] */
unsigned imm = insn & 0xff; /* immediate value */
unsigned rot = (insn & 0xf00) >> 7; /* rotate amount */
imm = (imm >> rot) | (imm << (32 - rot));
- fp_offset = -imm + ip_offset;
- cache->framereg = ARM_FP_REGNUM;
+ regs[ARM_FP_REGNUM] = pv_add_constant (regs[ARM_IP_REGNUM], -imm);
}
else if ((insn & 0xfffff000) == 0xe24dd000) /* sub sp, sp #n */
{
unsigned imm = insn & 0xff; /* immediate value */
unsigned rot = (insn & 0xf00) >> 7; /* rotate amount */
imm = (imm >> rot) | (imm << (32 - rot));
- sp_offset -= imm;
+ regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], -imm);
}
else if ((insn & 0xffff7fff) == 0xed6d0103 /* stfe f?, [sp, -#c]! */
&& gdbarch_tdep (gdbarch)->have_fpa_registers)
{
- sp_offset -= 12;
+ if (pv_area_store_would_trash (stack, regs[ARM_SP_REGNUM]))
+ break;
+
+ regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], -12);
regno = ARM_F0_REGNUM + ((insn >> 12) & 0x07);
- cache->saved_regs[regno].addr = sp_offset;
+ pv_area_store (stack, regs[ARM_SP_REGNUM], 12, regs[regno]);
}
else if ((insn & 0xffbf0fff) == 0xec2d0200 /* sfmfd f0, 4, [sp!] */
&& gdbarch_tdep (gdbarch)->have_fpa_registers)
int n_saved_fp_regs;
unsigned int fp_start_reg, fp_bound_reg;
+ if (pv_area_store_would_trash (stack, regs[ARM_SP_REGNUM]))
+ break;
+
if ((insn & 0x800) == 0x800) /* N0 is set */
{
if ((insn & 0x40000) == 0x40000) /* N1 is set */
fp_bound_reg = fp_start_reg + n_saved_fp_regs;
for (; fp_start_reg < fp_bound_reg; fp_start_reg++)
{
- sp_offset -= 12;
- cache->saved_regs[fp_start_reg++].addr = sp_offset;
+ regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], -12);
+ pv_area_store (stack, regs[ARM_SP_REGNUM], 12,
+ regs[fp_start_reg++]);
}
}
else if ((insn & 0xf0000000) != 0xe0000000)
continue;
}
- /* The frame size is just the negative of the offset (from the
- original SP) of the last thing thing we pushed on the stack.
- The frame offset is [new FP] - [new SP]. */
- cache->framesize = -sp_offset;
- if (cache->framereg == ARM_FP_REGNUM)
- cache->frameoffset = fp_offset - sp_offset;
+ /* The frame size is just the distance from the frame register
+ to the original stack pointer. */
+ if (pv_is_register (regs[ARM_FP_REGNUM], ARM_SP_REGNUM))
+ {
+ /* Frame pointer is fp. */
+ cache->framereg = ARM_FP_REGNUM;
+ cache->framesize = -regs[ARM_FP_REGNUM].k;
+ }
+ else if (pv_is_register (regs[ARM_SP_REGNUM], ARM_SP_REGNUM))
+ {
+ /* Try the stack pointer... this is a bit desperate. */
+ cache->framereg = ARM_SP_REGNUM;
+ cache->framesize = -regs[ARM_SP_REGNUM].k;
+ }
else
- cache->frameoffset = 0;
+ {
+ /* We're just out of luck. We don't know where the frame is. */
+ cache->framereg = -1;
+ cache->framesize = 0;
+ }
+
+ for (regno = 0; regno < ARM_FPS_REGNUM; regno++)
+ if (pv_area_find_reg (stack, gdbarch, regno, &offset))
+ cache->saved_regs[regno].addr = offset;
+
+ do_cleanups (back_to);
}
static struct arm_prologue_cache *
-arm_make_prologue_cache (struct frame_info *next_frame)
+arm_make_prologue_cache (struct frame_info *this_frame)
{
int reg;
struct arm_prologue_cache *cache;
CORE_ADDR unwound_fp;
cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
- cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
+ cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
- arm_scan_prologue (next_frame, cache);
+ arm_scan_prologue (this_frame, cache);
- unwound_fp = frame_unwind_register_unsigned (next_frame, cache->framereg);
+ unwound_fp = get_frame_register_unsigned (this_frame, cache->framereg);
if (unwound_fp == 0)
return cache;
- cache->prev_sp = unwound_fp + cache->framesize - cache->frameoffset;
+ cache->prev_sp = unwound_fp + cache->framesize;
/* Calculate actual addresses of saved registers using offsets
determined by arm_scan_prologue. */
- for (reg = 0; reg < gdbarch_num_regs (get_frame_arch (next_frame)); reg++)
+ for (reg = 0; reg < gdbarch_num_regs (get_frame_arch (this_frame)); reg++)
if (trad_frame_addr_p (cache->saved_regs, reg))
cache->saved_regs[reg].addr += cache->prev_sp;
and the caller's SP when we were called. */
static void
-arm_prologue_this_id (struct frame_info *next_frame,
+arm_prologue_this_id (struct frame_info *this_frame,
void **this_cache,
struct frame_id *this_id)
{
struct arm_prologue_cache *cache;
struct frame_id id;
- CORE_ADDR func;
+ CORE_ADDR pc, func;
if (*this_cache == NULL)
- *this_cache = arm_make_prologue_cache (next_frame);
+ *this_cache = arm_make_prologue_cache (this_frame);
cache = *this_cache;
- func = frame_func_unwind (next_frame, NORMAL_FRAME);
-
- /* This is meant to halt the backtrace at "_start". Make sure we
- don't halt it at a generic dummy frame. */
- if (func <= LOWEST_PC)
+ /* This is meant to halt the backtrace at "_start". */
+ pc = get_frame_pc (this_frame);
+ if (pc <= gdbarch_tdep (get_frame_arch (this_frame))->lowest_pc)
return;
/* If we've hit a wall, stop. */
if (cache->prev_sp == 0)
return;
+ func = get_frame_func (this_frame);
id = frame_id_build (cache->prev_sp, func);
*this_id = id;
}
-static void
-arm_prologue_prev_register (struct frame_info *next_frame,
+static struct value *
+arm_prologue_prev_register (struct frame_info *this_frame,
void **this_cache,
- int prev_regnum,
- int *optimized,
- enum lval_type *lvalp,
- CORE_ADDR *addrp,
- int *realnump,
- gdb_byte *valuep)
+ int prev_regnum)
{
struct arm_prologue_cache *cache;
if (*this_cache == NULL)
- *this_cache = arm_make_prologue_cache (next_frame);
+ *this_cache = arm_make_prologue_cache (this_frame);
cache = *this_cache;
/* If we are asked to unwind the PC, then we need to return the LR
- instead. The saved value of PC points into this frame's
- prologue, not the next frame's resume location. */
+ instead. The prologue may save PC, but it will point into this
+ frame's prologue, not the next frame's resume location. Also
+ strip the saved T bit. A valid LR may have the low bit set, but
+ a valid PC never does. */
if (prev_regnum == ARM_PC_REGNUM)
- prev_regnum = ARM_LR_REGNUM;
+ {
+ CORE_ADDR lr;
+
+ lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM);
+ return frame_unwind_got_constant (this_frame, prev_regnum,
+ arm_addr_bits_remove (lr));
+ }
/* SP is generally not saved to the stack, but this frame is
- identified by NEXT_FRAME's stack pointer at the time of the call.
+ 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)
{
- *lvalp = not_lval;
- if (valuep)
- store_unsigned_integer (valuep, 4, cache->prev_sp);
- return;
+ CORE_ADDR lr, cpsr;
+
+ 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 |= CPSR_T;
+ else
+ cpsr &= ~CPSR_T;
+ return frame_unwind_got_constant (this_frame, prev_regnum, cpsr);
}
- trad_frame_get_prev_register (next_frame, cache->saved_regs, prev_regnum,
- optimized, lvalp, addrp, realnump, valuep);
+ 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
+ arm_prologue_prev_register,
+ NULL,
+ default_frame_sniffer
};
-static const struct frame_unwind *
-arm_prologue_unwind_sniffer (struct frame_info *next_frame)
-{
- return &arm_prologue_unwind;
-}
-
static struct arm_prologue_cache *
-arm_make_stub_cache (struct frame_info *next_frame)
+arm_make_stub_cache (struct frame_info *this_frame)
{
int reg;
struct arm_prologue_cache *cache;
CORE_ADDR unwound_fp;
cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache);
- cache->saved_regs = trad_frame_alloc_saved_regs (next_frame);
+ cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
- cache->prev_sp = frame_unwind_register_unsigned (next_frame, ARM_SP_REGNUM);
+ cache->prev_sp = get_frame_register_unsigned (this_frame, ARM_SP_REGNUM);
return cache;
}
/* Our frame ID for a stub frame is the current SP and LR. */
static void
-arm_stub_this_id (struct frame_info *next_frame,
+arm_stub_this_id (struct frame_info *this_frame,
void **this_cache,
struct frame_id *this_id)
{
struct arm_prologue_cache *cache;
if (*this_cache == NULL)
- *this_cache = arm_make_stub_cache (next_frame);
+ *this_cache = arm_make_stub_cache (this_frame);
cache = *this_cache;
- *this_id = frame_id_build (cache->prev_sp,
- frame_pc_unwind (next_frame));
+ *this_id = frame_id_build (cache->prev_sp, get_frame_pc (this_frame));
}
-struct frame_unwind arm_stub_unwind = {
- NORMAL_FRAME,
- arm_stub_this_id,
- arm_prologue_prev_register
-};
-
-static const struct frame_unwind *
-arm_stub_unwind_sniffer (struct frame_info *next_frame)
+static int
+arm_stub_unwind_sniffer (const struct frame_unwind *self,
+ struct frame_info *this_frame,
+ void **this_prologue_cache)
{
CORE_ADDR addr_in_block;
char dummy[4];
- addr_in_block = frame_unwind_address_in_block (next_frame, NORMAL_FRAME);
+ addr_in_block = get_frame_address_in_block (this_frame);
if (in_plt_section (addr_in_block, NULL)
- || target_read_memory (frame_pc_unwind (next_frame), dummy, 4) != 0)
- return &arm_stub_unwind;
+ || target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0)
+ return 1;
- return NULL;
+ return 0;
}
+struct frame_unwind arm_stub_unwind = {
+ NORMAL_FRAME,
+ arm_stub_this_id,
+ arm_prologue_prev_register,
+ NULL,
+ arm_stub_unwind_sniffer
+};
+
static CORE_ADDR
-arm_normal_frame_base (struct frame_info *next_frame, void **this_cache)
+arm_normal_frame_base (struct frame_info *this_frame, void **this_cache)
{
struct arm_prologue_cache *cache;
if (*this_cache == NULL)
- *this_cache = arm_make_prologue_cache (next_frame);
+ *this_cache = arm_make_prologue_cache (this_frame);
cache = *this_cache;
- return cache->prev_sp + cache->frameoffset - cache->framesize;
+ return cache->prev_sp - cache->framesize;
}
struct frame_base arm_normal_base = {
arm_normal_frame_base
};
-/* Assuming NEXT_FRAME->prev is a dummy, return the frame ID of that
+/* Assuming THIS_FRAME is a dummy, return the frame ID of that
dummy frame. The frame ID's base needs to match the TOS value
saved by save_dummy_frame_tos() and returned from
arm_push_dummy_call, and the PC needs to match the dummy frame's
breakpoint. */
static struct frame_id
-arm_unwind_dummy_id (struct gdbarch *gdbarch, struct frame_info *next_frame)
+arm_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
{
- return frame_id_build (frame_unwind_register_unsigned (next_frame, ARM_SP_REGNUM),
- frame_pc_unwind (next_frame));
+ return frame_id_build (get_frame_register_unsigned (this_frame, ARM_SP_REGNUM),
+ get_frame_pc (this_frame));
}
/* Given THIS_FRAME, find the previous frame's resume PC (which will
return frame_unwind_register_unsigned (this_frame, ARM_SP_REGNUM);
}
+static struct value *
+arm_dwarf2_prev_register (struct frame_info *this_frame, void **this_cache,
+ int regnum)
+{
+ CORE_ADDR lr, cpsr;
+
+ switch (regnum)
+ {
+ case ARM_PC_REGNUM:
+ /* The PC is normally copied from the return column, which
+ describes saves of LR. However, that version may have an
+ extra bit set to indicate Thumb state. The bit is not
+ part of the PC. */
+ lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM);
+ return frame_unwind_got_constant (this_frame, regnum,
+ arm_addr_bits_remove (lr));
+
+ case ARM_PS_REGNUM:
+ /* Reconstruct the T bit; see arm_prologue_prev_register for details. */
+ cpsr = get_frame_register_unsigned (this_frame, regnum);
+ lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM);
+ if (IS_THUMB_ADDR (lr))
+ cpsr |= CPSR_T;
+ else
+ cpsr &= ~CPSR_T;
+ return frame_unwind_got_constant (this_frame, regnum, cpsr);
+
+ default:
+ internal_error (__FILE__, __LINE__,
+ _("Unexpected register %d"), regnum);
+ }
+}
+
+static void
+arm_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
+ struct dwarf2_frame_state_reg *reg,
+ struct frame_info *this_frame)
+{
+ switch (regnum)
+ {
+ case ARM_PC_REGNUM:
+ case ARM_PS_REGNUM:
+ reg->how = DWARF2_FRAME_REG_FN;
+ reg->loc.fn = arm_dwarf2_prev_register;
+ break;
+ case ARM_SP_REGNUM:
+ reg->how = DWARF2_FRAME_REG_CFA;
+ break;
+ }
+}
+
/* When arguments must be pushed onto the stack, they go on in reverse
order. The code below implements a FILO (stack) to do this. */
number. */
static int
-arm_dwarf_reg_to_regnum (int reg)
+arm_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg)
{
/* Core integer regs. */
if (reg >= 0 && reg <= 15)
/* Map GDB internal REGNUM onto the Arm simulator register numbers. */
static int
-arm_register_sim_regno (int regnum)
+arm_register_sim_regno (struct gdbarch *gdbarch, int regnum)
{
int reg = regnum;
- gdb_assert (reg >= 0 && reg < gdbarch_num_regs (current_gdbarch));
+ gdb_assert (reg >= 0 && reg < gdbarch_num_regs (gdbarch));
if (regnum >= ARM_WR0_REGNUM && regnum <= ARM_WR15_REGNUM)
return regnum - ARM_WR0_REGNUM + SIM_ARM_IWMMXT_COP0R0_REGNUM;
static void
convert_from_extended (const struct floatformat *fmt, const void *ptr,
- void *dbl)
+ void *dbl, int endianess)
{
DOUBLEST d;
- if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
+
+ if (endianess == BFD_ENDIAN_BIG)
floatformat_to_doublest (&floatformat_arm_ext_big, ptr, &d);
else
floatformat_to_doublest (&floatformat_arm_ext_littlebyte_bigword,
}
static void
-convert_to_extended (const struct floatformat *fmt, void *dbl, const void *ptr)
+convert_to_extended (const struct floatformat *fmt, void *dbl, const void *ptr,
+ int endianess)
{
DOUBLEST d;
+
floatformat_to_doublest (fmt, ptr, &d);
- if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
+ if (endianess == BFD_ENDIAN_BIG)
floatformat_from_doublest (&floatformat_arm_ext_big, &d, dbl);
else
floatformat_from_doublest (&floatformat_arm_ext_littlebyte_bigword,
unsigned long status;
CORE_ADDR nextpc;
- if (arm_pc_is_thumb (pc))
+ if (arm_frame_is_thumb (frame))
return thumb_get_next_pc (frame, pc);
pc_val = (unsigned long) pc;
nextpc = BranchDest (pc, this_instr);
nextpc |= bit (this_instr, 24) << 1;
- nextpc = gdbarch_addr_bits_remove (current_gdbarch, nextpc);
+ nextpc = gdbarch_addr_bits_remove (gdbarch, nextpc);
if (nextpc == pc)
error (_("Infinite loop detected"));
break;
else
info->symbols = NULL;
- if (gdbarch_byte_order (current_gdbarch) == BFD_ENDIAN_BIG)
+ if (info->endian == BFD_ENDIAN_BIG)
return print_insn_big_arm (memaddr, info);
else
return print_insn_little_arm (memaddr, info);
breakpoint should be inserted. */
static const unsigned char *
-arm_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr)
+arm_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr, int *lenptr)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
if (arm_pc_is_thumb (*pcptr))
{
arm_extract_return_value (struct type *type, struct regcache *regs,
gdb_byte *valbuf)
{
+ struct gdbarch *gdbarch = get_regcache_arch (regs);
+
if (TYPE_CODE_FLT == TYPE_CODE (type))
{
- switch (gdbarch_tdep (get_regcache_arch (regs))->fp_model)
+ switch (gdbarch_tdep (gdbarch)->fp_model)
{
case ARM_FLOAT_FPA:
{
regcache_cooked_read (regs, ARM_F0_REGNUM, tmpbuf);
convert_from_extended (floatformat_from_type (type), tmpbuf,
- valbuf);
+ valbuf, gdbarch_byte_order (gdbarch));
}
break;
arm_store_return_value (struct type *type, struct regcache *regs,
const gdb_byte *valbuf)
{
+ struct gdbarch *gdbarch = get_regcache_arch (regs);
+
if (TYPE_CODE (type) == TYPE_CODE_FLT)
{
char buf[MAX_REGISTER_SIZE];
- switch (gdbarch_tdep (get_regcache_arch (regs))->fp_model)
+ switch (gdbarch_tdep (gdbarch)->fp_model)
{
case ARM_FLOAT_FPA:
- convert_to_extended (floatformat_from_type (type), buf, valbuf);
+ convert_to_extended (floatformat_from_type (type), buf, valbuf,
+ gdbarch_byte_order (gdbarch));
regcache_cooked_write (regs, ARM_F0_REGNUM, buf);
break;
/* Handle function return values. */
static enum return_value_convention
-arm_return_value (struct gdbarch *gdbarch, struct type *valtype,
- struct regcache *regcache, gdb_byte *readbuf,
- const gdb_byte *writebuf)
+arm_return_value (struct gdbarch *gdbarch, struct type *func_type,
+ struct type *valtype, struct regcache *regcache,
+ gdb_byte *readbuf, const gdb_byte *writebuf)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
arm_abi_string);
}
+static void
+arm_show_fallback_mode (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+
+ fprintf_filtered (file, _("\
+The current execution mode assumed (when symbols are unavailable) is \"%s\".\n"),
+ arm_fallback_mode_string);
+}
+
+static void
+arm_show_force_mode (struct ui_file *file, int from_tty,
+ struct cmd_list_element *c, const char *value)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+
+ fprintf_filtered (file, _("\
+The current execution mode assumed (even when symbols are available) is \"%s\".\n"),
+ arm_force_mode_string);
+}
+
/* If the user changes the register disassembly style used for info
register and other commands, we have to also switch the style used
in opcodes for disassembly output. This function is run in the "set
\f
/* Return the ARM register name corresponding to register I. */
static const char *
-arm_register_name (int i)
+arm_register_name (struct gdbarch *gdbarch, int i)
{
if (i >= ARRAY_SIZE (arm_register_names))
/* These registers are only supported on targets which supply
MSYMBOL_SET_SPECIAL (msym);
}
+static void
+arm_objfile_data_cleanup (struct objfile *objfile, void *arg)
+{
+ struct arm_per_objfile *data = arg;
+ unsigned int i;
+
+ for (i = 0; i < objfile->obfd->section_count; i++)
+ VEC_free (arm_mapping_symbol_s, data->section_maps[i]);
+}
+
+static void
+arm_record_special_symbol (struct gdbarch *gdbarch, struct objfile *objfile,
+ asymbol *sym)
+{
+ const char *name = bfd_asymbol_name (sym);
+ struct arm_per_objfile *data;
+ VEC(arm_mapping_symbol_s) **map_p;
+ struct arm_mapping_symbol new_map_sym;
+
+ gdb_assert (name[0] == '$');
+ if (name[1] != 'a' && name[1] != 't' && name[1] != 'd')
+ return;
+
+ data = objfile_data (objfile, arm_objfile_data_key);
+ if (data == NULL)
+ {
+ data = OBSTACK_ZALLOC (&objfile->objfile_obstack,
+ struct arm_per_objfile);
+ set_objfile_data (objfile, arm_objfile_data_key, data);
+ data->section_maps = OBSTACK_CALLOC (&objfile->objfile_obstack,
+ objfile->obfd->section_count,
+ VEC(arm_mapping_symbol_s) *);
+ }
+ map_p = &data->section_maps[bfd_get_section (sym)->index];
+
+ new_map_sym.value = sym->value;
+ new_map_sym.type = name[1];
+
+ /* Assume that most mapping symbols appear in order of increasing
+ value. If they were randomly distributed, it would be faster to
+ always push here and then sort at first use. */
+ if (!VEC_empty (arm_mapping_symbol_s, *map_p))
+ {
+ struct arm_mapping_symbol *prev_map_sym;
+
+ prev_map_sym = VEC_last (arm_mapping_symbol_s, *map_p);
+ if (prev_map_sym->value >= sym->value)
+ {
+ unsigned int idx;
+ idx = VEC_lower_bound (arm_mapping_symbol_s, *map_p, &new_map_sym,
+ arm_compare_mapping_symbols);
+ VEC_safe_insert (arm_mapping_symbol_s, *map_p, idx, &new_map_sym);
+ return;
+ }
+ }
+
+ VEC_safe_push (arm_mapping_symbol_s, *map_p, &new_map_sym);
+}
+
static void
arm_write_pc (struct regcache *regcache, CORE_ADDR pc)
{
ULONGEST val;
regcache_cooked_read_unsigned (regcache, ARM_PS_REGNUM, &val);
if (arm_pc_is_thumb (pc))
- regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM, val | 0x20);
+ regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM, val | CPSR_T);
else
regcache_cooked_write_unsigned (regcache, ARM_PS_REGNUM,
- val & ~(ULONGEST) 0x20);
+ val & ~(ULONGEST) CPSR_T);
}
}
set_gdbarch_write_pc (gdbarch, arm_write_pc);
/* Frame handling. */
- set_gdbarch_unwind_dummy_id (gdbarch, arm_unwind_dummy_id);
+ set_gdbarch_dummy_id (gdbarch, arm_dummy_id);
set_gdbarch_unwind_pc (gdbarch, arm_unwind_pc);
set_gdbarch_unwind_sp (gdbarch, arm_unwind_sp);
set_gdbarch_print_float_info (gdbarch, arm_print_float_info);
/* Internal <-> external register number maps. */
- set_gdbarch_dwarf_reg_to_regnum (gdbarch, arm_dwarf_reg_to_regnum);
set_gdbarch_dwarf2_reg_to_regnum (gdbarch, arm_dwarf_reg_to_regnum);
set_gdbarch_register_sim_regno (gdbarch, arm_register_sim_regno);
set_gdbarch_elf_make_msymbol_special (gdbarch, arm_elf_make_msymbol_special);
set_gdbarch_coff_make_msymbol_special (gdbarch,
arm_coff_make_msymbol_special);
+ set_gdbarch_record_special_symbol (gdbarch, arm_record_special_symbol);
/* Virtual tables. */
set_gdbarch_vbit_in_delta (gdbarch, 1);
/* Hook in the ABI-specific overrides, if they have been registered. */
gdbarch_init_osabi (info, gdbarch);
+ dwarf2_frame_set_init_reg (gdbarch, arm_dwarf2_frame_init_reg);
+
/* Add some default predicates. */
- frame_unwind_append_sniffer (gdbarch, arm_stub_unwind_sniffer);
- frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
- frame_unwind_append_sniffer (gdbarch, arm_prologue_unwind_sniffer);
+ frame_unwind_append_unwinder (gdbarch, &arm_stub_unwind);
+ dwarf2_append_unwinders (gdbarch);
+ frame_unwind_append_unwinder (gdbarch, &arm_prologue_unwind);
/* Now we have tuned the configuration, set a few final things,
based on what the OS ABI has told us. */
}
if (tdesc_data)
- tdesc_use_registers (gdbarch, tdesc_data);
+ tdesc_use_registers (gdbarch, info.target_desc, tdesc_data);
/* Add standard register aliases. We add aliases even for those
nanes which are used by the current architecture - it's simpler,
gdbarch_register (bfd_arch_arm, arm_gdbarch_init, arm_dump_tdep);
+ arm_objfile_data_key
+ = register_objfile_data_with_cleanup (arm_objfile_data_cleanup);
+
/* Register an ELF OS ABI sniffer for ARM binaries. */
gdbarch_register_osabi_sniffer (bfd_arch_arm,
bfd_target_elf_flavour,
NULL, arm_set_abi, arm_show_abi,
&setarmcmdlist, &showarmcmdlist);
+ /* Add two commands to allow the user to force the assumed
+ execution mode. */
+ add_setshow_enum_cmd ("fallback-mode", class_support,
+ arm_mode_strings, &arm_fallback_mode_string,
+ _("Set the mode assumed when symbols are unavailable."),
+ _("Show the mode assumed when symbols are unavailable."),
+ NULL, NULL, arm_show_fallback_mode,
+ &setarmcmdlist, &showarmcmdlist);
+ add_setshow_enum_cmd ("force-mode", class_support,
+ arm_mode_strings, &arm_force_mode_string,
+ _("Set the mode assumed even when symbols are available."),
+ _("Show the mode assumed even when symbols are available."),
+ NULL, NULL, arm_show_force_mode,
+ &setarmcmdlist, &showarmcmdlist);
+
/* Debugging flag. */
add_setshow_boolean_cmd ("arm", class_maintenance, &arm_debug,
_("Set ARM debugging."),