#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;
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 - 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. */
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. */
{
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;
}
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
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)
{
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."),