X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Farm-tdep.c;h=12254ecebc703a4cee87dc05d1029afe7ed8be24;hb=2117c711ae07700adb57ea5b5ca61e4c32d7e3d2;hp=5a5152cc477af063f510e09d88ef80dd0d77d76c;hpb=467d42c48df9176d44263f5190cd85192f9ea8f7;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index 5a5152cc47..12254ecebc 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-2014 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 "gdbcmd.h" #include "gdbcore.h" -#include "gdb_string.h" +#include #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" @@ -55,7 +55,16 @@ #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 +103,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 +118,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 +131,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 +142,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; @@ -220,6 +236,8 @@ 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 +374,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 +381,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 +402,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 +423,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 +436,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 +448,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 +468,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 + && SYMBOL_VALUE_ADDRESS (msym.minsym) == pc + && SYMBOL_LINKAGE_NAME (msym.minsym) != NULL) { - const char *name = SYMBOL_LINKAGE_NAME (msym); + const char *name = SYMBOL_LINKAGE_NAME (msym.minsym); /* The GNU linker's Thumb call stub to foo is named __foo_from_thumb. */ @@ -525,7 +521,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 @@ -844,7 +840,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; @@ -1158,18 +1154,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)) @@ -1293,8 +1283,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; @@ -1309,8 +1299,9 @@ arm_skip_stack_protector(CORE_ADDR pc, struct gdbarch *gdbarch) /* 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", + if (stack_chk_guard.minsym + && strncmp (SYMBOL_LINKAGE_NAME (stack_chk_guard.minsym), + "__stack_chk_guard", strlen ("__stack_chk_guard")) != 0) return pc; @@ -1385,7 +1376,6 @@ arm_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) 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 @@ -1410,7 +1400,8 @@ arm_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) if (post_prologue_pc && (s == NULL || s->producer == NULL - || strncmp (s->producer, "GNU ", sizeof ("GNU ") - 1) == 0)) + || strncmp (s->producer, "GNU ", sizeof ("GNU ") - 1) == 0 + || strncmp (s->producer, "clang ", sizeof ("clang ") - 1) == 0)) return post_prologue_pc; if (post_prologue_pc != 0) @@ -1544,7 +1535,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)) @@ -1889,18 +1879,12 @@ arm_analyze_prologue (struct gdbarch *gdbarch, framereg = ARM_FP_REGNUM; framesize = -regs[ARM_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. */ 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) { @@ -2219,7 +2203,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; @@ -2230,6 +2214,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"); @@ -2919,10 +2904,10 @@ 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]; addr_in_block = get_frame_address_in_block (this_frame); - if (in_plt_section (addr_in_block, NULL) + 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) @@ -2940,6 +2925,127 @@ struct frame_unwind arm_stub_unwind = { 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) { @@ -3100,7 +3206,7 @@ thumb_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) if (insn & 0x0100) /* include PC. */ 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; @@ -3233,6 +3339,9 @@ arm_in_function_epilogue_p (struct gdbarch *gdbarch, CORE_ADDR pc) else if ((insn & 0x0fff0000) == 0x08bd0000) /* POP (LDMIA). */ found_stack_adjust = 1; + else if ((insn & 0x0fff0000) == 0x049d0000) + /* POP of a single register. */ + found_stack_adjust = 1; } if (found_stack_adjust) @@ -3298,7 +3407,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: @@ -3570,7 +3678,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; @@ -3664,7 +3772,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, @@ -3780,19 +3889,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 @@ -3806,15 +3915,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. */ @@ -3995,7 +4104,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)); } @@ -4006,7 +4115,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)); } @@ -4216,7 +4325,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); @@ -4314,8 +4423,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 @@ -4342,14 +4451,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. */ } @@ -4383,7 +4487,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); @@ -4559,7 +4663,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)); } @@ -4587,8 +4691,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 @@ -4596,7 +4699,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); @@ -4606,9 +4709,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); @@ -4790,8 +4890,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 +4906,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 +4921,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 +4960,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 +5227,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 +5246,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 +5279,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,6 +5449,7 @@ arm_adjust_breakpoint_address (struct gdbarch *gdbarch, CORE_ADDR bpaddr) /* NOP instruction (mov r0, r0). */ #define ARM_NOP 0xe1a00000 +#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 @@ -5314,8 +5659,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, " @@ -5327,6 +5672,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 @@ -5338,20 +5717,11 @@ 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; - - 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] @@ -5361,53 +5731,124 @@ copy_preload (struct gdbarch *gdbarch, uint32_t insn, struct regcache *regs, 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; - 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); - /* Preload register-offset instructions: + dsc->modinsn[0] = insn & 0xfff0ffff; - {pli/pld} [rn, rm {, shift}] - -> - {pli/pld} [r0, r1 {, shift}]. */ + install_preload (gdbarch, regs, dsc, rn); - 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); + return 0; +} - dsc->u.preload.immed = 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; - dsc->modinsn[0] = (insn & 0xfff0fff0) | 0x1; + if (rn != ARM_PC_REGNUM) + return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2, "preload", dsc); - dsc->cleanup = &cleanup_preload; + /* 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, 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->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; } @@ -5426,21 +5867,13 @@ 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; - 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: {stc/stc2} [, #+/-imm] (and other immediate addressing modes) @@ -5451,14 +5884,59 @@ copy_copro_load_store (struct gdbarch *gdbarch, uint32_t insn, 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; } @@ -5480,8 +5958,16 @@ cleanup_branch (struct gdbarch *gdbarch, struct regcache *regs, if (dsc->u.branch.link) { - ULONGEST pc = displaced_read_reg (regs, dsc, ARM_PC_REGNUM); - displaced_write_reg (regs, dsc, ARM_LR_REGNUM, 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, ARM_PC_REGNUM, dsc->u.branch.dest, write_pc); @@ -5489,29 +5975,48 @@ cleanup_branch (struct gdbarch *gdbarch, struct regcache *regs, /* 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