X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Farm-tdep.c;h=754c431fabd61e8f688e8edd997b2f853753f0d7;hb=3489610d2dea02c6a2863fc5f84c9b918faf8c4d;hp=48901c05255e4d3483eeb656dcdfd94c6a4db3c4;hpb=c055b1010f3ff41e33e30b195f809e05d68af989;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index 48901c0525..754c431fab 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -1,7 +1,7 @@ /* 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, 2010, 2011 Free Software Foundation, Inc. This file is part of GDB. @@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include /* XXX for isupper () */ +#include /* XXX for isupper (). */ #include "defs.h" #include "frame.h" @@ -27,8 +27,9 @@ #include "gdbcmd.h" #include "gdbcore.h" #include "gdb_string.h" -#include "dis-asm.h" /* For register styles. */ +#include "dis-asm.h" /* For register styles. */ #include "regcache.h" +#include "reggroups.h" #include "doublest.h" #include "value.h" #include "arch-utils.h" @@ -42,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" @@ -51,6 +53,9 @@ #include "elf/arm.h" #include "gdb_assert.h" +#include "vec.h" + +#include "features/arm-with-m.c" static int arm_debug; @@ -61,12 +66,27 @@ static int arm_debug; MSYMBOL_SET_SPECIAL Actually sets the "special" bit. 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) +#define MSYMBOL_SET_SPECIAL(msym) \ + MSYMBOL_TARGET_FLAG_1 (msym) = 1 #define MSYMBOL_IS_SPECIAL(msym) \ - (((long) MSYMBOL_INFO (msym) & 0x80000000) != 0) + MSYMBOL_TARGET_FLAG_1 (msym) + +/* 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; @@ -101,10 +121,25 @@ static const char *arm_abi_strings[] = 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", + NULL + }; + +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; -/* The standard register names, and all the valid aliases for them. */ +/* The standard register names, and all the valid aliases for them. Note + that `fp', `sp' and `pc' are not added in this alias list, because they + have been added as builtin user registers in + std-regs.c:_initialize_frame_reg. */ static const struct { const char *name; @@ -145,12 +180,9 @@ static const struct { "tr", 9 }, /* Special names. */ { "ip", 12 }, - { "sp", 13 }, { "lr", 14 }, - { "pc", 15 }, /* Names used by GCC (not listed in the ARM EABI). */ { "sl", 10 }, - { "fp", 11 }, /* A special name from the older ATPCS. */ { "wr", 7 }, }; @@ -181,6 +213,13 @@ static void convert_from_extended (const struct floatformat *, const void *, static void convert_to_extended (const struct floatformat *, void *, const void *, int); +static enum register_status arm_neon_quad_read (struct gdbarch *gdbarch, + struct regcache *regcache, + int regnum, gdb_byte *buf); +static void arm_neon_quad_write (struct gdbarch *gdbarch, + struct regcache *regcache, + int regnum, const gdb_byte *buf); + struct arm_prologue_cache { /* The stack pointer at the time this frame was created; i.e. the @@ -201,6 +240,16 @@ struct arm_prologue_cache struct trad_frame_saved_reg *saved_regs; }; +static CORE_ADDR arm_analyze_prologue (struct gdbarch *gdbarch, + CORE_ADDR prologue_start, + CORE_ADDR prologue_end, + struct arm_prologue_cache *cache); + +/* Architecture version for displaced stepping. This effects the behaviour of + certain instructions, and really should not be hard-wired. */ + +#define DISPLACED_STEPPING_ARCH_VERSION 5 + /* Addresses for calling Thumb functions have the bit 0 set. Here are some macros to test, set, or clear bit 0 of addresses. */ #define IS_THUMB_ADDR(addr) ((addr) & 1) @@ -211,33 +260,195 @@ struct arm_prologue_cache int arm_apcs_32 = 1; +/* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode. */ + +int +arm_psr_thumb_bit (struct gdbarch *gdbarch) +{ + if (gdbarch_tdep (gdbarch)->is_m) + return XPSR_T; + else + return CPSR_T; +} + +/* Determine if FRAME is executing in Thumb mode. */ + +int +arm_frame_is_thumb (struct frame_info *frame) +{ + CORE_ADDR cpsr; + ULONGEST t_bit = arm_psr_thumb_bit (get_frame_arch (frame)); + + /* 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 & t_bit) != 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; +} + +/* Search for the mapping symbol covering MEMADDR. If one is found, + return its type. Otherwise, return 0. If START is non-NULL, + set *START to the location of the mapping symbol. */ + +static char +arm_find_mapping_symbol (CORE_ADDR memaddr, CORE_ADDR *start) +{ + struct obj_section *sec; + + /* 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) + { + if (start) + *start = map_sym->value + obj_section_addr (sec); + return map_sym->type; + } + } + + if (idx > 0) + { + map_sym = VEC_index (arm_mapping_symbol_s, map, idx - 1); + if (start) + *start = map_sym->value + obj_section_addr (sec); + return map_sym->type; + } + } + } + } + + return 0; +} + +static CORE_ADDR arm_get_next_pc_raw (struct frame_info *frame, + CORE_ADDR pc, int insert_bkpt); + /* 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) +int +arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr) { + struct obj_section *sec; struct minimal_symbol *sym; + char type; + struct displaced_step_closure* dsc + = get_displaced_step_closure_by_addr(memaddr); + + /* If checking the mode of displaced instruction in copy area, the mode + should be determined by instruction on the original address. */ + if (dsc) + { + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, + "displaced: check mode of %.8lx instead of %.8lx\n", + (unsigned long) dsc->insn_addr, + (unsigned long) memaddr); + memaddr = dsc->insn_addr; + } /* 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; + + /* ARM v6-M and v7-M are always in Thumb mode. */ + if (gdbarch_tdep (gdbarch)->is_m) + return 1; + + /* If there are mapping symbols, consult them. */ + type = arm_find_mapping_symbol (memaddr, NULL); + if (type) + return 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)); + + /* 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). + + As a further heuristic if we detect that we are doing a single-step we + see what state executing the current instruction ends up with us being + in. */ + if (target_has_registers) { - return (MSYMBOL_IS_SPECIAL (sym)); - } - else - { - return 0; + struct frame_info *current_frame = get_current_frame (); + CORE_ADDR current_pc = get_frame_pc (current_frame); + int is_thumb = arm_frame_is_thumb (current_frame); + CORE_ADDR next_pc; + if (memaddr == current_pc) + return is_thumb; + else + { + struct gdbarch *gdbarch = get_frame_arch (current_frame); + next_pc = arm_get_next_pc_raw (current_frame, current_pc, FALSE); + if (memaddr == gdbarch_addr_bits_remove (gdbarch, next_pc)) + return IS_THUMB_ADDR (next_pc); + else + return is_thumb; + } } + + /* 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); @@ -248,44 +459,265 @@ arm_addr_bits_remove (CORE_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; } +/* Return 1 if PC is the start of a compiler helper function which + can be safely ignored during prologue skipping. IS_THUMB is true + if the function is known to be a Thumb function due to the way it + is being called. */ +static int +skip_prologue_function (struct gdbarch *gdbarch, CORE_ADDR pc, int is_thumb) +{ + enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); + struct minimal_symbol *msym; + + msym = lookup_minimal_symbol_by_pc (pc); + if (msym != NULL + && SYMBOL_VALUE_ADDRESS (msym) == pc + && SYMBOL_LINKAGE_NAME (msym) != NULL) + { + const char *name = SYMBOL_LINKAGE_NAME (msym); + + /* The GNU linker's Thumb call stub to foo is named + __foo_from_thumb. */ + if (strstr (name, "_from_thumb") != NULL) + name += 2; + + /* On soft-float targets, __truncdfsf2 is called to convert promoted + arguments to their argument types in non-prototyped + functions. */ + if (strncmp (name, "__truncdfsf2", strlen ("__truncdfsf2")) == 0) + return 1; + if (strncmp (name, "__aeabi_d2f", strlen ("__aeabi_d2f")) == 0) + return 1; + + /* Internal functions related to thread-local storage. */ + if (strncmp (name, "__tls_get_addr", strlen ("__tls_get_addr")) == 0) + return 1; + if (strncmp (name, "__aeabi_read_tp", strlen ("__aeabi_read_tp")) == 0) + return 1; + } + else + { + /* If we run against a stripped glibc, we may be unable to identify + special functions by name. Check for one important case, + __aeabi_read_tp, by comparing the *code* against the default + implementation (this is hand-written ARM assembler in glibc). */ + + if (!is_thumb + && read_memory_unsigned_integer (pc, 4, byte_order_for_code) + == 0xe3e00a0f /* mov r0, #0xffff0fff */ + && read_memory_unsigned_integer (pc + 4, 4, byte_order_for_code) + == 0xe240f01f) /* sub pc, r0, #31 */ + return 1; + } + + return 0; +} + +/* Support routines for instruction parsing. */ +#define submask(x) ((1L << ((x) + 1)) - 1) +#define bit(obj,st) (((obj) >> (st)) & 1) +#define bits(obj,st,fn) (((obj) >> (st)) & submask ((fn) - (st))) +#define sbits(obj,st,fn) \ + ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st)))) +#define BranchDest(addr,instr) \ + ((CORE_ADDR) (((long) (addr)) + 8 + (sbits (instr, 0, 23) << 2))) + +/* Extract the immediate from instruction movw/movt of encoding T. INSN1 is + the first 16-bit of instruction, and INSN2 is the second 16-bit of + instruction. */ +#define EXTRACT_MOVW_MOVT_IMM_T(insn1, insn2) \ + ((bits ((insn1), 0, 3) << 12) \ + | (bits ((insn1), 10, 10) << 11) \ + | (bits ((insn2), 12, 14) << 8) \ + | bits ((insn2), 0, 7)) + +/* Extract the immediate from instruction movw/movt of encoding A. INSN is + the 32-bit instruction. */ +#define EXTRACT_MOVW_MOVT_IMM_A(insn) \ + ((bits ((insn), 16, 19) << 12) \ + | bits ((insn), 0, 11)) + +/* Decode immediate value; implements ThumbExpandImmediate pseudo-op. */ + +static unsigned int +thumb_expand_immediate (unsigned int imm) +{ + unsigned int count = imm >> 7; + + if (count < 8) + switch (count / 2) + { + case 0: + return imm & 0xff; + case 1: + return (imm & 0xff) | ((imm & 0xff) << 16); + case 2: + return ((imm & 0xff) << 8) | ((imm & 0xff) << 24); + case 3: + return (imm & 0xff) | ((imm & 0xff) << 8) + | ((imm & 0xff) << 16) | ((imm & 0xff) << 24); + } + + return (0x80 | (imm & 0x7f)) << (32 - count); +} + +/* Return 1 if the 16-bit Thumb instruction INST might change + control flow, 0 otherwise. */ + +static int +thumb_instruction_changes_pc (unsigned short inst) +{ + if ((inst & 0xff00) == 0xbd00) /* pop {rlist, pc} */ + return 1; + + if ((inst & 0xf000) == 0xd000) /* conditional branch */ + return 1; + + if ((inst & 0xf800) == 0xe000) /* unconditional branch */ + return 1; + + if ((inst & 0xff00) == 0x4700) /* bx REG, blx REG */ + return 1; + + if ((inst & 0xff87) == 0x4687) /* mov pc, REG */ + return 1; + + if ((inst & 0xf500) == 0xb100) /* CBNZ or CBZ. */ + return 1; + + return 0; +} + +/* Return 1 if the 32-bit Thumb instruction in INST1 and INST2 + might change control flow, 0 otherwise. */ + +static int +thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2) +{ + if ((inst1 & 0xf800) == 0xf000 && (inst2 & 0x8000) == 0x8000) + { + /* Branches and miscellaneous control instructions. */ + + if ((inst2 & 0x1000) != 0 || (inst2 & 0xd001) == 0xc000) + { + /* B, BL, BLX. */ + return 1; + } + else if (inst1 == 0xf3de && (inst2 & 0xff00) == 0x3f00) + { + /* SUBS PC, LR, #imm8. */ + return 1; + } + else if ((inst2 & 0xd000) == 0x8000 && (inst1 & 0x0380) != 0x0380) + { + /* Conditional branch. */ + return 1; + } + + return 0; + } + + if ((inst1 & 0xfe50) == 0xe810) + { + /* Load multiple or RFE. */ + + if (bit (inst1, 7) && !bit (inst1, 8)) + { + /* LDMIA or POP */ + if (bit (inst2, 15)) + return 1; + } + else if (!bit (inst1, 7) && bit (inst1, 8)) + { + /* LDMDB */ + if (bit (inst2, 15)) + return 1; + } + else if (bit (inst1, 7) && bit (inst1, 8)) + { + /* RFEIA */ + return 1; + } + else if (!bit (inst1, 7) && !bit (inst1, 8)) + { + /* RFEDB */ + return 1; + } + + return 0; + } + + if ((inst1 & 0xffef) == 0xea4f && (inst2 & 0xfff0) == 0x0f00) + { + /* MOV PC or MOVS PC. */ + return 1; + } + + if ((inst1 & 0xff70) == 0xf850 && (inst2 & 0xf000) == 0xf000) + { + /* LDR PC. */ + if (bits (inst1, 0, 3) == 15) + return 1; + if (bit (inst1, 7)) + return 1; + if (bit (inst2, 11)) + return 1; + if ((inst2 & 0x0fc0) == 0x0000) + return 1; + + return 0; + } + + if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf000) + { + /* TBB. */ + return 1; + } + + if ((inst1 & 0xfff0) == 0xe8d0 && (inst2 & 0xfff0) == 0xf010) + { + /* TBH. */ + return 1; + } + + return 0; +} + /* Analyze a Thumb prologue, looking for a recognizable stack frame and frame pointer. Scan until we encounter a store that could - clobber the stack frame unexpectedly, or an unknown instruction. */ + clobber the stack frame unexpectedly, or an unknown instruction. + Return the last address which is definitely safe to skip for an + initial breakpoint. */ static CORE_ADDR thumb_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR start, CORE_ADDR limit, struct arm_prologue_cache *cache) { + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); int i; pv_t regs[16]; struct pv_area *stack; struct cleanup *back_to; CORE_ADDR offset; + CORE_ADDR unrecognized_pc = 0; for (i = 0; i < 16; i++) regs[i] = pv_register (i, 0); - stack = make_pv_area (ARM_SP_REGNUM); + stack = make_pv_area (ARM_SP_REGNUM, gdbarch_addr_bit (gdbarch)); 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; - insn = read_memory_unsigned_integer (start, 2); + insn = read_memory_unsigned_integer (start, 2, byte_order_for_code); if ((insn & 0xfe00) == 0xb400) /* push { rlist } */ { @@ -319,9 +751,29 @@ thumb_analyze_prologue (struct gdbarch *gdbarch, regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], offset); } - else if ((insn & 0xff00) == 0xaf00) /* add r7, sp, #imm */ - regs[THUMB_FP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], - (insn & 0xff) << 2); + else if ((insn & 0xf800) == 0xa800) /* add Rd, sp, #imm */ + regs[bits (insn, 8, 10)] = pv_add_constant (regs[ARM_SP_REGNUM], + (insn & 0xff) << 2); + else if ((insn & 0xfe00) == 0x1c00 /* add Rd, Rn, #imm */ + && pv_is_register (regs[bits (insn, 3, 5)], ARM_SP_REGNUM)) + regs[bits (insn, 0, 2)] = pv_add_constant (regs[bits (insn, 3, 5)], + bits (insn, 6, 8)); + else if ((insn & 0xf800) == 0x3000 /* add Rd, #imm */ + && pv_is_register (regs[bits (insn, 8, 10)], ARM_SP_REGNUM)) + regs[bits (insn, 8, 10)] = pv_add_constant (regs[bits (insn, 8, 10)], + bits (insn, 0, 7)); + else if ((insn & 0xfe00) == 0x1800 /* add Rd, Rn, Rm */ + && pv_is_register (regs[bits (insn, 6, 8)], ARM_SP_REGNUM) + && pv_is_constant (regs[bits (insn, 3, 5)])) + regs[bits (insn, 0, 2)] = pv_add (regs[bits (insn, 3, 5)], + regs[bits (insn, 6, 8)]); + else if ((insn & 0xff00) == 0x4400 /* add Rd, Rm */ + && pv_is_constant (regs[bits (insn, 3, 6)])) + { + int rd = (bit (insn, 7) << 3) + bits (insn, 0, 2); + int rm = bits (insn, 3, 6); + regs[rd] = pv_add (regs[rd], regs[rm]); + } else if ((insn & 0xff00) == 0x4600) /* mov hi, lo or mov lo, hi */ { int dst_reg = (insn & 0x7) + ((insn & 0x80) >> 4); @@ -344,116 +796,669 @@ thumb_analyze_prologue (struct gdbarch *gdbarch, pv_area_store (stack, addr, 4, regs[regno]); } - else + else if ((insn & 0xf800) == 0x6000) /* str rd, [rn, #off] */ { - /* We don't know what this instruction is. We're finished - scanning. NOTE: Recognizing more safe-to-ignore - instructions here will improve support for optimized - code. */ - break; - } + int rd = bits (insn, 0, 2); + int rn = bits (insn, 3, 5); + pv_t addr; - start += 2; - } + offset = bits (insn, 6, 10) << 2; + addr = pv_add_constant (regs[rn], offset); - if (cache == NULL) - { - do_cleanups (back_to); - return start; - } + if (pv_area_store_would_trash (stack, addr)) + break; - if (pv_is_register (regs[ARM_FP_REGNUM], ARM_SP_REGNUM)) - { - /* Frame pointer is fp. Frame size is constant. */ - cache->framereg = ARM_FP_REGNUM; - cache->framesize = -regs[ARM_FP_REGNUM].k; - } - else if (pv_is_register (regs[THUMB_FP_REGNUM], ARM_SP_REGNUM)) - { - /* Frame pointer is r7. Frame size is constant. */ - cache->framereg = THUMB_FP_REGNUM; - cache->framesize = -regs[THUMB_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 - { - /* We're just out of luck. We don't know where the frame is. */ - cache->framereg = -1; - cache->framesize = 0; - } + pv_area_store (stack, addr, 4, regs[rd]); + } + else if (((insn & 0xf800) == 0x7000 /* strb Rd, [Rn, #off] */ + || (insn & 0xf800) == 0x8000) /* strh Rd, [Rn, #off] */ + && pv_is_register (regs[bits (insn, 3, 5)], ARM_SP_REGNUM)) + /* Ignore stores of argument registers to the stack. */ + ; + else if ((insn & 0xf800) == 0xc800 /* ldmia Rn!, { registers } */ + && pv_is_register (regs[bits (insn, 8, 10)], ARM_SP_REGNUM)) + /* Ignore block loads from the stack, potentially copying + parameters from memory. */ + ; + else if ((insn & 0xf800) == 0x9800 /* ldr Rd, [Rn, #immed] */ + || ((insn & 0xf800) == 0x6800 /* ldr Rd, [sp, #immed] */ + && pv_is_register (regs[bits (insn, 3, 5)], ARM_SP_REGNUM))) + /* Similarly ignore single loads from the stack. */ + ; + else if ((insn & 0xffc0) == 0x0000 /* lsls Rd, Rm, #0 */ + || (insn & 0xffc0) == 0x1c00) /* add Rd, Rn, #0 */ + /* Skip register copies, i.e. saves to another register + instead of the stack. */ + ; + else if ((insn & 0xf800) == 0x2000) /* movs Rd, #imm */ + /* Recognize constant loads; even with small stacks these are necessary + on Thumb. */ + regs[bits (insn, 8, 10)] = pv_constant (bits (insn, 0, 7)); + else if ((insn & 0xf800) == 0x4800) /* ldr Rd, [pc, #imm] */ + { + /* Constant pool loads, for the same reason. */ + unsigned int constant; + CORE_ADDR loc; - for (i = 0; i < 16; i++) - if (pv_area_find_reg (stack, gdbarch, i, &offset)) - cache->saved_regs[i].addr = offset; + loc = start + 4 + bits (insn, 0, 7) * 4; + constant = read_memory_unsigned_integer (loc, 4, byte_order); + regs[bits (insn, 8, 10)] = pv_constant (constant); + } + else if ((insn & 0xe000) == 0xe000) + { + unsigned short inst2; - do_cleanups (back_to); - return start; -} + inst2 = read_memory_unsigned_integer (start + 2, 2, + byte_order_for_code); -/* Advance the PC across any function entry prologue instructions to - reach some "real" code. + if ((insn & 0xf800) == 0xf000 && (inst2 & 0xe800) == 0xe800) + { + /* BL, BLX. Allow some special function calls when + skipping the prologue; GCC generates these before + storing arguments to the stack. */ + CORE_ADDR nextpc; + int j1, j2, imm1, imm2; + + imm1 = sbits (insn, 0, 10); + imm2 = bits (inst2, 0, 10); + j1 = bit (inst2, 13); + j2 = bit (inst2, 11); + + offset = ((imm1 << 12) + (imm2 << 1)); + offset ^= ((!j2) << 22) | ((!j1) << 23); + + nextpc = start + 4 + offset; + /* For BLX make sure to clear the low bits. */ + if (bit (inst2, 12) == 0) + nextpc = nextpc & 0xfffffffc; + + if (!skip_prologue_function (gdbarch, nextpc, + bit (inst2, 12) != 0)) + break; + } - The APCS (ARM Procedure Call Standard) defines the following - prologue: + else if ((insn & 0xffd0) == 0xe900 /* stmdb Rn{!}, + { registers } */ + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + { + pv_t addr = regs[bits (insn, 0, 3)]; + int regno; - mov ip, sp - [stmfd sp!, {a1,a2,a3,a4}] - stmfd sp!, {...,fp,ip,lr,pc} - [stfe f7, [sp, #-12]!] - [stfe f6, [sp, #-12]!] - [stfe f5, [sp, #-12]!] - [stfe f4, [sp, #-12]!] - sub fp, ip, #nn @@ nn == 20 or 4 depending on second insn */ + if (pv_area_store_would_trash (stack, addr)) + break; -static CORE_ADDR -arm_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) -{ - unsigned long inst; - CORE_ADDR skip_pc; - CORE_ADDR func_addr, func_end = 0; - char *func_name; - struct symtab_and_line sal; + /* Calculate offsets of saved registers. */ + for (regno = ARM_LR_REGNUM; regno >= 0; regno--) + if (inst2 & (1 << regno)) + { + addr = pv_add_constant (addr, -4); + pv_area_store (stack, addr, 4, regs[regno]); + } - /* If we're in a dummy frame, don't even try to skip the prologue. */ - if (deprecated_pc_in_call_dummy (pc)) - return pc; + if (insn & 0x0020) + regs[bits (insn, 0, 3)] = addr; + } - /* See what the symbol table says. */ + else if ((insn & 0xff50) == 0xe940 /* strd Rt, Rt2, + [Rn, #+/-imm]{!} */ + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + { + int regno1 = bits (inst2, 12, 15); + int regno2 = bits (inst2, 8, 11); + pv_t addr = regs[bits (insn, 0, 3)]; - if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end)) - { - struct symbol *sym; + offset = inst2 & 0xff; + if (insn & 0x0080) + addr = pv_add_constant (addr, offset); + else + addr = pv_add_constant (addr, -offset); - /* 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; - } - } + if (pv_area_store_would_trash (stack, addr)) + break; - /* Can't find the prologue end in the symbol table, try it the hard way - by disassembling the instructions. */ + pv_area_store (stack, addr, 4, regs[regno1]); + pv_area_store (stack, pv_add_constant (addr, 4), + 4, regs[regno2]); - /* Like arm_scan_prologue, stop no later than pc + 64. */ - if (func_end == 0 || func_end > pc + 64) - func_end = pc + 64; + if (insn & 0x0020) + regs[bits (insn, 0, 3)] = addr; + } - /* Check if this is Thumb code. */ - if (arm_pc_is_thumb (pc)) - return thumb_analyze_prologue (gdbarch, pc, func_end, NULL); + else if ((insn & 0xfff0) == 0xf8c0 /* str Rt,[Rn,+/-#imm]{!} */ + && (inst2 & 0x0c00) == 0x0c00 + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + { + int regno = bits (inst2, 12, 15); + pv_t addr = regs[bits (insn, 0, 3)]; - for (skip_pc = pc; skip_pc < func_end; skip_pc += 4) - { - inst = read_memory_unsigned_integer (skip_pc, 4); + offset = inst2 & 0xff; + if (inst2 & 0x0200) + addr = pv_add_constant (addr, offset); + else + addr = pv_add_constant (addr, -offset); + + if (pv_area_store_would_trash (stack, addr)) + break; + + pv_area_store (stack, addr, 4, regs[regno]); + + if (inst2 & 0x0100) + regs[bits (insn, 0, 3)] = addr; + } + + else if ((insn & 0xfff0) == 0xf8c0 /* str.w Rt,[Rn,#imm] */ + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + { + int regno = bits (inst2, 12, 15); + pv_t addr; + + offset = inst2 & 0xfff; + addr = pv_add_constant (regs[bits (insn, 0, 3)], offset); + + if (pv_area_store_would_trash (stack, addr)) + break; + + pv_area_store (stack, addr, 4, regs[regno]); + } + + else if ((insn & 0xffd0) == 0xf880 /* str{bh}.w Rt,[Rn,#imm] */ + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + /* Ignore stores of argument registers to the stack. */ + ; + + else if ((insn & 0xffd0) == 0xf800 /* str{bh} Rt,[Rn,#+/-imm] */ + && (inst2 & 0x0d00) == 0x0c00 + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + /* Ignore stores of argument registers to the stack. */ + ; + + else if ((insn & 0xffd0) == 0xe890 /* ldmia Rn[!], + { registers } */ + && (inst2 & 0x8000) == 0x0000 + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + /* Ignore block loads from the stack, potentially copying + parameters from memory. */ + ; + + else if ((insn & 0xffb0) == 0xe950 /* ldrd Rt, Rt2, + [Rn, #+/-imm] */ + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + /* Similarly ignore dual loads from the stack. */ + ; + + else if ((insn & 0xfff0) == 0xf850 /* ldr Rt,[Rn,#+/-imm] */ + && (inst2 & 0x0d00) == 0x0c00 + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + /* Similarly ignore single loads from the stack. */ + ; + + else if ((insn & 0xfff0) == 0xf8d0 /* ldr.w Rt,[Rn,#imm] */ + && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) + /* Similarly ignore single loads from the stack. */ + ; + + else if ((insn & 0xfbf0) == 0xf100 /* add.w Rd, Rn, #imm */ + && (inst2 & 0x8000) == 0x0000) + { + unsigned int imm = ((bits (insn, 10, 10) << 11) + | (bits (inst2, 12, 14) << 8) + | bits (inst2, 0, 7)); + + regs[bits (inst2, 8, 11)] + = pv_add_constant (regs[bits (insn, 0, 3)], + thumb_expand_immediate (imm)); + } + + else if ((insn & 0xfbf0) == 0xf200 /* addw Rd, Rn, #imm */ + && (inst2 & 0x8000) == 0x0000) + { + unsigned int imm = ((bits (insn, 10, 10) << 11) + | (bits (inst2, 12, 14) << 8) + | bits (inst2, 0, 7)); + + regs[bits (inst2, 8, 11)] + = pv_add_constant (regs[bits (insn, 0, 3)], imm); + } + + else if ((insn & 0xfbf0) == 0xf1a0 /* sub.w Rd, Rn, #imm */ + && (inst2 & 0x8000) == 0x0000) + { + unsigned int imm = ((bits (insn, 10, 10) << 11) + | (bits (inst2, 12, 14) << 8) + | bits (inst2, 0, 7)); + + regs[bits (inst2, 8, 11)] + = pv_add_constant (regs[bits (insn, 0, 3)], + - (CORE_ADDR) thumb_expand_immediate (imm)); + } + + else if ((insn & 0xfbf0) == 0xf2a0 /* subw Rd, Rn, #imm */ + && (inst2 & 0x8000) == 0x0000) + { + unsigned int imm = ((bits (insn, 10, 10) << 11) + | (bits (inst2, 12, 14) << 8) + | bits (inst2, 0, 7)); + + regs[bits (inst2, 8, 11)] + = pv_add_constant (regs[bits (insn, 0, 3)], - (CORE_ADDR) imm); + } + + else if ((insn & 0xfbff) == 0xf04f) /* mov.w Rd, #const */ + { + unsigned int imm = ((bits (insn, 10, 10) << 11) + | (bits (inst2, 12, 14) << 8) + | bits (inst2, 0, 7)); + + regs[bits (inst2, 8, 11)] + = pv_constant (thumb_expand_immediate (imm)); + } + + else if ((insn & 0xfbf0) == 0xf240) /* movw Rd, #const */ + { + unsigned int imm + = EXTRACT_MOVW_MOVT_IMM_T (insn, inst2); + + regs[bits (inst2, 8, 11)] = pv_constant (imm); + } + + else if (insn == 0xea5f /* mov.w Rd,Rm */ + && (inst2 & 0xf0f0) == 0) + { + int dst_reg = (inst2 & 0x0f00) >> 8; + int src_reg = inst2 & 0xf; + regs[dst_reg] = regs[src_reg]; + } + + else if ((insn & 0xff7f) == 0xf85f) /* ldr.w Rt,