X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Farm-tdep.c;h=f3a6325b5ddd7c7196a46cf106af7f590a3d18a0;hb=b480a4819bb85f0d8004cc0904e7a2eeb03cb2b7;hp=4c1810df300a51fcce118e188a8ef724414e07fd;hpb=4db71c0b79ff7e89a392f903f39251faa795d69c;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index 4c1810df30..f3a6325b5d 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -1,8 +1,6 @@ /* 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, 2009, 2010, 2011 - Free Software Foundation, Inc. + Copyright (C) 1988-2015 Free Software Foundation, Inc. This file is part of GDB. @@ -19,14 +17,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#include "defs.h" + #include /* XXX for isupper (). */ -#include "defs.h" #include "frame.h" #include "inferior.h" +#include "infrun.h" #include "gdbcmd.h" #include "gdbcore.h" -#include "gdb_string.h" #include "dis-asm.h" /* For register styles. */ #include "regcache.h" #include "reggroups.h" @@ -41,6 +40,7 @@ #include "dwarf2-frame.h" #include "gdbtypes.h" #include "prologue-value.h" +#include "remote.h" #include "target-descriptions.h" #include "user-regs.h" #include "observer.h" @@ -52,10 +52,18 @@ #include "coff/internal.h" #include "elf/arm.h" -#include "gdb_assert.h" #include "vec.h" +#include "record.h" +#include "record-full.h" + #include "features/arm-with-m.c" +#include "features/arm-with-m-fpa-layout.c" +#include "features/arm-with-m-vfp-d16.c" +#include "features/arm-with-iwmmxt.c" +#include "features/arm-with-vfpv2.c" +#include "features/arm-with-vfpv3.c" +#include "features/arm-with-neon.c" static int arm_debug; @@ -94,7 +102,7 @@ static struct cmd_list_element *showarmcmdlist = NULL; /* The type of floating-point to use. Keep this in sync with enum arm_float_model, and the help string in _initialize_arm_tdep. */ -static const char *fp_model_strings[] = +static const char *const fp_model_strings[] = { "auto", "softfpa", @@ -109,7 +117,7 @@ static enum arm_float_model arm_fp_model = ARM_FLOAT_AUTO; static const char *current_fp_model = "auto"; /* The ABI to use. Keep this in sync with arm_abi_kind. */ -static const char *arm_abi_strings[] = +static const char *const arm_abi_strings[] = { "auto", "APCS", @@ -122,7 +130,7 @@ 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[] = +static const char *const arm_mode_strings[] = { "auto", "arm", @@ -133,6 +141,13 @@ static const char *arm_mode_strings[] = static const char *arm_fallback_mode_string = "auto"; static const char *arm_force_mode_string = "auto"; +/* Internal override of the execution mode. -1 means no override, + 0 means override to ARM mode, 1 means override to Thumb mode. + The effect is the same as if arm_force_mode has been set by the + user (except the internal override has precedence over a user's + arm_force_mode override). */ +static int arm_override_mode = -1; + /* Number of different reg name sets (options). */ static int num_disassembly_options; @@ -213,13 +228,15 @@ static void convert_from_extended (const struct floatformat *, const void *, static void convert_to_extended (const struct floatformat *, void *, const void *, int); -static void arm_neon_quad_read (struct gdbarch *gdbarch, - struct regcache *regcache, - int regnum, gdb_byte *buf); +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); +static int thumb_insn_size (unsigned short inst1); + struct arm_prologue_cache { /* The stack pointer at the time this frame was created; i.e. the @@ -356,9 +373,6 @@ arm_find_mapping_symbol (CORE_ADDR memaddr, CORE_ADDR *start) 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. This function should be called for addresses unrelated to any executing frame; otherwise, prefer arm_frame_is_thumb. */ @@ -366,8 +380,7 @@ static CORE_ADDR arm_get_next_pc_raw (struct frame_info *frame, int arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr) { - struct obj_section *sec; - struct minimal_symbol *sym; + struct bound_minimal_symbol sym; char type; struct displaced_step_closure* dsc = get_displaced_step_closure_by_addr(memaddr); @@ -388,6 +401,10 @@ arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr) if (IS_THUMB_ADDR (memaddr)) return 1; + /* Respect internal mode override if active. */ + if (arm_override_mode != -1) + return arm_override_mode; + /* If the user wants to override the symbol table, let him. */ if (strcmp (arm_force_mode_string, "arm") == 0) return 0; @@ -405,8 +422,8 @@ arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr) /* 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 (sym.minsym) + return (MSYMBOL_IS_SPECIAL (sym.minsym)); /* If the user wants to override the fallback mode, let them. */ if (strcmp (arm_fallback_mode_string, "arm") == 0) @@ -418,29 +435,9 @@ arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr) 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. */ + displayed in the mode it will be executed). */ if (target_has_registers) - { - 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; - } - } + return arm_frame_is_thumb (get_current_frame ()); /* Otherwise we're out of luck; we assume ARM. */ return 0; @@ -450,20 +447,18 @@ arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr) static CORE_ADDR arm_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR val) { + /* On M-profile devices, do not strip the low bit from EXC_RETURN + (the magic exception return address). */ + if (gdbarch_tdep (gdbarch)->is_m + && (val & 0xfffffff0) == 0xfffffff0) + return val; + if (arm_apcs_32) return UNMAKE_THUMB_ADDR (val); else return (val & 0x03fffffc); } -/* 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 (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 @@ -472,14 +467,14 @@ 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; + struct bound_minimal_symbol msym; msym = lookup_minimal_symbol_by_pc (pc); - if (msym != NULL - && SYMBOL_VALUE_ADDRESS (msym) == pc - && SYMBOL_LINKAGE_NAME (msym) != NULL) + if (msym.minsym != NULL + && BMSYMBOL_VALUE_ADDRESS (msym) == pc + && MSYMBOL_LINKAGE_NAME (msym.minsym) != NULL) { - const char *name = SYMBOL_LINKAGE_NAME (msym); + const char *name = MSYMBOL_LINKAGE_NAME (msym.minsym); /* The GNU linker's Thumb call stub to foo is named __foo_from_thumb. */ @@ -525,7 +520,7 @@ skip_prologue_function (struct gdbarch *gdbarch, CORE_ADDR pc, int is_thumb) #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))) + ((CORE_ADDR) (((unsigned 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 @@ -688,6 +683,17 @@ thumb2_instruction_changes_pc (unsigned short inst1, unsigned short inst2) return 0; } +/* Return 1 if the 16-bit Thumb instruction INSN restores SP in + epilogue, 0 otherwise. */ + +static int +thumb_instruction_restores_sp (unsigned short insn) +{ + return (insn == 0x46bd /* mov sp, r7 */ + || (insn & 0xff80) == 0xb000 /* add sp, imm */ + || (insn & 0xfe00) == 0xbc00); /* pop */ +} + /* 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. @@ -740,16 +746,16 @@ thumb_analyze_prologue (struct gdbarch *gdbarch, pv_area_store (stack, regs[ARM_SP_REGNUM], 4, regs[regno]); } } - else if ((insn & 0xff00) == 0xb000) /* add sp, #simm OR - sub sp, #simm */ + else if ((insn & 0xff80) == 0xb080) /* sub sp, #imm */ { offset = (insn & 0x7f) << 2; /* get scaled offset */ - if (insn & 0x80) /* Check for SUB. */ - regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], - -offset); - else - regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], - offset); + regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], + -offset); + } + else if (thumb_instruction_restores_sp (insn)) + { + /* Don't scan past the epilogue. */ + break; } else if ((insn & 0xf800) == 0xa800) /* add Rd, sp, #imm */ regs[bits (insn, 8, 10)] = pv_add_constant (regs[ARM_SP_REGNUM], @@ -844,7 +850,7 @@ thumb_analyze_prologue (struct gdbarch *gdbarch, constant = read_memory_unsigned_integer (loc, 4, byte_order); regs[bits (insn, 8, 10)] = pv_constant (constant); } - else if ((insn & 0xe000) == 0xe000) + else if (thumb_insn_size (insn) == 4) /* 32-bit Thumb-2 instructions. */ { unsigned short inst2; @@ -1075,7 +1081,7 @@ thumb_analyze_prologue (struct gdbarch *gdbarch, unsigned int constant; CORE_ADDR loc; - offset = bits (insn, 0, 11); + offset = bits (inst2, 0, 11); if (insn & 0x0080) loc = start + 4 + offset; else @@ -1091,7 +1097,7 @@ thumb_analyze_prologue (struct gdbarch *gdbarch, unsigned int constant; CORE_ADDR loc; - offset = bits (insn, 0, 7) << 2; + offset = bits (inst2, 0, 7) << 2; if (insn & 0x0080) loc = start + 4 + offset; else @@ -1158,18 +1164,12 @@ thumb_analyze_prologue (struct gdbarch *gdbarch, cache->framereg = THUMB_FP_REGNUM; cache->framesize = -regs[THUMB_FP_REGNUM].k; } - else if (pv_is_register (regs[ARM_SP_REGNUM], ARM_SP_REGNUM)) + else { /* 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; - } for (i = 0; i < 16; i++) if (pv_area_find_reg (stack, gdbarch, i, &offset)) @@ -1204,7 +1204,9 @@ arm_analyze_load_stack_chk_guard(CORE_ADDR pc, struct gdbarch *gdbarch, { *destreg = bits (insn1, 8, 10); *offset = 2; - address = bits (insn1, 0, 7); + address = (pc & 0xfffffffc) + 4 + (bits (insn1, 0, 7) << 2); + address = read_memory_unsigned_integer (address, 4, + byte_order_for_code); } else if ((insn1 & 0xfbf0) == 0xf240) /* movw Rd, #const */ { @@ -1233,9 +1235,12 @@ arm_analyze_load_stack_chk_guard(CORE_ADDR pc, struct gdbarch *gdbarch, unsigned int insn = read_memory_unsigned_integer (pc, 4, byte_order_for_code); - if ((insn & 0x0e5f0000) == 0x041f0000) /* ldr Rd, #immed */ + if ((insn & 0x0e5f0000) == 0x041f0000) /* ldr Rd, [PC, #immed] */ { - address = bits (insn, 0, 11); + address = bits (insn, 0, 11) + pc + 8; + address = read_memory_unsigned_integer (address, 4, + byte_order_for_code); + *destreg = bits (insn, 12, 15); *offset = 4; } @@ -1293,8 +1298,8 @@ static CORE_ADDR arm_skip_stack_protector(CORE_ADDR pc, struct gdbarch *gdbarch) { enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); - unsigned int address, basereg; - struct minimal_symbol *stack_chk_guard; + unsigned int basereg; + struct bound_minimal_symbol stack_chk_guard; int offset; int is_thumb = arm_pc_is_thumb (gdbarch, pc); CORE_ADDR addr; @@ -1306,11 +1311,11 @@ arm_skip_stack_protector(CORE_ADDR pc, struct gdbarch *gdbarch) return pc; stack_chk_guard = lookup_minimal_symbol_by_pc (addr); - /* If name of symbol doesn't start with '__stack_chk_guard', this - instruction sequence is not for stack protector. If symbol is - removed, we conservatively think this sequence is for stack protector. */ - if (stack_chk_guard - && strncmp (SYMBOL_LINKAGE_NAME (stack_chk_guard), "__stack_chk_guard", + /* ADDR must correspond to a symbol whose name is __stack_chk_guard. + Otherwise, this sequence cannot be for stack protector. */ + if (stack_chk_guard.minsym == NULL + || strncmp (MSYMBOL_LINKAGE_NAME (stack_chk_guard.minsym), + "__stack_chk_guard", strlen ("__stack_chk_guard")) != 0) return pc; @@ -1383,9 +1388,7 @@ arm_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) { enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); unsigned long inst; - CORE_ADDR skip_pc; CORE_ADDR func_addr, limit_pc; - struct symtab_and_line sal; /* 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 @@ -1394,7 +1397,7 @@ arm_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) { CORE_ADDR post_prologue_pc = skip_prologue_using_sal (gdbarch, func_addr); - struct symtab *s = find_pc_symtab (func_addr); + struct compunit_symtab *cust = find_pc_compunit_symtab (func_addr); if (post_prologue_pc) post_prologue_pc @@ -1408,9 +1411,12 @@ arm_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) will have producer information for most binaries; if it is missing (e.g. for -gstabs), assuming the GNU tools. */ if (post_prologue_pc - && (s == NULL - || s->producer == NULL - || strncmp (s->producer, "GNU ", sizeof ("GNU ") - 1) == 0)) + && (cust == NULL + || COMPUNIT_PRODUCER (cust) == NULL + || strncmp (COMPUNIT_PRODUCER (cust), "GNU ", + sizeof ("GNU ") - 1) == 0 + || strncmp (COMPUNIT_PRODUCER (cust), "clang ", + sizeof ("clang ") - 1) == 0)) return post_prologue_pc; if (post_prologue_pc != 0) @@ -1455,65 +1461,8 @@ arm_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) /* Check if this is Thumb code. */ if (arm_pc_is_thumb (gdbarch, pc)) return thumb_analyze_prologue (gdbarch, pc, limit_pc, NULL); - - for (skip_pc = pc; skip_pc < limit_pc; skip_pc += 4) - { - inst = read_memory_unsigned_integer (skip_pc, 4, byte_order_for_code); - - /* "mov ip, sp" is no longer a required part of the prologue. */ - if (inst == 0xe1a0c00d) /* mov ip, sp */ - continue; - - if ((inst & 0xfffff000) == 0xe28dc000) /* add ip, sp #n */ - continue; - - if ((inst & 0xfffff000) == 0xe24dc000) /* sub ip, sp #n */ - continue; - - /* Some prologues begin with "str lr, [sp, #-4]!". */ - if (inst == 0xe52de004) /* str lr, [sp, #-4]! */ - continue; - - if ((inst & 0xfffffff0) == 0xe92d0000) /* stmfd sp!,{a1,a2,a3,a4} */ - continue; - - if ((inst & 0xfffff800) == 0xe92dd800) /* stmfd sp!,{fp,ip,lr,pc} */ - continue; - - /* Any insns after this point may float into the code, if it makes - for better instruction scheduling, so we skip them only if we - find them, but still consider the function to be frame-ful. */ - - /* We may have either one sfmfd instruction here, or several stfe - insns, depending on the version of floating point code we - support. */ - if ((inst & 0xffbf0fff) == 0xec2d0200) /* sfmfd fn, , [sp]! */ - continue; - - if ((inst & 0xffff8fff) == 0xed6d0103) /* stfe fn, [sp, #-12]! */ - continue; - - if ((inst & 0xfffff000) == 0xe24cb000) /* sub fp, ip, #nn */ - continue; - - if ((inst & 0xfffff000) == 0xe24dd000) /* sub sp, sp, #nn */ - continue; - - if ((inst & 0xffffc000) == 0xe54b0000 /* strb r(0123),[r11,#-nn] */ - || (inst & 0xffffc0f0) == 0xe14b00b0 /* strh r(0123),[r11,#-nn] */ - || (inst & 0xffffc000) == 0xe50b0000) /* str r(0123),[r11,#-nn] */ - continue; - - if ((inst & 0xffffc000) == 0xe5cd0000 /* strb r(0123),[sp,#nn] */ - || (inst & 0xffffc0f0) == 0xe1cd00b0 /* strh r(0123),[sp,#nn] */ - || (inst & 0xffffc000) == 0xe58d0000) /* str r(0123),[sp,#nn] */ - continue; - - /* Un-recognized instruction; stop scanning. */ - break; - } - - return skip_pc; /* End of prologue. */ + else + return arm_analyze_prologue (gdbarch, pc, limit_pc, NULL); } /* *INDENT-OFF* */ @@ -1544,7 +1493,6 @@ thumb_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR prev_pc, { CORE_ADDR prologue_start; CORE_ADDR prologue_end; - CORE_ADDR current_pc; if (find_pc_partial_function (block_addr, NULL, &prologue_start, &prologue_end)) @@ -1658,6 +1606,30 @@ arm_instruction_changes_pc (uint32_t this_instr) } } +/* Return 1 if the ARM instruction INSN restores SP in epilogue, 0 + otherwise. */ + +static int +arm_instruction_restores_sp (unsigned int insn) +{ + if (bits (insn, 28, 31) != INST_NV) + { + if ((insn & 0x0df0f000) == 0x0080d000 + /* ADD SP (register or immediate). */ + || (insn & 0x0df0f000) == 0x0040d000 + /* SUB SP (register or immediate). */ + || (insn & 0x0ffffff0) == 0x01a0d000 + /* MOV SP. */ + || (insn & 0x0fff0000) == 0x08bd0000 + /* POP (LDMIA). */ + || (insn & 0x0fff0000) == 0x049d0000) + /* POP of a single register. */ + return 1; + } + + return 0; +} + /* Analyze an ARM mode prologue starting at PROLOGUE_START and continuing no further than PROLOGUE_END. If CACHE is non-NULL, fill it in. Return the first address not recognized as a prologue @@ -1680,7 +1652,6 @@ arm_analyze_prologue (struct gdbarch *gdbarch, pv_t regs[ARM_FPS_REGNUM]; struct pv_area *stack; struct cleanup *back_to; - int framereg, framesize; CORE_ADDR unrecognized_pc = 0; /* Search the prologue looking for instructions that set up the @@ -1856,62 +1827,62 @@ arm_analyze_prologue (struct gdbarch *gdbarch, else if (arm_instruction_changes_pc (insn)) /* Don't scan past anything that might change control flow. */ break; - else if ((insn & 0xfe500000) == 0xe8100000) /* ldm */ - { - /* Ignore block loads from the stack, potentially copying - parameters from memory. */ - if (pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM)) - continue; - else - break; - } - else if ((insn & 0xfc500000) == 0xe4100000) + else if (arm_instruction_restores_sp (insn)) { - /* Similarly ignore single loads from the stack. */ - if (pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM)) - continue; - else - break; + /* Don't scan past the epilogue. */ + break; } + else if ((insn & 0xfe500000) == 0xe8100000 /* ldm */ + && pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM)) + /* Ignore block loads from the stack, potentially copying + parameters from memory. */ + continue; + else if ((insn & 0xfc500000) == 0xe4100000 + && pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM)) + /* Similarly ignore single loads from the stack. */ + continue; else if ((insn & 0xffff0ff0) == 0xe1a00000) /* MOV Rd, Rm. Skip register copies, i.e. saves to another register instead of the stack. */ continue; else { - /* The optimizer might shove anything into the prologue, - so we just skip what we don't recognize. */ + /* The optimizer might shove anything into the prologue, if + we build up cache (cache != NULL) from scanning prologue, + we just skip what we don't recognize and scan further to + make cache as complete as possible. However, if we skip + prologue, we'll stop immediately on unrecognized + instruction. */ unrecognized_pc = current_pc; - continue; + if (cache != NULL) + continue; + else + break; } } if (unrecognized_pc == 0) unrecognized_pc = current_pc; - /* 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. */ - framereg = ARM_FP_REGNUM; - 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. */ - framereg = ARM_SP_REGNUM; - framesize = -regs[ARM_SP_REGNUM].k; - } - else - { - /* We're just out of luck. We don't know where the frame is. */ - framereg = -1; - framesize = 0; - } - if (cache) { + int framereg, framesize; + + /* 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. */ + framereg = ARM_FP_REGNUM; + framesize = -regs[ARM_FP_REGNUM].k; + } + else + { + /* Try the stack pointer... this is a bit desperate. */ + framereg = ARM_SP_REGNUM; + framesize = -regs[ARM_SP_REGNUM].k; + } + cache->framereg = framereg; cache->framesize = framesize; @@ -2050,6 +2021,31 @@ arm_make_prologue_cache (struct frame_info *this_frame) return cache; } +/* Implementation of the stop_reason hook for arm_prologue frames. */ + +static enum unwind_stop_reason +arm_prologue_unwind_stop_reason (struct frame_info *this_frame, + void **this_cache) +{ + struct arm_prologue_cache *cache; + CORE_ADDR pc; + + if (*this_cache == NULL) + *this_cache = arm_make_prologue_cache (this_frame); + cache = *this_cache; + + /* 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 UNWIND_OUTERMOST; + + /* If we've hit a wall, stop. */ + if (cache->prev_sp == 0) + return UNWIND_OUTERMOST; + + return UNWIND_NO_REASON; +} + /* Our frame ID for a normal frame is the current function's starting PC and the caller's SP when we were called. */ @@ -2066,18 +2062,10 @@ arm_prologue_this_id (struct frame_info *this_frame, *this_cache = arm_make_prologue_cache (this_frame); cache = *this_cache; - /* 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; - /* Use function start address as part of the frame ID. If we cannot identify the start address (due to missing symbol information), fall back to just using the current PC. */ + pc = get_frame_pc (this_frame); func = get_frame_func (this_frame); if (!func) func = pc; @@ -2146,6 +2134,7 @@ arm_prologue_prev_register (struct frame_info *this_frame, struct frame_unwind arm_prologue_unwind = { NORMAL_FRAME, + arm_prologue_unwind_stop_reason, arm_prologue_this_id, arm_prologue_prev_register, NULL, @@ -2226,7 +2215,7 @@ arm_obj_section_from_vma (struct objfile *objfile, bfd_vma vma) static void arm_exidx_new_objfile (struct objfile *objfile) { - struct cleanup *cleanups = make_cleanup (null_cleanup, NULL); + struct cleanup *cleanups; struct arm_exidx_data *data; asection *exidx, *extab; bfd_vma exidx_vma = 0, extab_vma = 0; @@ -2237,6 +2226,7 @@ arm_exidx_new_objfile (struct objfile *objfile) /* If we've already touched this file, do nothing. */ if (!objfile || objfile_data (objfile, arm_exidx_data_key) != NULL) return; + cleanups = make_cleanup (null_cleanup, NULL); /* Read contents of exception table and index. */ exidx = bfd_get_section_by_name (objfile->obfd, ".ARM.exidx"); @@ -2884,12 +2874,71 @@ arm_exidx_unwind_sniffer (const struct frame_unwind *self, struct frame_unwind arm_exidx_unwind = { NORMAL_FRAME, + default_frame_unwind_stop_reason, arm_prologue_this_id, arm_prologue_prev_register, NULL, arm_exidx_unwind_sniffer }; +/* Recognize GCC's trampoline for thumb call-indirect. If we are in a + trampoline, return the target PC. Otherwise return 0. + + void call0a (char c, short s, int i, long l) {} + + int main (void) + { + (*pointer_to_call0a) (c, s, i, l); + } + + Instead of calling a stub library function _call_via_xx (xx is + the register name), GCC may inline the trampoline in the object + file as below (register r2 has the address of call0a). + + .global main + .type main, %function + ... + bl .L1 + ... + .size main, .-main + + .L1: + bx r2 + + The trampoline 'bx r2' doesn't belong to main. */ + +static CORE_ADDR +arm_skip_bx_reg (struct frame_info *frame, CORE_ADDR pc) +{ + /* The heuristics of recognizing such trampoline is that FRAME is + executing in Thumb mode and the instruction on PC is 'bx Rm'. */ + if (arm_frame_is_thumb (frame)) + { + gdb_byte buf[2]; + + if (target_read_memory (pc, buf, 2) == 0) + { + struct gdbarch *gdbarch = get_frame_arch (frame); + enum bfd_endian byte_order_for_code + = gdbarch_byte_order_for_code (gdbarch); + uint16_t insn + = extract_unsigned_integer (buf, 2, byte_order_for_code); + + if ((insn & 0xff80) == 0x4700) /* bx */ + { + CORE_ADDR dest + = get_frame_register_unsigned (frame, bits (insn, 3, 6)); + + /* Clear the LSB so that gdb core sets step-resume + breakpoint at the right address. */ + return UNMAKE_THUMB_ADDR (dest); + } + } + } + + return 0; +} + static struct arm_prologue_cache * arm_make_stub_cache (struct frame_info *this_frame) { @@ -2925,13 +2974,20 @@ arm_stub_unwind_sniffer (const struct frame_unwind *self, void **this_prologue_cache) { CORE_ADDR addr_in_block; - char dummy[4]; + gdb_byte dummy[4]; + CORE_ADDR pc, start_addr; + const char *name; addr_in_block = get_frame_address_in_block (this_frame); - if (in_plt_section (addr_in_block, NULL) + pc = get_frame_pc (this_frame); + if (in_plt_section (addr_in_block) /* We also use the stub winder if the target memory is unreadable to avoid having the prologue unwinder trying to read it. */ - || target_read_memory (get_frame_pc (this_frame), dummy, 4) != 0) + || target_read_memory (pc, dummy, 4) != 0) + return 1; + + if (find_pc_partial_function (pc, &name, &start_addr, NULL) == 0 + && arm_skip_bx_reg (this_frame, pc) != 0) return 1; return 0; @@ -2939,12 +2995,134 @@ arm_stub_unwind_sniffer (const struct frame_unwind *self, struct frame_unwind arm_stub_unwind = { NORMAL_FRAME, + default_frame_unwind_stop_reason, arm_stub_this_id, arm_prologue_prev_register, NULL, arm_stub_unwind_sniffer }; +/* Put here the code to store, into CACHE->saved_regs, the addresses + of the saved registers of frame described by THIS_FRAME. CACHE is + returned. */ + +static struct arm_prologue_cache * +arm_m_exception_cache (struct frame_info *this_frame) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + struct arm_prologue_cache *cache; + CORE_ADDR unwound_sp; + LONGEST xpsr; + + cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache); + cache->saved_regs = trad_frame_alloc_saved_regs (this_frame); + + unwound_sp = get_frame_register_unsigned (this_frame, + ARM_SP_REGNUM); + + /* The hardware saves eight 32-bit words, comprising xPSR, + ReturnAddress, LR (R14), R12, R3, R2, R1, R0. See details in + "B1.5.6 Exception entry behavior" in + "ARMv7-M Architecture Reference Manual". */ + cache->saved_regs[0].addr = unwound_sp; + cache->saved_regs[1].addr = unwound_sp + 4; + cache->saved_regs[2].addr = unwound_sp + 8; + cache->saved_regs[3].addr = unwound_sp + 12; + cache->saved_regs[12].addr = unwound_sp + 16; + cache->saved_regs[14].addr = unwound_sp + 20; + cache->saved_regs[15].addr = unwound_sp + 24; + cache->saved_regs[ARM_PS_REGNUM].addr = unwound_sp + 28; + + /* If bit 9 of the saved xPSR is set, then there is a four-byte + aligner between the top of the 32-byte stack frame and the + previous context's stack pointer. */ + cache->prev_sp = unwound_sp + 32; + if (safe_read_memory_integer (unwound_sp + 28, 4, byte_order, &xpsr) + && (xpsr & (1 << 9)) != 0) + cache->prev_sp += 4; + + return cache; +} + +/* Implementation of function hook 'this_id' in + 'struct frame_uwnind'. */ + +static void +arm_m_exception_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_m_exception_cache (this_frame); + cache = *this_cache; + + /* Our frame ID for a stub frame is the current SP and LR. */ + *this_id = frame_id_build (cache->prev_sp, + get_frame_pc (this_frame)); +} + +/* Implementation of function hook 'prev_register' in + 'struct frame_uwnind'. */ + +static struct value * +arm_m_exception_prev_register (struct frame_info *this_frame, + void **this_cache, + int prev_regnum) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + struct arm_prologue_cache *cache; + + if (*this_cache == NULL) + *this_cache = arm_m_exception_cache (this_frame); + cache = *this_cache; + + /* 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); + + return trad_frame_get_prev_register (this_frame, cache->saved_regs, + prev_regnum); +} + +/* Implementation of function hook 'sniffer' in + 'struct frame_uwnind'. */ + +static int +arm_m_exception_unwind_sniffer (const struct frame_unwind *self, + struct frame_info *this_frame, + void **this_prologue_cache) +{ + CORE_ADDR this_pc = get_frame_pc (this_frame); + + /* No need to check is_m; this sniffer is only registered for + M-profile architectures. */ + + /* Exception frames return to one of these magic PCs. Other values + are not defined as of v7-M. See details in "B1.5.8 Exception + return behavior" in "ARMv7-M Architecture Reference Manual". */ + if (this_pc == 0xfffffff1 || this_pc == 0xfffffff9 + || this_pc == 0xfffffffd) + return 1; + + return 0; +} + +/* Frame unwinder for M-profile exceptions. */ + +struct frame_unwind arm_m_exception_unwind = +{ + SIGTRAMP_FRAME, + default_frame_unwind_stop_reason, + arm_m_exception_this_id, + arm_m_exception_prev_register, + NULL, + arm_m_exception_unwind_sniffer +}; + static CORE_ADDR arm_normal_frame_base (struct frame_info *this_frame, void **this_cache) { @@ -3095,17 +3273,12 @@ thumb_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) found_return = 1; else if (insn == 0x46f7) /* mov pc, lr */ found_return = 1; - else if (insn == 0x46bd) /* mov sp, r7 */ - found_stack_adjust = 1; - else if ((insn & 0xff00) == 0xb000) /* add sp, imm or sub sp, imm */ - found_stack_adjust = 1; - else if ((insn & 0xfe00) == 0xbc00) /* pop */ - { - found_stack_adjust = 1; - if (insn & 0x0100) /* include PC. */ + else if (thumb_instruction_restores_sp (insn)) + { + if ((insn & 0xff00) == 0xbd00) /* pop */ found_return = 1; } - else if ((insn & 0xe000) == 0xe000) /* 32-bit Thumb-2 instruction */ + else if (thumb_insn_size (insn) == 4) /* 32-bit Thumb-2 instruction */ { if (target_read_memory (scan_pc, buf, 2)) break; @@ -3115,20 +3288,18 @@ thumb_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) if (insn == 0xe8bd) /* ldm.w sp!, */ { - found_stack_adjust = 1; if (insn2 & 0x8000) /* include PC. */ found_return = 1; } else if (insn == 0xf85d /* ldr.w , [sp], #4 */ && (insn2 & 0x0fff) == 0x0b04) { - found_stack_adjust = 1; if ((insn2 & 0xf000) == 0xf000) /* is PC. */ found_return = 1; } else if ((insn & 0xffbf) == 0xecbd /* vldm sp!, */ && (insn2 & 0x0e00) == 0x0a00) - found_stack_adjust = 1; + ; else break; } @@ -3145,31 +3316,24 @@ thumb_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) a 32-bit instruction. This is just a heuristic, so we do not worry too much about false positives. */ - if (!found_stack_adjust) - { - if (pc - 4 < func_start) - return 0; - if (target_read_memory (pc - 4, buf, 4)) - return 0; + if (pc - 4 < func_start) + return 0; + if (target_read_memory (pc - 4, buf, 4)) + return 0; - insn = extract_unsigned_integer (buf, 2, byte_order_for_code); - insn2 = extract_unsigned_integer (buf + 2, 2, byte_order_for_code); - - if (insn2 == 0x46bd) /* mov sp, r7 */ - found_stack_adjust = 1; - else if ((insn2 & 0xff00) == 0xb000) /* add sp, imm or sub sp, imm */ - found_stack_adjust = 1; - else if ((insn2 & 0xff00) == 0xbc00) /* pop without PC */ - found_stack_adjust = 1; - else if (insn == 0xe8bd) /* ldm.w sp!, */ - found_stack_adjust = 1; - else if (insn == 0xf85d /* ldr.w , [sp], #4 */ - && (insn2 & 0x0fff) == 0x0b04) - found_stack_adjust = 1; - else if ((insn & 0xffbf) == 0xecbd /* vldm sp!, */ - && (insn2 & 0x0e00) == 0x0a00) - found_stack_adjust = 1; - } + insn = extract_unsigned_integer (buf, 2, byte_order_for_code); + insn2 = extract_unsigned_integer (buf + 2, 2, byte_order_for_code); + + if (thumb_instruction_restores_sp (insn2)) + found_stack_adjust = 1; + else if (insn == 0xe8bd) /* ldm.w sp!, */ + found_stack_adjust = 1; + else if (insn == 0xf85d /* ldr.w , [sp], #4 */ + && (insn2 & 0x0fff) == 0x0b04) + found_stack_adjust = 1; + else if ((insn & 0xffbf) == 0xecbd /* vldm sp!, */ + && (insn2 & 0x0e00) == 0x0a00) + found_stack_adjust = 1; return found_stack_adjust; } @@ -3182,7 +3346,7 @@ arm_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) { enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); unsigned int insn; - int found_return, found_stack_adjust; + int found_return; CORE_ADDR func_start, func_end; if (arm_pc_is_thumb (gdbarch, pc)) @@ -3222,25 +3386,8 @@ arm_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) if (pc < func_start + 4) return 0; - found_stack_adjust = 0; insn = read_memory_unsigned_integer (pc - 4, 4, byte_order_for_code); - if (bits (insn, 28, 31) != INST_NV) - { - if ((insn & 0x0df0f000) == 0x0080d000) - /* ADD SP (register or immediate). */ - found_stack_adjust = 1; - else if ((insn & 0x0df0f000) == 0x0040d000) - /* SUB SP (register or immediate). */ - found_stack_adjust = 1; - else if ((insn & 0x0ffffff0) == 0x01a0d000) - /* MOV SP. */ - found_stack_adjust = 1; - else if ((insn & 0x0fff0000) == 0x08bd0000) - /* POP (LDMIA). */ - found_stack_adjust = 1; - } - - if (found_stack_adjust) + if (arm_instruction_restores_sp (insn)) return 1; return 0; @@ -3303,7 +3450,6 @@ arm_type_align (struct type *t) case TYPE_CODE_FLT: case TYPE_CODE_SET: case TYPE_CODE_RANGE: - case TYPE_CODE_BITSTRING: case TYPE_CODE_REF: case TYPE_CODE_CHAR: case TYPE_CODE_BOOL: @@ -3392,8 +3538,8 @@ arm_vfp_cprc_reg_char (enum arm_vfp_cprc_base_type b) classified from *BASE_TYPE, or two types differently classified from each other, return -1, otherwise return the total number of base-type elements found (possibly 0 in an empty structure or - array). Vectors and complex types are not currently supported, - matching the generic AAPCS support. */ + array). Vector types are not currently supported, matching the + generic AAPCS support. */ static int arm_vfp_cprc_sub_candidate (struct type *t, @@ -3424,23 +3570,55 @@ arm_vfp_cprc_sub_candidate (struct type *t, } break; - case TYPE_CODE_ARRAY: - { - int count; - unsigned unitlen; - count = arm_vfp_cprc_sub_candidate (TYPE_TARGET_TYPE (t), base_type); - if (count == -1) - return -1; - if (TYPE_LENGTH (t) == 0) - { - gdb_assert (count == 0); - return 0; - } - else if (count == 0) - return -1; - unitlen = arm_vfp_cprc_unit_length (*base_type); - gdb_assert ((TYPE_LENGTH (t) % unitlen) == 0); - return TYPE_LENGTH (t) / unitlen; + case TYPE_CODE_COMPLEX: + /* Arguments of complex T where T is one of the types float or + double get treated as if they are implemented as: + + struct complexT + { + T real; + T imag; + }; + + */ + switch (TYPE_LENGTH (t)) + { + case 8: + if (*base_type == VFP_CPRC_UNKNOWN) + *base_type = VFP_CPRC_SINGLE; + else if (*base_type != VFP_CPRC_SINGLE) + return -1; + return 2; + + case 16: + if (*base_type == VFP_CPRC_UNKNOWN) + *base_type = VFP_CPRC_DOUBLE; + else if (*base_type != VFP_CPRC_DOUBLE) + return -1; + return 2; + + default: + return -1; + } + break; + + case TYPE_CODE_ARRAY: + { + int count; + unsigned unitlen; + count = arm_vfp_cprc_sub_candidate (TYPE_TARGET_TYPE (t), base_type); + if (count == -1) + return -1; + if (TYPE_LENGTH (t) == 0) + { + gdb_assert (count == 0); + return 0; + } + else if (count == 0) + return -1; + unitlen = arm_vfp_cprc_unit_length (*base_type); + gdb_assert ((TYPE_LENGTH (t) % unitlen) == 0); + return TYPE_LENGTH (t) / unitlen; } break; @@ -3575,7 +3753,7 @@ arm_push_dummy_call (struct gdbarch *gdbarch, struct value *function, /* Walk through the list of args and determine how large a temporary stack is required. Need to take care here as structs may be - passed on the stack, and we have to to push them. */ + passed on the stack, and we have to push them. */ nstack = 0; argreg = ARM_A1_REGNUM; @@ -3669,7 +3847,8 @@ arm_push_dummy_call (struct gdbarch *gdbarch, struct value *function, val + i * unit_length); else { - sprintf (name_buf, "%c%d", reg_char, reg_scaled + i); + xsnprintf (name_buf, sizeof (name_buf), "%c%d", + reg_char, reg_scaled + i); regnum = user_reg_map_name_to_regnum (gdbarch, name_buf, strlen (name_buf)); regcache_cooked_write (regcache, regnum, @@ -3785,19 +3964,19 @@ arm_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp) } static void -print_fpu_flags (int flags) +print_fpu_flags (struct ui_file *file, int flags) { if (flags & (1 << 0)) - fputs ("IVO ", stdout); + fputs_filtered ("IVO ", file); if (flags & (1 << 1)) - fputs ("DVZ ", stdout); + fputs_filtered ("DVZ ", file); if (flags & (1 << 2)) - fputs ("OFL ", stdout); + fputs_filtered ("OFL ", file); if (flags & (1 << 3)) - fputs ("UFL ", stdout); + fputs_filtered ("UFL ", file); if (flags & (1 << 4)) - fputs ("INX ", stdout); - putchar ('\n'); + fputs_filtered ("INX ", file); + fputc_filtered ('\n', file); } /* Print interesting information about the floating point processor @@ -3811,15 +3990,15 @@ arm_print_float_info (struct gdbarch *gdbarch, struct ui_file *file, type = (status >> 24) & 127; if (status & (1 << 31)) - printf (_("Hardware FPU type %d\n"), type); + fprintf_filtered (file, _("Hardware FPU type %d\n"), type); else - printf (_("Software FPU type %d\n"), type); + fprintf_filtered (file, _("Software FPU type %d\n"), type); /* i18n: [floating point unit] mask */ - fputs (_("mask: "), stdout); - print_fpu_flags (status >> 16); + fputs_filtered (_("mask: "), file); + print_fpu_flags (file, status >> 16); /* i18n: [floating point unit] flags */ - fputs (_("flags: "), stdout); - print_fpu_flags (status); + fputs_filtered (_("flags: "), file); + print_fpu_flags (file, status); } /* Construct the ARM extended floating point type. */ @@ -4000,7 +4179,7 @@ arm_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg) { char name_buf[4]; - sprintf (name_buf, "s%d", reg - 64); + xsnprintf (name_buf, sizeof (name_buf), "s%d", reg - 64); return user_reg_map_name_to_regnum (gdbarch, name_buf, strlen (name_buf)); } @@ -4011,7 +4190,7 @@ arm_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg) { char name_buf[4]; - sprintf (name_buf, "d%d", reg - 256); + xsnprintf (name_buf, sizeof (name_buf), "d%d", reg - 256); return user_reg_map_name_to_regnum (gdbarch, name_buf, strlen (name_buf)); } @@ -4144,7 +4323,7 @@ shifted_reg_val (struct frame_info *frame, unsigned long inst, int carry, else shift = bits (inst, 7, 11); - res = (rm == 15 + res = (rm == ARM_PC_REGNUM ? (pc_val + (bit (inst, 4) ? 12 : 8)) : get_frame_register_unsigned (frame, rm)); @@ -4221,7 +4400,7 @@ thumb_advance_itstate (unsigned int itstate) another breakpoint by our caller. */ static CORE_ADDR -thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) +thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc) { struct gdbarch *gdbarch = get_frame_arch (frame); struct address_space *aspace = get_frame_address_space (frame); @@ -4319,8 +4498,8 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) /* Set a breakpoint on the following instruction. */ gdb_assert ((itstate & 0x0f) != 0); - if (insert_bkpt) - insert_single_step_breakpoint (gdbarch, aspace, pc); + arm_insert_single_step_breakpoint (gdbarch, aspace, + MAKE_THUMB_ADDR (pc)); cond_negated = (itstate >> 4) & 1; /* Skip all following instructions with the same @@ -4347,14 +4526,9 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) int cond = itstate >> 4; if (! condition_true (cond, status)) - { - /* Advance to the next instruction. All the 32-bit - instructions share a common prefix. */ - if ((inst1 & 0xe000) == 0xe000 && (inst1 & 0x1800) != 0) - return MAKE_THUMB_ADDR (pc + 4); - else - return MAKE_THUMB_ADDR (pc + 2); - } + /* Advance to the next instruction. All the 32-bit + instructions share a common prefix. */ + return MAKE_THUMB_ADDR (pc + thumb_insn_size (inst1)); /* Otherwise, handle the instruction normally. */ } @@ -4388,7 +4562,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) { nextpc = pc_val + (sbits (inst1, 0, 10) << 1); } - else if ((inst1 & 0xe000) == 0xe000) /* 32-bit instruction */ + else if (thumb_insn_size (inst1) == 4) /* 32-bit instruction */ { unsigned short inst2; inst2 = read_memory_unsigned_integer (pc + 2, 2, byte_order_for_code); @@ -4498,7 +4672,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) rn = bits (inst1, 0, 3); base = get_frame_register_unsigned (frame, rn); - if (rn == 15) + if (rn == ARM_PC_REGNUM) { base = (base + 4) & ~(CORE_ADDR) 0x3; if (bit (inst1, 7)) @@ -4564,7 +4738,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) else if ((inst1 & 0xff00) == 0x4700) /* bx REG, blx REG */ { if (bits (inst1, 3, 6) == 0x0f) - nextpc = pc_val; + nextpc = UNMAKE_THUMB_ADDR (pc_val); else nextpc = get_frame_register_unsigned (frame, bits (inst1, 3, 6)); } @@ -4592,8 +4766,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) } /* Get the raw next address. PC is the current program counter, in - FRAME. INSERT_BKPT should be TRUE if we want a breakpoint set on - the alternative next instruction if there are two options. + FRAME, which is assumed to be executing in ARM mode. The value returned has the execution state of the next instruction encoded in it. Use IS_THUMB_ADDR () to see whether the instruction is @@ -4601,7 +4774,7 @@ thumb_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) address. */ static CORE_ADDR -arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) +arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc) { struct gdbarch *gdbarch = get_frame_arch (frame); enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); @@ -4611,9 +4784,6 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) unsigned long status; CORE_ADDR nextpc; - if (arm_frame_is_thumb (frame)) - return thumb_get_next_pc_raw (frame, pc, insert_bkpt); - pc_val = (unsigned long) pc; this_instr = read_memory_unsigned_integer (pc, 4, byte_order_for_code); @@ -4665,16 +4835,19 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) || bits (this_instr, 4, 27) == 0x12fff3) { rn = bits (this_instr, 0, 3); - nextpc = (rn == 15) ? pc_val + 8 - : get_frame_register_unsigned (frame, rn); + nextpc = ((rn == ARM_PC_REGNUM) + ? (pc_val + 8) + : get_frame_register_unsigned (frame, rn)); + return nextpc; } /* Multiply into PC. */ c = (status & FLAG_C) ? 1 : 0; rn = bits (this_instr, 16, 19); - operand1 = (rn == 15) ? pc_val + 8 - : get_frame_register_unsigned (frame, rn); + operand1 = ((rn == ARM_PC_REGNUM) + ? (pc_val + 8) + : get_frame_register_unsigned (frame, rn)); if (bit (this_instr, 25)) { @@ -4774,8 +4947,10 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) /* byte write to PC */ rn = bits (this_instr, 16, 19); - base = (rn == 15) ? pc_val + 8 - : get_frame_register_unsigned (frame, rn); + base = ((rn == ARM_PC_REGNUM) + ? (pc_val + 8) + : get_frame_register_unsigned (frame, rn)); + if (bit (this_instr, 24)) { /* pre-indexed */ @@ -4790,8 +4965,9 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) else base -= offset; } - nextpc = (CORE_ADDR) read_memory_integer ((CORE_ADDR) base, - 4, byte_order); + nextpc = + (CORE_ADDR) read_memory_unsigned_integer ((CORE_ADDR) base, + 4, byte_order); } } break; @@ -4805,6 +4981,9 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) { /* loading pc */ int offset = 0; + unsigned long rn_val + = get_frame_register_unsigned (frame, + bits (this_instr, 16, 19)); if (bit (this_instr, 23)) { @@ -4817,15 +4996,10 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) else if (bit (this_instr, 24)) offset = -4; - { - unsigned long rn_val = - get_frame_register_unsigned (frame, - bits (this_instr, 16, 19)); - nextpc = - (CORE_ADDR) read_memory_integer ((CORE_ADDR) (rn_val - + offset), - 4, byte_order); - } + nextpc = + (CORE_ADDR) read_memory_unsigned_integer ((CORE_ADDR) + (rn_val + offset), + 4, byte_order); } } break; @@ -4861,18 +5035,263 @@ arm_get_next_pc_raw (struct frame_info *frame, CORE_ADDR pc, int insert_bkpt) return nextpc; } +/* Determine next PC after current instruction executes. Will call either + arm_get_next_pc_raw or thumb_get_next_pc_raw. Error out if infinite + loop is detected. */ + CORE_ADDR arm_get_next_pc (struct frame_info *frame, CORE_ADDR pc) { - struct gdbarch *gdbarch = get_frame_arch (frame); - CORE_ADDR nextpc = - gdbarch_addr_bits_remove (gdbarch, - arm_get_next_pc_raw (frame, pc, TRUE)); - if (nextpc == pc) - error (_("Infinite loop detected")); + CORE_ADDR nextpc; + + if (arm_frame_is_thumb (frame)) + nextpc = thumb_get_next_pc_raw (frame, pc); + else + nextpc = arm_get_next_pc_raw (frame, pc); + return nextpc; } +/* Like insert_single_step_breakpoint, but make sure we use a breakpoint + of the appropriate mode (as encoded in the PC value), even if this + differs from what would be expected according to the symbol tables. */ + +void +arm_insert_single_step_breakpoint (struct gdbarch *gdbarch, + struct address_space *aspace, + CORE_ADDR pc) +{ + struct cleanup *old_chain + = make_cleanup_restore_integer (&arm_override_mode); + + arm_override_mode = IS_THUMB_ADDR (pc); + pc = gdbarch_addr_bits_remove (gdbarch, pc); + + insert_single_step_breakpoint (gdbarch, aspace, pc); + + do_cleanups (old_chain); +} + +/* Checks for an atomic sequence of instructions beginning with a LDREX{,B,H,D} + instruction and ending with a STREX{,B,H,D} instruction. If such a sequence + is found, attempt to step through it. A breakpoint is placed at the end of + the sequence. */ + +static int +thumb_deal_with_atomic_sequence_raw (struct frame_info *frame) +{ + struct gdbarch *gdbarch = get_frame_arch (frame); + struct address_space *aspace = get_frame_address_space (frame); + enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); + CORE_ADDR pc = get_frame_pc (frame); + CORE_ADDR breaks[2] = {-1, -1}; + CORE_ADDR loc = pc; + unsigned short insn1, insn2; + int insn_count; + int index; + int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */ + const int atomic_sequence_length = 16; /* Instruction sequence length. */ + ULONGEST status, itstate; + + /* We currently do not support atomic sequences within an IT block. */ + status = get_frame_register_unsigned (frame, ARM_PS_REGNUM); + itstate = ((status >> 8) & 0xfc) | ((status >> 25) & 0x3); + if (itstate & 0x0f) + return 0; + + /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction. */ + insn1 = read_memory_unsigned_integer (loc, 2, byte_order_for_code); + loc += 2; + if (thumb_insn_size (insn1) != 4) + return 0; + + insn2 = read_memory_unsigned_integer (loc, 2, byte_order_for_code); + loc += 2; + if (!((insn1 & 0xfff0) == 0xe850 + || ((insn1 & 0xfff0) == 0xe8d0 && (insn2 & 0x00c0) == 0x0040))) + return 0; + + /* Assume that no atomic sequence is longer than "atomic_sequence_length" + instructions. */ + for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count) + { + insn1 = read_memory_unsigned_integer (loc, 2, byte_order_for_code); + loc += 2; + + if (thumb_insn_size (insn1) != 4) + { + /* Assume that there is at most one conditional branch in the + atomic sequence. If a conditional branch is found, put a + breakpoint in its destination address. */ + if ((insn1 & 0xf000) == 0xd000 && bits (insn1, 8, 11) != 0x0f) + { + if (last_breakpoint > 0) + return 0; /* More than one conditional branch found, + fallback to the standard code. */ + + breaks[1] = loc + 2 + (sbits (insn1, 0, 7) << 1); + last_breakpoint++; + } + + /* We do not support atomic sequences that use any *other* + instructions but conditional branches to change the PC. + Fall back to standard code to avoid losing control of + execution. */ + else if (thumb_instruction_changes_pc (insn1)) + return 0; + } + else + { + insn2 = read_memory_unsigned_integer (loc, 2, byte_order_for_code); + loc += 2; + + /* Assume that there is at most one conditional branch in the + atomic sequence. If a conditional branch is found, put a + breakpoint in its destination address. */ + if ((insn1 & 0xf800) == 0xf000 + && (insn2 & 0xd000) == 0x8000 + && (insn1 & 0x0380) != 0x0380) + { + int sign, j1, j2, imm1, imm2; + unsigned int offset; + + sign = sbits (insn1, 10, 10); + imm1 = bits (insn1, 0, 5); + imm2 = bits (insn2, 0, 10); + j1 = bit (insn2, 13); + j2 = bit (insn2, 11); + + offset = (sign << 20) + (j2 << 19) + (j1 << 18); + offset += (imm1 << 12) + (imm2 << 1); + + if (last_breakpoint > 0) + return 0; /* More than one conditional branch found, + fallback to the standard code. */ + + breaks[1] = loc + offset; + last_breakpoint++; + } + + /* We do not support atomic sequences that use any *other* + instructions but conditional branches to change the PC. + Fall back to standard code to avoid losing control of + execution. */ + else if (thumb2_instruction_changes_pc (insn1, insn2)) + return 0; + + /* If we find a strex{,b,h,d}, we're done. */ + if ((insn1 & 0xfff0) == 0xe840 + || ((insn1 & 0xfff0) == 0xe8c0 && (insn2 & 0x00c0) == 0x0040)) + break; + } + } + + /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence. */ + if (insn_count == atomic_sequence_length) + return 0; + + /* Insert a breakpoint right after the end of the atomic sequence. */ + breaks[0] = loc; + + /* Check for duplicated breakpoints. Check also for a breakpoint + placed (branch instruction's destination) anywhere in sequence. */ + if (last_breakpoint + && (breaks[1] == breaks[0] + || (breaks[1] >= pc && breaks[1] < loc))) + last_breakpoint = 0; + + /* Effectively inserts the breakpoints. */ + for (index = 0; index <= last_breakpoint; index++) + arm_insert_single_step_breakpoint (gdbarch, aspace, + MAKE_THUMB_ADDR (breaks[index])); + + return 1; +} + +static int +arm_deal_with_atomic_sequence_raw (struct frame_info *frame) +{ + struct gdbarch *gdbarch = get_frame_arch (frame); + struct address_space *aspace = get_frame_address_space (frame); + enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); + CORE_ADDR pc = get_frame_pc (frame); + CORE_ADDR breaks[2] = {-1, -1}; + CORE_ADDR loc = pc; + unsigned int insn; + int insn_count; + int index; + int last_breakpoint = 0; /* Defaults to 0 (no breakpoints placed). */ + const int atomic_sequence_length = 16; /* Instruction sequence length. */ + + /* Assume all atomic sequences start with a ldrex{,b,h,d} instruction. + Note that we do not currently support conditionally executed atomic + instructions. */ + insn = read_memory_unsigned_integer (loc, 4, byte_order_for_code); + loc += 4; + if ((insn & 0xff9000f0) != 0xe1900090) + return 0; + + /* Assume that no atomic sequence is longer than "atomic_sequence_length" + instructions. */ + for (insn_count = 0; insn_count < atomic_sequence_length; ++insn_count) + { + insn = read_memory_unsigned_integer (loc, 4, byte_order_for_code); + loc += 4; + + /* Assume that there is at most one conditional branch in the atomic + sequence. If a conditional branch is found, put a breakpoint in + its destination address. */ + if (bits (insn, 24, 27) == 0xa) + { + if (last_breakpoint > 0) + return 0; /* More than one conditional branch found, fallback + to the standard single-step code. */ + + breaks[1] = BranchDest (loc - 4, insn); + last_breakpoint++; + } + + /* We do not support atomic sequences that use any *other* instructions + but conditional branches to change the PC. Fall back to standard + code to avoid losing control of execution. */ + else if (arm_instruction_changes_pc (insn)) + return 0; + + /* If we find a strex{,b,h,d}, we're done. */ + if ((insn & 0xff9000f0) == 0xe1800090) + break; + } + + /* If we didn't find the strex{,b,h,d}, we cannot handle the sequence. */ + if (insn_count == atomic_sequence_length) + return 0; + + /* Insert a breakpoint right after the end of the atomic sequence. */ + breaks[0] = loc; + + /* Check for duplicated breakpoints. Check also for a breakpoint + placed (branch instruction's destination) anywhere in sequence. */ + if (last_breakpoint + && (breaks[1] == breaks[0] + || (breaks[1] >= pc && breaks[1] < loc))) + last_breakpoint = 0; + + /* Effectively inserts the breakpoints. */ + for (index = 0; index <= last_breakpoint; index++) + arm_insert_single_step_breakpoint (gdbarch, aspace, breaks[index]); + + return 1; +} + +int +arm_deal_with_atomic_sequence (struct frame_info *frame) +{ + if (arm_frame_is_thumb (frame)) + return thumb_deal_with_atomic_sequence_raw (frame); + else + return arm_deal_with_atomic_sequence_raw (frame); +} + /* single_step() is called just before we want to resume the inferior, if we want to single-step it but there is no hardware or kernel single-step support. We find the target of the coming instruction @@ -4883,13 +5302,13 @@ arm_software_single_step (struct frame_info *frame) { struct gdbarch *gdbarch = get_frame_arch (frame); struct address_space *aspace = get_frame_address_space (frame); + CORE_ADDR next_pc; - /* NOTE: This may insert the wrong breakpoint instruction when - single-stepping over a mode-changing instruction, if the - CPSR heuristics are used. */ + if (arm_deal_with_atomic_sequence (frame)) + return 1; - CORE_ADDR next_pc = arm_get_next_pc (frame, get_frame_pc (frame)); - insert_single_step_breakpoint (gdbarch, aspace, next_pc); + next_pc = arm_get_next_pc (frame, get_frame_pc (frame)); + arm_insert_single_step_breakpoint (gdbarch, aspace, next_pc); return 1; } @@ -4902,7 +5321,7 @@ static gdb_byte * extend_buffer_earlier (gdb_byte *buf, CORE_ADDR endaddr, int old_len, int new_len) { - gdb_byte *new_buf, *middle; + gdb_byte *new_buf; int bytes_to_read = new_len - old_len; new_buf = xmalloc (new_len); @@ -4935,7 +5354,7 @@ arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr) gdb_byte *buf; char map_type; CORE_ADDR boundary, func_start; - int buf_len, buf2_len; + int buf_len; enum bfd_endian order = gdbarch_byte_order_for_code (gdbarch); int i, any, last_it, last_it_count; @@ -5105,19 +5524,20 @@ arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr) /* NOP instruction (mov r0, r0). */ #define ARM_NOP 0xe1a00000 - -static int displaced_in_arm_mode (struct regcache *regs); +#define THUMB_NOP 0x4600 /* Helper for register reads for displaced stepping. In particular, this returns the PC as it would be seen by the instruction at its original location. */ ULONGEST -displaced_read_reg (struct regcache *regs, CORE_ADDR from, int regno) +displaced_read_reg (struct regcache *regs, struct displaced_step_closure *dsc, + int regno) { ULONGEST ret; + CORE_ADDR from = dsc->insn_addr; - if (regno == 15) + if (regno == ARM_PC_REGNUM) { /* Compute pipeline offset: - When executing an ARM instruction, PC reads as the address of the @@ -5125,7 +5545,7 @@ displaced_read_reg (struct regcache *regs, CORE_ADDR from, int regno) - When executing a Thumb instruction, PC reads as the address of the current instruction plus 4. */ - if (displaced_in_arm_mode (regs)) + if (!dsc->is_thumb) from += 8; else from += 4; @@ -5159,9 +5579,10 @@ displaced_in_arm_mode (struct regcache *regs) /* Write to the PC as from a branch instruction. */ static void -branch_write_pc (struct regcache *regs, ULONGEST val) +branch_write_pc (struct regcache *regs, struct displaced_step_closure *dsc, + ULONGEST val) { - if (displaced_in_arm_mode (regs)) + if (!dsc->is_thumb) /* Note: If bits 0/1 are set, this branch would be unpredictable for architecture versions < 6. */ regcache_cooked_write_unsigned (regs, ARM_PC_REGNUM, @@ -5204,23 +5625,25 @@ bx_write_pc (struct regcache *regs, ULONGEST val) /* Write to the PC as if from a load instruction. */ static void -load_write_pc (struct regcache *regs, ULONGEST val) +load_write_pc (struct regcache *regs, struct displaced_step_closure *dsc, + ULONGEST val) { if (DISPLACED_STEPPING_ARCH_VERSION >= 5) bx_write_pc (regs, val); else - branch_write_pc (regs, val); + branch_write_pc (regs, dsc, val); } /* Write to the PC as if from an ALU instruction. */ static void -alu_write_pc (struct regcache *regs, ULONGEST val) +alu_write_pc (struct regcache *regs, struct displaced_step_closure *dsc, + ULONGEST val) { - if (DISPLACED_STEPPING_ARCH_VERSION >= 7 && displaced_in_arm_mode (regs)) + if (DISPLACED_STEPPING_ARCH_VERSION >= 7 && !dsc->is_thumb) bx_write_pc (regs, val); else - branch_write_pc (regs, val); + branch_write_pc (regs, dsc, val); } /* Helper for writing to registers for displaced stepping. Writing to the PC @@ -5231,7 +5654,7 @@ void displaced_write_reg (struct regcache *regs, struct displaced_step_closure *dsc, int regno, ULONGEST val, enum pc_write_style write_pc) { - if (regno == 15) + if (regno == ARM_PC_REGNUM) { if (debug_displaced) fprintf_unfiltered (gdb_stdlog, "displaced: writing pc %.8lx\n", @@ -5239,7 +5662,7 @@ displaced_write_reg (struct regcache *regs, struct displaced_step_closure *dsc, switch (write_pc) { case BRANCH_WRITE_PC: - branch_write_pc (regs, val); + branch_write_pc (regs, dsc, val); break; case BX_WRITE_PC: @@ -5247,11 +5670,11 @@ displaced_write_reg (struct regcache *regs, struct displaced_step_closure *dsc, break; case LOAD_WRITE_PC: - load_write_pc (regs, val); + load_write_pc (regs, dsc, val); break; case ALU_WRITE_PC: - alu_write_pc (regs, val); + alu_write_pc (regs, dsc, val); break; case CANNOT_WRITE_PC: @@ -5311,8 +5734,8 @@ insn_references_pc (uint32_t insn, uint32_t bitmask) matter what address they are executed at: in those cases, use this. */ static int -copy_unmodified (struct gdbarch *gdbarch, uint32_t insn, - const char *iname, struct displaced_step_closure *dsc) +arm_copy_unmodified (struct gdbarch *gdbarch, uint32_t insn, + const char *iname, struct displaced_step_closure *dsc) { if (debug_displaced) fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.8lx, " @@ -5324,6 +5747,40 @@ copy_unmodified (struct gdbarch *gdbarch, uint32_t insn, return 0; } +static int +thumb_copy_unmodified_32bit (struct gdbarch *gdbarch, uint16_t insn1, + uint16_t insn2, const char *iname, + struct displaced_step_closure *dsc) +{ + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.4x %.4x, " + "opcode/class '%s' unmodified\n", insn1, insn2, + iname); + + dsc->modinsn[0] = insn1; + dsc->modinsn[1] = insn2; + dsc->numinsns = 2; + + return 0; +} + +/* Copy 16-bit Thumb(Thumb and 16-bit Thumb-2) instruction without any + modification. */ +static int +thumb_copy_unmodified_16bit (struct gdbarch *gdbarch, unsigned int insn, + const char *iname, + struct displaced_step_closure *dsc) +{ + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: copying insn %.4x, " + "opcode/class '%s' unmodified\n", insn, + iname); + + dsc->modinsn[0] = insn; + + return 0; +} + /* Preload instructions with immediate offset. */ static void @@ -5335,78 +5792,138 @@ cleanup_preload (struct gdbarch *gdbarch, displaced_write_reg (regs, dsc, 1, dsc->tmp[1], CANNOT_WRITE_PC); } -static int -copy_preload (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs, - struct displaced_step_closure *dsc) +static void +install_preload (struct gdbarch *gdbarch, struct regcache *regs, + struct displaced_step_closure *dsc, unsigned int rn) { - unsigned int rn = bits (insn, 16, 19); ULONGEST rn_val; - CORE_ADDR from = dsc->insn_addr; - - if (!insn_references_pc (insn, 0x000f0000ul)) - return copy_unmodified (gdbarch, insn, "preload", dsc); - - if (debug_displaced) - fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n", - (unsigned long) insn); - /* Preload instructions: {pli/pld} [rn, #+/-imm] -> {pli/pld} [r0, #+/-imm]. */ - dsc->tmp[0] = displaced_read_reg (regs, from, 0); - rn_val = displaced_read_reg (regs, from, rn); + dsc->tmp[0] = displaced_read_reg (regs, dsc, 0); + rn_val = displaced_read_reg (regs, dsc, rn); displaced_write_reg (regs, dsc, 0, rn_val, CANNOT_WRITE_PC); - dsc->u.preload.immed = 1; - dsc->modinsn[0] = insn & 0xfff0ffff; - dsc->cleanup = &cleanup_preload; - - return 0; } -/* Preload instructions with register offset. */ - static int -copy_preload_reg (struct gdbarch *gdbarch, uint32_t insn, - struct regcache *regs, +arm_copy_preload (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs, struct displaced_step_closure *dsc) { unsigned int rn = bits (insn, 16, 19); - unsigned int rm = bits (insn, 0, 3); - ULONGEST rn_val, rm_val; - CORE_ADDR from = dsc->insn_addr; - if (!insn_references_pc (insn, 0x000f000ful)) - return copy_unmodified (gdbarch, insn, "preload reg", dsc); + if (!insn_references_pc (insn, 0x000f0000ul)) + return arm_copy_unmodified (gdbarch, insn, "preload", dsc); if (debug_displaced) fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n", (unsigned long) insn); + dsc->modinsn[0] = insn & 0xfff0ffff; + + install_preload (gdbarch, regs, dsc, rn); + + return 0; +} + +static int +thumb2_copy_preload (struct gdbarch *gdbarch, uint16_t insn1, uint16_t insn2, + struct regcache *regs, struct displaced_step_closure *dsc) +{ + unsigned int rn = bits (insn1, 0, 3); + unsigned int u_bit = bit (insn1, 7); + int imm12 = bits (insn2, 0, 11); + ULONGEST pc_val; + + if (rn != ARM_PC_REGNUM) + return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "preload", dsc); + + /* PC is only allowed to use in PLI (immediate,literal) Encoding T3, and + PLD (literal) Encoding T1. */ + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, + "displaced: copying pld/pli pc (0x%x) %c imm12 %.4x\n", + (unsigned int) dsc->insn_addr, u_bit ? '+' : '-', + imm12); + + if (!u_bit) + imm12 = -1 * imm12; + + /* Rewrite instruction {pli/pld} PC imm12 into: + Prepare: tmp[0] <- r0, tmp[1] <- r1, r0 <- pc, r1 <- imm12 + + {pli/pld} [r0, r1] + + Cleanup: r0 <- tmp[0], r1 <- tmp[1]. */ + + dsc->tmp[0] = displaced_read_reg (regs, dsc, 0); + dsc->tmp[1] = displaced_read_reg (regs, dsc, 1); + + pc_val = displaced_read_reg (regs, dsc, ARM_PC_REGNUM); + + displaced_write_reg (regs, dsc, 0, pc_val, CANNOT_WRITE_PC); + displaced_write_reg (regs, dsc, 1, imm12, CANNOT_WRITE_PC); + dsc->u.preload.immed = 0; + + /* {pli/pld} [r0, r1] */ + dsc->modinsn[0] = insn1 & 0xfff0; + dsc->modinsn[1] = 0xf001; + dsc->numinsns = 2; + + dsc->cleanup = &cleanup_preload; + return 0; +} + +/* Preload instructions with register offset. */ + +static void +install_preload_reg(struct gdbarch *gdbarch, struct regcache *regs, + struct displaced_step_closure *dsc, unsigned int rn, + unsigned int rm) +{ + ULONGEST rn_val, rm_val; + /* Preload register-offset instructions: {pli/pld} [rn, rm {, shift}] -> {pli/pld} [r0, r1 {, shift}]. */ - dsc->tmp[0] = displaced_read_reg (regs, from, 0); - dsc->tmp[1] = displaced_read_reg (regs, from, 1); - rn_val = displaced_read_reg (regs, from, rn); - rm_val = displaced_read_reg (regs, from, rm); + dsc->tmp[0] = displaced_read_reg (regs, dsc, 0); + dsc->tmp[1] = displaced_read_reg (regs, dsc, 1); + rn_val = displaced_read_reg (regs, dsc, rn); + rm_val = displaced_read_reg (regs, dsc, rm); displaced_write_reg (regs, dsc, 0, rn_val, CANNOT_WRITE_PC); displaced_write_reg (regs, dsc, 1, rm_val, CANNOT_WRITE_PC); - dsc->u.preload.immed = 0; - dsc->modinsn[0] = (insn & 0xfff0fff0) | 0x1; - dsc->cleanup = &cleanup_preload; +} + +static int +arm_copy_preload_reg (struct gdbarch *gdbarch, uint32_t insn, + struct regcache *regs, + struct displaced_step_closure *dsc) +{ + unsigned int rn = bits (insn, 16, 19); + unsigned int rm = bits (insn, 0, 3); + + if (!insn_references_pc (insn, 0x000f000ful)) + return arm_copy_unmodified (gdbarch, insn, "preload reg", dsc); + + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: copying preload insn %.8lx\n", + (unsigned long) insn); + + dsc->modinsn[0] = (insn & 0xfff0fff0) | 0x1; + + install_preload_reg (gdbarch, regs, dsc, rn, rm); return 0; } @@ -5417,7 +5934,7 @@ cleanup_copro_load_store (struct gdbarch *gdbarch, struct regcache *regs, struct displaced_step_closure *dsc) { - ULONGEST rn_val = displaced_read_reg (regs, dsc->insn_addr, 0); + ULONGEST rn_val = displaced_read_reg (regs, dsc, 0); displaced_write_reg (regs, dsc, 0, dsc->tmp[0], CANNOT_WRITE_PC); @@ -5425,21 +5942,12 @@ cleanup_copro_load_store (struct gdbarch *gdbarch, displaced_write_reg (regs, dsc, dsc->u.ldst.rn, rn_val, LOAD_WRITE_PC); } -static int -copy_copro_load_store (struct gdbarch *gdbarch, uint32_t insn, - struct regcache *regs, - struct displaced_step_closure *dsc) +static void +install_copro_load_store (struct gdbarch *gdbarch, struct regcache *regs, + struct displaced_step_closure *dsc, + int writeback, unsigned int rn) { - unsigned int rn = bits (insn, 16, 19); ULONGEST rn_val; - CORE_ADDR from = dsc->insn_addr; - - if (!insn_references_pc (insn, 0x000f0000ul)) - return copy_unmodified (gdbarch, insn, "copro load/store", dsc); - - if (debug_displaced) - fprintf_unfiltered (gdb_stdlog, "displaced: copying coprocessor " - "load/store insn %.8lx\n", (unsigned long) insn); /* Coprocessor load/store instructions: @@ -5449,16 +5957,61 @@ copy_copro_load_store (struct gdbarch *gdbarch, uint32_t insn, ldc/ldc2 are handled identically. */ - dsc->tmp[0] = displaced_read_reg (regs, from, 0); - rn_val = displaced_read_reg (regs, from, rn); + dsc->tmp[0] = displaced_read_reg (regs, dsc, 0); + rn_val = displaced_read_reg (regs, dsc, rn); + /* PC should be 4-byte aligned. */ + rn_val = rn_val & 0xfffffffc; displaced_write_reg (regs, dsc, 0, rn_val, CANNOT_WRITE_PC); - dsc->u.ldst.writeback = bit (insn, 25); + dsc->u.ldst.writeback = writeback; dsc->u.ldst.rn = rn; + dsc->cleanup = &cleanup_copro_load_store; +} + +static int +arm_copy_copro_load_store (struct gdbarch *gdbarch, uint32_t insn, + struct regcache *regs, + struct displaced_step_closure *dsc) +{ + unsigned int rn = bits (insn, 16, 19); + + if (!insn_references_pc (insn, 0x000f0000ul)) + return arm_copy_unmodified (gdbarch, insn, "copro load/store", dsc); + + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: copying coprocessor " + "load/store insn %.8lx\n", (unsigned long) insn); + dsc->modinsn[0] = insn & 0xfff0ffff; - dsc->cleanup = &cleanup_copro_load_store; + install_copro_load_store (gdbarch, regs, dsc, bit (insn, 25), rn); + + return 0; +} + +static int +thumb2_copy_copro_load_store (struct gdbarch *gdbarch, uint16_t insn1, + uint16_t insn2, struct regcache *regs, + struct displaced_step_closure *dsc) +{ + unsigned int rn = bits (insn1, 0, 3); + + if (rn != ARM_PC_REGNUM) + return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, + "copro load/store", dsc); + + if (debug_displaced) + fprintf_unfiltered (gdb_stdlog, "displaced: copying coprocessor " + "load/store insn %.4x%.4x\n", insn1, insn2); + + dsc->modinsn[0] = insn1 & 0xfff0; + dsc->modinsn[1] = insn2; + dsc->numinsns = 2; + + /* This function is called for copying instruction LDC/LDC2/VLDR, which + doesn't support writeback, so pass 0. */ + install_copro_load_store (gdbarch, regs, dsc, 0, rn); return 0; } @@ -5470,8 +6023,7 @@ static void cleanup_branch (struct gdbarch *gdbarch, struct regcache *regs, struct displaced_step_closure *dsc) { - ULONGEST from = dsc->insn_addr; - uint32_t status = displaced_read_reg (regs, from, ARM_PS_REGNUM); + uint32_t status = displaced_read_reg (regs, dsc, ARM_PS_REGNUM); int branch_taken = condition_true (dsc->u.branch.cond, status); enum pc_write_style write_pc = dsc->u.branch.exchange ? BX_WRITE_PC : BRANCH_WRITE_PC; @@ -5481,38 +6033,65 @@ cleanup_branch (struct gdbarch *gdbarch, struct regcache *regs, if (dsc->u.branch.link) { - ULONGEST pc = displaced_read_reg (regs, from, 15); - displaced_write_reg (regs, dsc, 14, pc - 4, CANNOT_WRITE_PC); + /* The value of LR should be the next insn of current one. In order + not to confuse logic hanlding later insn `bx lr', if current insn mode + is Thumb, the bit 0 of LR value should be set to 1. */ + ULONGEST next_insn_addr = dsc->insn_addr + dsc->insn_size; + + if (dsc->is_thumb) + next_insn_addr |= 0x1; + + displaced_write_reg (regs, dsc, ARM_LR_REGNUM, next_insn_addr, + CANNOT_WRITE_PC); } - displaced_write_reg (regs, dsc, 15, dsc->u.branch.dest, write_pc); + displaced_write_reg (regs, dsc, ARM_PC_REGNUM, dsc->u.branch.dest, write_pc); } /* Copy B/BL/BLX instructions with immediate destinations. */ +static void +install_b_bl_blx (struct gdbarch *gdbarch, struct regcache *regs, + struct displaced_step_closure *dsc, + unsigned int cond, int exchange, int link, long offset) +{ + /* Implement "BL