/* 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, 2008
+ 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
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;
MSYMBOL_IS_SPECIAL Tests the "special" bit in a minimal symbol. */
#define MSYMBOL_SET_SPECIAL(msym) \
- MSYMBOL_INFO (msym) = (char *) (((long) MSYMBOL_INFO (msym)) \
- | 0x80000000)
+ MSYMBOL_TARGET_FLAG_1 (msym) = 1
#define MSYMBOL_IS_SPECIAL(msym) \
- (((long) MSYMBOL_INFO (msym) & 0x80000000) != 0)
+ MSYMBOL_TARGET_FLAG_1 (msym)
+
+/* Macros for swapping shorts and ints. In the unlikely case that anybody else needs these,
+ move to a general header. (A better solution might be to define memory read routines that
+ know whether they are reading code or data.) */
+
+#define SWAP_SHORT(x) \
+ ((((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8));
+
+#define SWAP_INT(x) \
+ ( ((x & 0xff000000) >> 24) \
+ | ((x & 0x00ff0000) >> 8) \
+ | ((x & 0x0000ff00) << 8) \
+ | ((x & 0x000000ff) << 24))
+
+/* 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 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;
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. 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 - obj_section_addr (sec),
+ 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. */
static CORE_ADDR
-arm_addr_bits_remove (CORE_ADDR val)
+arm_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR val)
{
if (arm_apcs_32)
return UNMAKE_THUMB_ADDR (val);
/* When reading symbols, we need to zap the low bit of the address,
which may be set to 1 for Thumb functions. */
static CORE_ADDR
-arm_smash_text_address (CORE_ADDR val)
+arm_smash_text_address (struct gdbarch *gdbarch, CORE_ADDR val)
{
return val & ~1;
}
insn = read_memory_unsigned_integer (start, 2);
+ if (gdbarch_byte_order_for_code (gdbarch) != gdbarch_byte_order (gdbarch))
+ insn = SWAP_SHORT (insn);
+
if ((insn & 0xfe00) == 0xb400) /* push { rlist } */
{
int regno;
{
unsigned long inst;
CORE_ADDR skip_pc;
- CORE_ADDR func_addr, func_end = 0;
- char *func_name;
+ CORE_ADDR func_addr, limit_pc;
struct symtab_and_line sal;
/* If we're in a dummy frame, don't even try to skip the prologue. */
if (deprecated_pc_in_call_dummy (pc))
return pc;
- /* See what the symbol table says. */
-
- if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end))
+ /* See if we can determine the end of the prologue via the symbol table.
+ If so, then return either PC, or the PC after the prologue, whichever
+ is greater. */
+ if (find_pc_partial_function (pc, NULL, &func_addr, NULL))
{
- struct symbol *sym;
-
- /* Found a function. */
- sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL, NULL);
- if (sym && SYMBOL_LANGUAGE (sym) != language_asm)
- {
- /* Don't use this trick for assembly source files. */
- sal = find_pc_line (func_addr, 0);
- if ((sal.line != 0) && (sal.end < func_end))
- return sal.end;
- }
+ CORE_ADDR post_prologue_pc = skip_prologue_using_sal (func_addr);
+ if (post_prologue_pc != 0)
+ return max (pc, post_prologue_pc);
}
- /* Can't find the prologue end in the symbol table, try it the hard way
- by disassembling the instructions. */
+ /* Can't determine prologue from the symbol table, need to examine
+ instructions. */
+ /* Find an upper limit on the function prologue using the debug
+ information. If the debug information could not be used to provide
+ that bound, then use an arbitrary large number as the upper bound. */
/* Like arm_scan_prologue, stop no later than pc + 64. */
- if (func_end == 0 || func_end > pc + 64)
- func_end = pc + 64;
+ limit_pc = skip_prologue_using_sal (pc);
+ if (limit_pc == 0)
+ limit_pc = pc + 64; /* Magic. */
+
/* Check if this is Thumb code. */
if (arm_pc_is_thumb (pc))
- return thumb_analyze_prologue (gdbarch, pc, func_end, NULL);
+ return thumb_analyze_prologue (gdbarch, pc, limit_pc, NULL);
- for (skip_pc = pc; skip_pc < func_end; skip_pc += 4)
+ for (skip_pc = pc; skip_pc < limit_pc; skip_pc += 4)
{
inst = read_memory_unsigned_integer (skip_pc, 4);
+ if (gdbarch_byte_order_for_code (gdbarch) != gdbarch_byte_order (gdbarch))
+ inst = SWAP_INT (inst);
+
/* "mov ip, sp" is no longer a required part of the prologue. */
if (inst == 0xe1a0c00d) /* mov ip, sp */
continue;
{
unsigned int insn = read_memory_unsigned_integer (current_pc, 4);
+ if (gdbarch_byte_order_for_code (gdbarch) != gdbarch_byte_order (gdbarch))
+ insn = SWAP_INT (insn);
+
if (insn == 0xe1a0c00d) /* mov ip, sp */
{
regs[ARM_IP_REGNUM] = regs[ARM_SP_REGNUM];
{
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 (this_frame);
cache = *this_cache;
- func = get_frame_func (this_frame);
-
- /* This is meant to halt the backtrace at "_start". Make sure we
- don't halt it at a generic dummy frame. */
- if (func <= gdbarch_tdep (get_frame_arch (this_frame))->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;
}
void **this_cache,
int prev_regnum)
{
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
struct arm_prologue_cache *cache;
if (*this_cache == NULL)
lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM);
return frame_unwind_got_constant (this_frame, prev_regnum,
- arm_addr_bits_remove (lr));
+ arm_addr_bits_remove (gdbarch, lr));
}
/* SP is generally not saved to the stack, but this frame is
{
CORE_ADDR pc;
pc = frame_unwind_register_unsigned (this_frame, ARM_PC_REGNUM);
- return arm_addr_bits_remove (pc);
+ return arm_addr_bits_remove (gdbarch, pc);
}
static CORE_ADDR
arm_dwarf2_prev_register (struct frame_info *this_frame, void **this_cache,
int regnum)
{
+ struct gdbarch * gdbarch = get_frame_arch (this_frame);
CORE_ADDR lr, cpsr;
switch (regnum)
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));
+ arm_addr_bits_remove (gdbarch, lr));
case ARM_PS_REGNUM:
/* Reconstruct the T bit; see arm_prologue_prev_register for details. */
if (regnum >= ARM_F0_REGNUM && regnum < ARM_F0_REGNUM + NUM_FREGS)
return builtin_type_arm_ext;
else if (regnum == ARM_SP_REGNUM)
- return builtin_type_void_data_ptr;
+ return builtin_type (gdbarch)->builtin_data_ptr;
else if (regnum == ARM_PC_REGNUM)
- return builtin_type_void_func_ptr;
+ return builtin_type (gdbarch)->builtin_func_ptr;
else if (regnum >= ARRAY_SIZE (arm_register_names))
/* These registers are only supported on targets which supply
an XML description. */
CORE_ADDR nextpc = pc + 2; /* default is next instruction */
unsigned long offset;
+ if (gdbarch_byte_order_for_code (gdbarch) != gdbarch_byte_order (gdbarch))
+ inst1 = SWAP_SHORT (inst1);
+
if ((inst1 & 0xff00) == 0xbd00) /* pop {rlist, pc} */
{
CORE_ADDR sp;
else if ((inst1 & 0xf800) == 0xf000) /* long branch with link, and blx */
{
unsigned short inst2 = read_memory_unsigned_integer (pc + 2, 2);
+ if (gdbarch_byte_order_for_code (gdbarch) != gdbarch_byte_order (gdbarch))
+ inst2 = SWAP_SHORT (inst2);
offset = (sbits (inst1, 0, 10) << 12) + (bits (inst2, 0, 10) << 1);
nextpc = pc_val + offset;
/* For BLX make sure to clear the low bits. */
pc_val = (unsigned long) pc;
this_instr = read_memory_unsigned_integer (pc, 4);
+
+ if (gdbarch_byte_order_for_code (gdbarch) != gdbarch_byte_order (gdbarch))
+ this_instr = SWAP_INT (this_instr);
+
status = get_frame_register_unsigned (frame, ARM_PS_REGNUM);
nextpc = (CORE_ADDR) (pc_val + 4); /* Default case */
struct gdbarch_info info;
/* If the current architecture is not ARM, we have nothing to do. */
- if (gdbarch_bfd_arch_info (current_gdbarch)->arch != bfd_arch_arm)
+ if (gdbarch_bfd_arch_info (target_gdbarch)->arch != bfd_arch_arm)
return;
/* Update the architecture. */
show_fp_model (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (target_gdbarch);
if (arm_fp_model == ARM_FLOAT_AUTO
- && gdbarch_bfd_arch_info (current_gdbarch)->arch == bfd_arch_arm)
+ && gdbarch_bfd_arch_info (target_gdbarch)->arch == bfd_arch_arm)
fprintf_filtered (file, _("\
The current ARM floating point model is \"auto\" (currently \"%s\").\n"),
fp_model_strings[tdep->fp_model]);
arm_show_abi (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
- struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (target_gdbarch);
if (arm_abi_global == ARM_ABI_AUTO
- && gdbarch_bfd_arch_info (current_gdbarch)->arch == bfd_arch_arm)
+ && gdbarch_bfd_arch_info (target_gdbarch)->arch == bfd_arch_arm)
fprintf_filtered (file, _("\
The current ARM ABI is \"auto\" (currently \"%s\").\n"),
arm_abi_strings[tdep->arm_abi]);
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 (target_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 (target_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
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)
{
break;
}
}
+
+ if (e_flags & EF_ARM_BE8)
+ info.byte_order_for_code = BFD_ENDIAN_LITTLE;
+
break;
default:
tdep->have_fpa_registers = have_fpa_registers;
/* Breakpoints. */
- switch (info.byte_order)
+ switch (info.byte_order_for_code)
{
case BFD_ENDIAN_BIG:
tdep->arm_breakpoint = arm_default_arm_be_breakpoint;
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);
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."),