| /* Common target dependent code for GDB on ARM systems. |
| |
| Copyright (C) 1988-2024 Free Software Foundation, Inc. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| |
| #include <ctype.h> |
| |
| #include "extract-store-integer.h" |
| #include "frame.h" |
| #include "language.h" |
| #include "inferior.h" |
| #include "infrun.h" |
| #include "cli/cli-cmds.h" |
| #include "gdbcore.h" |
| #include "dis-asm.h" |
| #include "disasm.h" |
| #include "regcache.h" |
| #include "reggroups.h" |
| #include "target-float.h" |
| #include "value.h" |
| #include "arch-utils.h" |
| #include "osabi.h" |
| #include "frame-unwind.h" |
| #include "frame-base.h" |
| #include "trad-frame.h" |
| #include "objfiles.h" |
| #include "dwarf2.h" |
| #include "dwarf2/frame.h" |
| #include "gdbtypes.h" |
| #include "prologue-value.h" |
| #include "remote.h" |
| #include "target-descriptions.h" |
| #include "user-regs.h" |
| #include "observable.h" |
| #include "count-one-bits.h" |
| |
| #include "arch/arm.h" |
| #include "arch/arm-get-next-pcs.h" |
| #include "arm-tdep.h" |
| #include "sim/sim-arm.h" |
| |
| #include "elf-bfd.h" |
| #include "coff/internal.h" |
| #include "elf/arm.h" |
| |
| #include "record.h" |
| #include "record-full.h" |
| #include <algorithm> |
| |
| #include "producer.h" |
| |
| #if GDB_SELF_TEST |
| #include "gdbsupport/selftest.h" |
| #endif |
| |
| static bool arm_debug; |
| |
| /* Print an "arm" debug statement. */ |
| |
| #define arm_debug_printf(fmt, ...) \ |
| debug_prefixed_printf_cond (arm_debug, "arm", fmt, ##__VA_ARGS__) |
| |
| /* Macros for setting and testing a bit in a minimal symbol that marks |
| it as Thumb function. The MSB of the minimal symbol's "info" field |
| is used for this purpose. |
| |
| MSYMBOL_SET_SPECIAL Actually sets the "special" bit. |
| MSYMBOL_IS_SPECIAL Tests the "special" bit in a minimal symbol. */ |
| |
| #define MSYMBOL_SET_SPECIAL(msym) \ |
| (msym)->set_target_flag_1 (true) |
| |
| #define MSYMBOL_IS_SPECIAL(msym) \ |
| (msym)->target_flag_1 () |
| |
| struct arm_mapping_symbol |
| { |
| CORE_ADDR value; |
| char type; |
| |
| bool operator< (const arm_mapping_symbol &other) const |
| { return this->value < other.value; } |
| }; |
| |
| typedef std::vector<arm_mapping_symbol> arm_mapping_symbol_vec; |
| |
| struct arm_per_bfd |
| { |
| explicit arm_per_bfd (size_t num_sections) |
| : section_maps (new arm_mapping_symbol_vec[num_sections]), |
| section_maps_sorted (new bool[num_sections] ()) |
| {} |
| |
| DISABLE_COPY_AND_ASSIGN (arm_per_bfd); |
| |
| /* Information about mapping symbols ($a, $d, $t) in the objfile. |
| |
| The format is an array of vectors of arm_mapping_symbols, there is one |
| vector for each section of the objfile (the array is index by BFD section |
| index). |
| |
| For each section, the vector of arm_mapping_symbol is sorted by |
| symbol value (address). */ |
| std::unique_ptr<arm_mapping_symbol_vec[]> section_maps; |
| |
| /* For each corresponding element of section_maps above, is this vector |
| sorted. */ |
| std::unique_ptr<bool[]> section_maps_sorted; |
| }; |
| |
| /* Per-bfd data used for mapping symbols. */ |
| static const registry<bfd>::key<arm_per_bfd> arm_bfd_data_key; |
| |
| /* The list of available "set arm ..." and "show arm ..." commands. */ |
| static struct cmd_list_element *setarmcmdlist = NULL; |
| 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 *const fp_model_strings[] = |
| { |
| "auto", |
| "softfpa", |
| "fpa", |
| "softvfp", |
| "vfp", |
| NULL |
| }; |
| |
| /* A variable that can be configured by the user. */ |
| 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 *const arm_abi_strings[] = |
| { |
| "auto", |
| "APCS", |
| "AAPCS", |
| NULL |
| }; |
| |
| /* A variable that can be configured by the user. */ |
| 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 *const arm_mode_strings[] = |
| { |
| "auto", |
| "arm", |
| "thumb", |
| NULL |
| }; |
| |
| static const char *arm_fallback_mode_string = "auto"; |
| static const char *arm_force_mode_string = "auto"; |
| |
| /* The standard register names, and all the valid aliases for them. Note |
| that `fp', `sp' and `pc' are not added in this alias list, because they |
| have been added as builtin user registers in |
| std-regs.c:_initialize_frame_reg. */ |
| static const struct |
| { |
| const char *name; |
| int regnum; |
| } arm_register_aliases[] = { |
| /* Basic register numbers. */ |
| { "r0", 0 }, |
| { "r1", 1 }, |
| { "r2", 2 }, |
| { "r3", 3 }, |
| { "r4", 4 }, |
| { "r5", 5 }, |
| { "r6", 6 }, |
| { "r7", 7 }, |
| { "r8", 8 }, |
| { "r9", 9 }, |
| { "r10", 10 }, |
| { "r11", 11 }, |
| { "r12", 12 }, |
| { "r13", 13 }, |
| { "r14", 14 }, |
| { "r15", 15 }, |
| /* Synonyms (argument and variable registers). */ |
| { "a1", 0 }, |
| { "a2", 1 }, |
| { "a3", 2 }, |
| { "a4", 3 }, |
| { "v1", 4 }, |
| { "v2", 5 }, |
| { "v3", 6 }, |
| { "v4", 7 }, |
| { "v5", 8 }, |
| { "v6", 9 }, |
| { "v7", 10 }, |
| { "v8", 11 }, |
| /* Other platform-specific names for r9. */ |
| { "sb", 9 }, |
| { "tr", 9 }, |
| /* Special names. */ |
| { "ip", 12 }, |
| { "lr", 14 }, |
| /* Names used by GCC (not listed in the ARM EABI). */ |
| { "sl", 10 }, |
| /* A special name from the older ATPCS. */ |
| { "wr", 7 }, |
| }; |
| |
| static const char *const arm_register_names[] = |
| {"r0", "r1", "r2", "r3", /* 0 1 2 3 */ |
| "r4", "r5", "r6", "r7", /* 4 5 6 7 */ |
| "r8", "r9", "r10", "r11", /* 8 9 10 11 */ |
| "r12", "sp", "lr", "pc", /* 12 13 14 15 */ |
| "f0", "f1", "f2", "f3", /* 16 17 18 19 */ |
| "f4", "f5", "f6", "f7", /* 20 21 22 23 */ |
| "fps", "cpsr" }; /* 24 25 */ |
| |
| /* Holds the current set of options to be passed to the disassembler. */ |
| static std::string arm_disassembler_options; |
| |
| /* Valid register name styles. */ |
| static const char **valid_disassembly_styles; |
| |
| /* Disassembly style to use. Default to "std" register names. */ |
| static const char *disassembly_style; |
| |
| /* All possible arm target descriptors. */ |
| static struct target_desc *tdesc_arm_list[ARM_FP_TYPE_INVALID][2]; |
| static struct target_desc *tdesc_arm_mprofile_list[ARM_M_TYPE_INVALID]; |
| |
| /* This is used to keep the bfd arch_info in sync with the disassembly |
| style. */ |
| static void set_disassembly_style_sfunc (const char *, int, |
| struct cmd_list_element *); |
| static void show_disassembly_style_sfunc (struct ui_file *, int, |
| struct cmd_list_element *, |
| const char *); |
| |
| static enum register_status arm_neon_quad_read (struct gdbarch *gdbarch, |
| readable_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 CORE_ADDR |
| arm_get_next_pcs_syscall_next_pc (struct arm_get_next_pcs *self); |
| |
| |
| /* get_next_pcs operations. */ |
| static struct arm_get_next_pcs_ops arm_get_next_pcs_ops = { |
| arm_get_next_pcs_read_memory_unsigned_integer, |
| arm_get_next_pcs_syscall_next_pc, |
| arm_get_next_pcs_addr_bits_remove, |
| arm_get_next_pcs_is_thumb, |
| NULL, |
| }; |
| |
| struct arm_prologue_cache |
| { |
| /* The stack pointer at the time this frame was created; i.e. the |
| caller's stack pointer when this function was called. It is used |
| to identify this frame. */ |
| CORE_ADDR sp; |
| |
| /* Additional stack pointers used by M-profile with Security extension. */ |
| /* Use msp_s / psp_s to hold the values of msp / psp when there is |
| no Security extension. */ |
| CORE_ADDR msp_s; |
| CORE_ADDR msp_ns; |
| CORE_ADDR psp_s; |
| CORE_ADDR psp_ns; |
| |
| /* Active stack pointer. */ |
| int active_sp_regnum; |
| int active_msp_regnum; |
| int active_psp_regnum; |
| |
| /* The frame base for this frame is just prev_sp - frame size. |
| FRAMESIZE is the distance from the frame pointer to the |
| initial stack pointer. */ |
| |
| int framesize; |
| |
| /* The register used to hold the frame pointer for this frame. */ |
| int framereg; |
| |
| /* True if the return address is signed, false otherwise. */ |
| std::optional<bool> ra_signed_state; |
| |
| /* Saved register offsets. */ |
| trad_frame_saved_reg *saved_regs; |
| |
| arm_prologue_cache() = default; |
| }; |
| |
| |
| /* Reconstruct T bit in program status register from LR value. */ |
| |
| static inline ULONGEST |
| reconstruct_t_bit(struct gdbarch *gdbarch, CORE_ADDR lr, ULONGEST psr) |
| { |
| ULONGEST t_bit = arm_psr_thumb_bit (gdbarch); |
| if (IS_THUMB_ADDR (lr)) |
| psr |= t_bit; |
| else |
| psr &= ~t_bit; |
| |
| return psr; |
| } |
| |
| /* Initialize CACHE fields for which zero is not adequate (CACHE is |
| expected to have been ZALLOC'ed before calling this function). */ |
| |
| static void |
| arm_cache_init (struct arm_prologue_cache *cache, struct gdbarch *gdbarch) |
| { |
| cache->active_sp_regnum = ARM_SP_REGNUM; |
| |
| cache->saved_regs = trad_frame_alloc_saved_regs (gdbarch); |
| } |
| |
| /* Similar to the previous function, but extracts GDBARCH from FRAME. */ |
| |
| static void |
| arm_cache_init (struct arm_prologue_cache *cache, const frame_info_ptr &frame) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); |
| |
| arm_cache_init (cache, gdbarch); |
| cache->sp = get_frame_register_unsigned (frame, ARM_SP_REGNUM); |
| |
| if (tdep->have_sec_ext) |
| { |
| const CORE_ADDR msp_val |
| = get_frame_register_unsigned (frame, tdep->m_profile_msp_regnum); |
| const CORE_ADDR psp_val |
| = get_frame_register_unsigned (frame, tdep->m_profile_psp_regnum); |
| |
| cache->msp_s |
| = get_frame_register_unsigned (frame, tdep->m_profile_msp_s_regnum); |
| cache->msp_ns |
| = get_frame_register_unsigned (frame, tdep->m_profile_msp_ns_regnum); |
| cache->psp_s |
| = get_frame_register_unsigned (frame, tdep->m_profile_psp_s_regnum); |
| cache->psp_ns |
| = get_frame_register_unsigned (frame, tdep->m_profile_psp_ns_regnum); |
| |
| /* Identify what msp is alias for (msp_s or msp_ns). */ |
| if (msp_val == cache->msp_s) |
| cache->active_msp_regnum = tdep->m_profile_msp_s_regnum; |
| else if (msp_val == cache->msp_ns) |
| cache->active_msp_regnum = tdep->m_profile_msp_ns_regnum; |
| else |
| { |
| warning (_("Invalid state, unable to determine msp alias, assuming " |
| "msp_s.")); |
| cache->active_msp_regnum = tdep->m_profile_msp_s_regnum; |
| } |
| |
| /* Identify what psp is alias for (psp_s or psp_ns). */ |
| if (psp_val == cache->psp_s) |
| cache->active_psp_regnum = tdep->m_profile_psp_s_regnum; |
| else if (psp_val == cache->psp_ns) |
| cache->active_psp_regnum = tdep->m_profile_psp_ns_regnum; |
| else |
| { |
| warning (_("Invalid state, unable to determine psp alias, assuming " |
| "psp_s.")); |
| cache->active_psp_regnum = tdep->m_profile_psp_s_regnum; |
| } |
| |
| /* Identify what sp is alias for (msp_s, msp_ns, psp_s or psp_ns). */ |
| if (msp_val == cache->sp) |
| cache->active_sp_regnum = cache->active_msp_regnum; |
| else if (psp_val == cache->sp) |
| cache->active_sp_regnum = cache->active_psp_regnum; |
| else |
| { |
| warning (_("Invalid state, unable to determine sp alias, assuming " |
| "msp.")); |
| cache->active_sp_regnum = cache->active_msp_regnum; |
| } |
| } |
| else if (tdep->is_m) |
| { |
| cache->msp_s |
| = get_frame_register_unsigned (frame, tdep->m_profile_msp_regnum); |
| cache->psp_s |
| = get_frame_register_unsigned (frame, tdep->m_profile_psp_regnum); |
| |
| /* Identify what sp is alias for (msp or psp). */ |
| if (cache->msp_s == cache->sp) |
| cache->active_sp_regnum = tdep->m_profile_msp_regnum; |
| else if (cache->psp_s == cache->sp) |
| cache->active_sp_regnum = tdep->m_profile_psp_regnum; |
| else |
| { |
| warning (_("Invalid state, unable to determine sp alias, assuming " |
| "msp.")); |
| cache->active_sp_regnum = tdep->m_profile_msp_regnum; |
| } |
| } |
| else |
| { |
| cache->msp_s |
| = get_frame_register_unsigned (frame, ARM_SP_REGNUM); |
| |
| cache->active_sp_regnum = ARM_SP_REGNUM; |
| } |
| } |
| |
| /* Return the requested stack pointer value (in REGNUM), taking into |
| account whether we have a Security extension or an M-profile |
| CPU. */ |
| |
| static CORE_ADDR |
| arm_cache_get_sp_register (struct arm_prologue_cache *cache, |
| arm_gdbarch_tdep *tdep, int regnum) |
| { |
| if (tdep->have_sec_ext) |
| { |
| if (regnum == tdep->m_profile_msp_s_regnum) |
| return cache->msp_s; |
| if (regnum == tdep->m_profile_msp_ns_regnum) |
| return cache->msp_ns; |
| if (regnum == tdep->m_profile_psp_s_regnum) |
| return cache->psp_s; |
| if (regnum == tdep->m_profile_psp_ns_regnum) |
| return cache->psp_ns; |
| if (regnum == tdep->m_profile_msp_regnum) |
| return arm_cache_get_sp_register (cache, tdep, cache->active_msp_regnum); |
| if (regnum == tdep->m_profile_psp_regnum) |
| return arm_cache_get_sp_register (cache, tdep, cache->active_psp_regnum); |
| if (regnum == ARM_SP_REGNUM) |
| return arm_cache_get_sp_register (cache, tdep, cache->active_sp_regnum); |
| } |
| else if (tdep->is_m) |
| { |
| if (regnum == tdep->m_profile_msp_regnum) |
| return cache->msp_s; |
| if (regnum == tdep->m_profile_psp_regnum) |
| return cache->psp_s; |
| if (regnum == ARM_SP_REGNUM) |
| return arm_cache_get_sp_register (cache, tdep, cache->active_sp_regnum); |
| } |
| else if (regnum == ARM_SP_REGNUM) |
| return cache->sp; |
| |
| gdb_assert_not_reached ("Invalid SP selection"); |
| } |
| |
| /* Return the previous stack address, depending on which SP register |
| is active. */ |
| |
| static CORE_ADDR |
| arm_cache_get_prev_sp_value (struct arm_prologue_cache *cache, arm_gdbarch_tdep *tdep) |
| { |
| CORE_ADDR val = arm_cache_get_sp_register (cache, tdep, cache->active_sp_regnum); |
| return val; |
| } |
| |
| /* Set the active stack pointer to VAL. */ |
| |
| static void |
| arm_cache_set_active_sp_value (struct arm_prologue_cache *cache, |
| arm_gdbarch_tdep *tdep, CORE_ADDR val) |
| { |
| if (tdep->have_sec_ext) |
| { |
| if (cache->active_sp_regnum == tdep->m_profile_msp_s_regnum) |
| cache->msp_s = val; |
| else if (cache->active_sp_regnum == tdep->m_profile_msp_ns_regnum) |
| cache->msp_ns = val; |
| else if (cache->active_sp_regnum == tdep->m_profile_psp_s_regnum) |
| cache->psp_s = val; |
| else if (cache->active_sp_regnum == tdep->m_profile_psp_ns_regnum) |
| cache->psp_ns = val; |
| |
| return; |
| } |
| else if (tdep->is_m) |
| { |
| if (cache->active_sp_regnum == tdep->m_profile_msp_regnum) |
| cache->msp_s = val; |
| else if (cache->active_sp_regnum == tdep->m_profile_psp_regnum) |
| cache->psp_s = val; |
| |
| return; |
| } |
| else if (cache->active_sp_regnum == ARM_SP_REGNUM) |
| { |
| cache->sp = val; |
| return; |
| } |
| |
| gdb_assert_not_reached ("Invalid SP selection"); |
| } |
| |
| /* Return true if REGNUM is one of the alternative stack pointers. */ |
| |
| static bool |
| arm_is_alternative_sp_register (arm_gdbarch_tdep *tdep, int regnum) |
| { |
| if ((regnum == tdep->m_profile_msp_regnum) |
| || (regnum == tdep->m_profile_msp_s_regnum) |
| || (regnum == tdep->m_profile_msp_ns_regnum) |
| || (regnum == tdep->m_profile_psp_regnum) |
| || (regnum == tdep->m_profile_psp_s_regnum) |
| || (regnum == tdep->m_profile_psp_ns_regnum)) |
| return true; |
| else |
| return false; |
| } |
| |
| /* Set the active stack pointer to SP_REGNUM. */ |
| |
| static void |
| arm_cache_switch_prev_sp (struct arm_prologue_cache *cache, |
| arm_gdbarch_tdep *tdep, int sp_regnum) |
| { |
| gdb_assert (arm_is_alternative_sp_register (tdep, sp_regnum)); |
| |
| if (tdep->have_sec_ext) |
| { |
| gdb_assert (sp_regnum != tdep->m_profile_msp_regnum |
| && sp_regnum != tdep->m_profile_psp_regnum); |
| |
| if (sp_regnum == tdep->m_profile_msp_s_regnum |
| || sp_regnum == tdep->m_profile_psp_s_regnum) |
| { |
| cache->active_msp_regnum = tdep->m_profile_msp_s_regnum; |
| cache->active_psp_regnum = tdep->m_profile_psp_s_regnum; |
| } |
| else if (sp_regnum == tdep->m_profile_msp_ns_regnum |
| || sp_regnum == tdep->m_profile_psp_ns_regnum) |
| { |
| cache->active_msp_regnum = tdep->m_profile_msp_ns_regnum; |
| cache->active_psp_regnum = tdep->m_profile_psp_ns_regnum; |
| } |
| } |
| |
| cache->active_sp_regnum = sp_regnum; |
| } |
| |
| namespace { |
| |
| /* Abstract class to read ARM instructions from memory. */ |
| |
| class arm_instruction_reader |
| { |
| public: |
| /* Read a 4 bytes instruction from memory using the BYTE_ORDER endianness. */ |
| virtual uint32_t read (CORE_ADDR memaddr, bfd_endian byte_order) const = 0; |
| }; |
| |
| /* Read instructions from target memory. */ |
| |
| class target_arm_instruction_reader : public arm_instruction_reader |
| { |
| public: |
| uint32_t read (CORE_ADDR memaddr, bfd_endian byte_order) const override |
| { |
| return read_code_unsigned_integer (memaddr, 4, byte_order); |
| } |
| }; |
| |
| } /* namespace */ |
| |
| static CORE_ADDR arm_analyze_prologue |
| (struct gdbarch *gdbarch, CORE_ADDR prologue_start, CORE_ADDR prologue_end, |
| struct arm_prologue_cache *cache, const arm_instruction_reader &insn_reader); |
| |
| /* Architecture version for displaced stepping. This effects the behaviour of |
| certain instructions, and really should not be hard-wired. */ |
| |
| #define DISPLACED_STEPPING_ARCH_VERSION 5 |
| |
| /* See arm-tdep.h. */ |
| |
| bool arm_apcs_32 = true; |
| bool arm_unwind_secure_frames = true; |
| |
| /* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode. */ |
| |
| int |
| arm_psr_thumb_bit (struct gdbarch *gdbarch) |
| { |
| arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); |
| |
| if (tdep->is_m) |
| return XPSR_T; |
| else |
| return CPSR_T; |
| } |
| |
| /* Determine if the processor is currently executing in Thumb mode. */ |
| |
| int |
| arm_is_thumb (struct regcache *regcache) |
| { |
| ULONGEST cpsr; |
| ULONGEST t_bit = arm_psr_thumb_bit (regcache->arch ()); |
| |
| cpsr = regcache_raw_get_unsigned (regcache, ARM_PS_REGNUM); |
| |
| return (cpsr & t_bit) != 0; |
| } |
| |
| /* Determine if FRAME is executing in Thumb mode. FRAME must be an ARM |
| frame. */ |
| |
| int |
| arm_frame_is_thumb (const frame_info_ptr &frame) |
| { |
| /* Check the architecture of FRAME. */ |
| struct gdbarch *gdbarch = get_frame_arch (frame); |
| gdb_assert (gdbarch_bfd_arch_info (gdbarch)->arch == bfd_arch_arm); |
| |
| /* Every ARM frame unwinder can unwind the T bit of the CPSR, either |
| directly (from a signal frame or dummy frame) or by interpreting |
| the saved LR (from a prologue or DWARF frame). So consult it and |
| trust the unwinders. */ |
| CORE_ADDR cpsr = get_frame_register_unsigned (frame, ARM_PS_REGNUM); |
| |
| /* Find and extract the thumb bit. */ |
| ULONGEST t_bit = arm_psr_thumb_bit (gdbarch); |
| return (cpsr & t_bit) != 0; |
| } |
| |
| /* Search for the mapping symbol covering MEMADDR. If one is found, |
| return its type. Otherwise, return 0. If START is non-NULL, |
| set *START to the location of the mapping symbol. */ |
| |
| static char |
| arm_find_mapping_symbol (CORE_ADDR memaddr, CORE_ADDR *start) |
| { |
| struct obj_section *sec; |
| |
| /* If there are mapping symbols, consult them. */ |
| sec = find_pc_section (memaddr); |
| if (sec != NULL) |
| { |
| arm_per_bfd *data = arm_bfd_data_key.get (sec->objfile->obfd.get ()); |
| if (data != NULL) |
| { |
| unsigned int section_idx = sec->the_bfd_section->index; |
| arm_mapping_symbol_vec &map |
| = data->section_maps[section_idx]; |
| |
| /* Sort the vector on first use. */ |
| if (!data->section_maps_sorted[section_idx]) |
| { |
| std::sort (map.begin (), map.end ()); |
| data->section_maps_sorted[section_idx] = true; |
| } |
| |
| arm_mapping_symbol map_key = { memaddr - sec->addr (), 0 }; |
| arm_mapping_symbol_vec::const_iterator it |
| = std::lower_bound (map.begin (), map.end (), map_key); |
| |
| /* std::lower_bound finds the earliest ordered insertion |
| point. If the symbol at this position starts at this exact |
| address, we use that; otherwise, the preceding |
| mapping symbol covers this address. */ |
| if (it < map.end ()) |
| { |
| if (it->value == map_key.value) |
| { |
| if (start) |
| *start = it->value + sec->addr (); |
| return it->type; |
| } |
| } |
| |
| if (it > map.begin ()) |
| { |
| arm_mapping_symbol_vec::const_iterator prev_it |
| = it - 1; |
| |
| if (start) |
| *start = prev_it->value + sec->addr (); |
| return prev_it->type; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* 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. */ |
| |
| int |
| arm_pc_is_thumb (struct gdbarch *gdbarch, CORE_ADDR memaddr) |
| { |
| char type; |
| arm_displaced_step_copy_insn_closure *dsc = nullptr; |
| arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); |
| |
| if (gdbarch_displaced_step_copy_insn_closure_by_addr_p (gdbarch)) |
| dsc = ((arm_displaced_step_copy_insn_closure * ) |
| gdbarch_displaced_step_copy_insn_closure_by_addr |
| (gdbarch, current_inferior (), memaddr)); |
| |
| /* If checking the mode of displaced instruction in copy area, the mode |
| should be determined by instruction on the original address. */ |
| if (dsc) |
| { |
| displaced_debug_printf ("check mode of %.8lx instead of %.8lx", |
| (unsigned long) dsc->insn_addr, |
| (unsigned long) memaddr); |
| memaddr = dsc->insn_addr; |
| } |
| |
| /* If bit 0 of the address is set, assume this is a Thumb address. */ |
| if (IS_THUMB_ADDR (memaddr)) |
| return 1; |
| |
| /* If the user wants to override the symbol table, let him. */ |
| if (strcmp (arm_force_mode_string, "arm") == 0) |
| return 0; |
| if (strcmp (arm_force_mode_string, "thumb") == 0) |
| return 1; |
| |
| /* ARM v6-M and v7-M are always in Thumb mode. */ |
| if (tdep->is_m) |
| return 1; |
| |
| /* If there are mapping symbols, consult them. */ |
| type = arm_find_mapping_symbol (memaddr, NULL); |
| if (type) |
| return type == 't'; |
| |
| /* Thumb functions have a "special" bit set in minimal symbols. */ |
| bound_minimal_symbol sym = lookup_minimal_symbol_by_pc (memaddr); |
| 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) |
| return 0; |
| if (strcmp (arm_fallback_mode_string, "thumb") == 0) |
| return 1; |
| |
| /* If we couldn't find any symbol, but we're talking to a running |
| target, then trust the current value of $cpsr. This lets |
| "display/i $pc" always show the correct mode (though if there is |
| a symbol table we will not reach here, so it still may not be |
| displayed in the mode it will be executed). */ |
| if (target_has_registers ()) |
| return arm_frame_is_thumb (get_current_frame ()); |
| |
| /* Otherwise we're out of luck; we assume ARM. */ |
| return 0; |
| } |
| |
| static inline bool |
| arm_m_addr_is_lockup (CORE_ADDR addr) |
| { |
| switch (addr) |
| { |
| /* Values for lockup state. |
| For more details see "B1.5.15 Unrecoverable exception cases" in |
| both ARMv6-M and ARMv7-M Architecture Reference Manuals, or |
| see "B4.32 Lockup" in ARMv8-M Architecture Reference Manual. */ |
| case 0xeffffffe: |
| case 0xfffffffe: |
| case 0xffffffff: |
| return true; |
| |
| default: |
| /* Address is not lockup. */ |
| return false; |
| } |
| } |
| |
| /* Determine if the address specified equals any of these magic return |
| values, called EXC_RETURN, defined by the ARM v6-M, v7-M and v8-M |
| architectures. Also include lockup magic PC value. |
| Check also for FNC_RETURN if we have the v8-M security extension. |
| |
| From ARMv6-M Reference Manual B1.5.8 |
| Table B1-5 Exception return behavior |
| |
| EXC_RETURN Return To Return Stack |
| 0xFFFFFFF1 Handler mode Main |
| 0xFFFFFFF9 Thread mode Main |
| 0xFFFFFFFD Thread mode Process |
| |
| From ARMv7-M Reference Manual B1.5.8 |
| Table B1-8 EXC_RETURN definition of exception return behavior, no FP |
| |
| EXC_RETURN Return To Return Stack |
| 0xFFFFFFF1 Handler mode Main |
| 0xFFFFFFF9 Thread mode Main |
| 0xFFFFFFFD Thread mode Process |
| |
| Table B1-9 EXC_RETURN definition of exception return behavior, with |
| FP |
| |
| EXC_RETURN Return To Return Stack Frame Type |
| 0xFFFFFFE1 Handler mode Main Extended |
| 0xFFFFFFE9 Thread mode Main Extended |
| 0xFFFFFFED Thread mode Process Extended |
| 0xFFFFFFF1 Handler mode Main Basic |
| 0xFFFFFFF9 Thread mode Main Basic |
| 0xFFFFFFFD Thread mode Process Basic |
| |
| For more details see "B1.5.8 Exception return behavior" |
| in both ARMv6-M and ARMv7-M Architecture Reference Manuals. |
| |
| From ARMv8-M Architecture Technical Reference, D1.2.95 |
| FType, Mode and SPSEL bits are to be considered when the Security |
| Extension is not implemented. |
| |
| EXC_RETURN Return To Return Stack Frame Type |
| 0xFFFFFFA0 Handler mode Main Extended |
| 0xFFFFFFA8 Thread mode Main Extended |
| 0xFFFFFFAC Thread mode Process Extended |
| 0xFFFFFFB0 Handler mode Main Standard |
| 0xFFFFFFB8 Thread mode Main Standard |
| 0xFFFFFFBC Thread mode Process Standard */ |
| |
| static int |
| arm_m_addr_is_magic (struct gdbarch *gdbarch, CORE_ADDR addr) |
| { |
| if (arm_m_addr_is_lockup (addr)) |
| return 1; |
| |
| arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); |
| if (tdep->have_sec_ext) |
| { |
| switch ((addr & 0xff000000)) |
| { |
| case 0xff000000: /* EXC_RETURN pattern. */ |
| case 0xfe000000: /* FNC_RETURN pattern. */ |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| else |
| { |
| switch (addr) |
| { |
| /* Values from ARMv8-M Architecture Technical Reference. */ |
| case 0xffffffa0: |
| case 0xffffffa8: |
| case 0xffffffac: |
| case 0xffffffb0: |
| case 0xffffffb8: |
| case 0xffffffbc: |
| /* Values from Tables in B1.5.8 the EXC_RETURN definitions of |
| the exception return behavior. */ |
| case 0xffffffe1: |
| case 0xffffffe9: |
| case 0xffffffed: |
| case 0xfffffff1: |
| case 0xfffffff9: |
| case 0xfffffffd: |
| /* Address is magic. */ |
| return 1; |
| |
| default: |
| /* Address is not magic. */ |
| return 0; |
| } |
| } |
| } |
| |
| /* Remove useless bits from addresses in a running program. */ |
| static CORE_ADDR |
| arm_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR val) |
| { |
| arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); |
| |
| /* On M-profile devices, do not strip the low bit from EXC_RETURN |
| (the magic exception return address). */ |
| if (tdep->is_m && arm_m_addr_is_magic (gdbarch, val)) |
| return val; |
| |
| if (arm_apcs_32) |
| return UNMAKE_THUMB_ADDR (val); |
| else |
| return (val & 0x03fffffc); |
| } |
| |
| /* Return 1 if PC is the start of a compiler helper function which |
| can be safely ignored during prologue skipping. IS_THUMB is true |
| if the function is known to be a Thumb function due to the way it |
| is being called. */ |
| static int |
| skip_prologue_function (struct gdbarch *gdbarch, CORE_ADDR pc, int is_thumb) |
| { |
| enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); |
| |
| bound_minimal_symbol msym = lookup_minimal_symbol_by_pc (pc); |
| if (msym.minsym != NULL |
| && msym.value_address () == pc |
| && msym.minsym->linkage_name () != NULL) |
| { |
| const char *name = msym.minsym->linkage_name (); |
| |
| /* The GNU linker's Thumb call stub to foo is named |
| __foo_from_thumb. */ |
| if (strstr (name, "_from_thumb") != NULL) |
| name += 2; |
| |
| /* On soft-float targets, __truncdfsf2 is called to convert promoted |
| arguments to their argument types in non-prototyped |
| functions. */ |
| if (startswith (name, "__truncdfsf2")) |
| return 1; |
| if (startswith (name, "__aeabi_d2f")) |
| return 1; |
| |
| /* Internal functions related to thread-local storage. */ |
| if (startswith (name, "__tls_get_addr")) |
| return 1; |
| if (startswith (name, "__aeabi_read_tp")) |
| return 1; |
| } |
| else |
| { |
| /* If we run against a stripped glibc, we may be unable to identify |
| special functions by name. Check for one important case, |
| __aeabi_read_tp, by comparing the *code* against the default |
| implementation (this is hand-written ARM assembler in glibc). */ |
| |
| if (!is_thumb |
| && read_code_unsigned_integer (pc, 4, byte_order_for_code) |
| == 0xe3e00a0f /* mov r0, #0xffff0fff */ |
| && read_code_unsigned_integer (pc + 4, 4, byte_order_for_code) |
| == 0xe240f01f) /* sub pc, r0, #31 */ |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* Extract the immediate from instruction movw/movt of encoding T. INSN1 is |
| the first 16-bit of instruction, and INSN2 is the second 16-bit of |
| instruction. */ |
| #define EXTRACT_MOVW_MOVT_IMM_T(insn1, insn2) \ |
| ((bits ((insn1), 0, 3) << 12) \ |
| | (bits ((insn1), 10, 10) << 11) \ |
| | (bits ((insn2), 12, 14) << 8) \ |
| | bits ((insn2), 0, 7)) |
| |
| /* Extract the immediate from instruction movw/movt of encoding A. INSN is |
| the 32-bit instruction. */ |
| #define EXTRACT_MOVW_MOVT_IMM_A(insn) \ |
| ((bits ((insn), 16, 19) << 12) \ |
| | bits ((insn), 0, 11)) |
| |
| /* Decode immediate value; implements ThumbExpandImmediate pseudo-op. */ |
| |
| static unsigned int |
| thumb_expand_immediate (unsigned int imm) |
| { |
| unsigned int count = imm >> 7; |
| |
| if (count < 8) |
| switch (count / 2) |
| { |
| case 0: |
| return imm & 0xff; |
| case 1: |
| return (imm & 0xff) | ((imm & 0xff) << 16); |
| case 2: |
| return ((imm & 0xff) << 8) | ((imm & 0xff) << 24); |
| case 3: |
| return (imm & 0xff) | ((imm & 0xff) << 8) |
| | ((imm & 0xff) << 16) | ((imm & 0xff) << 24); |
| } |
| |
| return (0x80 | (imm & 0x7f)) << (32 - count); |
| } |
| |
| /* Return 1 if the 16-bit Thumb instruction 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 <registers> */ |
| } |
| |
| /* 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. |
| Return the last address which is definitely safe to skip for an |
| initial breakpoint. */ |
| |
| static CORE_ADDR |
| thumb_analyze_prologue (struct gdbarch *gdbarch, |
| CORE_ADDR start, CORE_ADDR limit, |
| struct arm_prologue_cache *cache) |
| { |
| arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); |
| int i; |
| pv_t regs[16]; |
| CORE_ADDR offset; |
| CORE_ADDR unrecognized_pc = 0; |
| |
| for (i = 0; i < 16; i++) |
| regs[i] = pv_register (i, 0); |
| pv_area stack (ARM_SP_REGNUM, gdbarch_addr_bit (gdbarch)); |
| |
| while (start < limit) |
| { |
| unsigned short insn; |
| std::optional<bool> ra_signed_state; |
| |
| insn = read_code_unsigned_integer (start, 2, byte_order_for_code); |
| |
| if ((insn & 0xfe00) == 0xb400) /* push { rlist } */ |
| { |
| int regno; |
| int mask; |
| |
| if (stack.store_would_trash (regs[ARM_SP_REGNUM])) |
| break; |
| |
| /* Bits 0-7 contain a mask for registers R0-R7. Bit 8 says |
| whether to save LR (R14). */ |
| mask = (insn & 0xff) | ((insn & 0x100) << 6); |
| |
| /* Calculate offsets of saved R0-R7 and LR. */ |
| for (regno = ARM_LR_REGNUM; regno >= 0; regno--) |
| if (mask & (1 << regno)) |
| { |
| regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], |
| -4); |
| stack.store (regs[ARM_SP_REGNUM], 4, regs[regno]); |
| } |
| } |
| else if ((insn & 0xff80) == 0xb080) /* sub sp, #imm */ |
| { |
| offset = (insn & 0x7f) << 2; /* get scaled 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], |
| (insn & 0xff) << 2); |
| else if ((insn & 0xfe00) == 0x1c00 /* add Rd, Rn, #imm */ |
| && pv_is_register (regs[bits (insn, 3, 5)], ARM_SP_REGNUM)) |
| regs[bits (insn, 0, 2)] = pv_add_constant (regs[bits (insn, 3, 5)], |
| bits (insn, 6, 8)); |
| else if ((insn & 0xf800) == 0x3000 /* add Rd, #imm */ |
| && pv_is_register (regs[bits (insn, 8, 10)], ARM_SP_REGNUM)) |
| regs[bits (insn, 8, 10)] = pv_add_constant (regs[bits (insn, 8, 10)], |
| bits (insn, 0, 7)); |
| else if ((insn & 0xfe00) == 0x1800 /* add Rd, Rn, Rm */ |
| && pv_is_register (regs[bits (insn, 6, 8)], ARM_SP_REGNUM) |
| && pv_is_constant (regs[bits (insn, 3, 5)])) |
| regs[bits (insn, 0, 2)] = pv_add (regs[bits (insn, 3, 5)], |
| regs[bits (insn, 6, 8)]); |
| else if ((insn & 0xff00) == 0x4400 /* add Rd, Rm */ |
| && pv_is_constant (regs[bits (insn, 3, 6)])) |
| { |
| int rd = (bit (insn, 7) << 3) + bits (insn, 0, 2); |
| int rm = bits (insn, 3, 6); |
| regs[rd] = pv_add (regs[rd], regs[rm]); |
| } |
| else if ((insn & 0xff00) == 0x4600) /* mov hi, lo or mov lo, hi */ |
| { |
| int dst_reg = (insn & 0x7) + ((insn & 0x80) >> 4); |
| int src_reg = (insn & 0x78) >> 3; |
| regs[dst_reg] = regs[src_reg]; |
| } |
| else if ((insn & 0xf800) == 0x9000) /* str rd, [sp, #off] */ |
| { |
| /* Handle stores to the stack. Normally pushes are used, |
| but with GCC -mtpcs-frame, there may be other stores |
| in the prologue to create the frame. */ |
| int regno = (insn >> 8) & 0x7; |
| pv_t addr; |
| |
| offset = (insn & 0xff) << 2; |
| addr = pv_add_constant (regs[ARM_SP_REGNUM], offset); |
| |
| if (stack.store_would_trash (addr)) |
| break; |
| |
| stack.store (addr, 4, regs[regno]); |
| } |
| else if ((insn & 0xf800) == 0x6000) /* str rd, [rn, #off] */ |
| { |
| int rd = bits (insn, 0, 2); |
| int rn = bits (insn, 3, 5); |
| pv_t addr; |
| |
| offset = bits (insn, 6, 10) << 2; |
| addr = pv_add_constant (regs[rn], offset); |
| |
| if (stack.store_would_trash (addr)) |
| break; |
| |
| stack.store (addr, 4, regs[rd]); |
| } |
| else if (((insn & 0xf800) == 0x7000 /* strb Rd, [Rn, #off] */ |
| || (insn & 0xf800) == 0x8000) /* strh Rd, [Rn, #off] */ |
| && pv_is_register (regs[bits (insn, 3, 5)], ARM_SP_REGNUM)) |
| /* Ignore stores of argument registers to the stack. */ |
| ; |
| else if ((insn & 0xf800) == 0xc800 /* ldmia Rn!, { registers } */ |
| && pv_is_register (regs[bits (insn, 8, 10)], ARM_SP_REGNUM)) |
| /* Ignore block loads from the stack, potentially copying |
| parameters from memory. */ |
| ; |
| else if ((insn & 0xf800) == 0x9800 /* ldr Rd, [Rn, #immed] */ |
| || ((insn & 0xf800) == 0x6800 /* ldr Rd, [sp, #immed] */ |
| && pv_is_register (regs[bits (insn, 3, 5)], ARM_SP_REGNUM))) |
| /* Similarly ignore single loads from the stack. */ |
| ; |
| else if ((insn & 0xffc0) == 0x0000 /* lsls Rd, Rm, #0 */ |
| || (insn & 0xffc0) == 0x1c00) /* add Rd, Rn, #0 */ |
| /* Skip register copies, i.e. saves to another register |
| instead of the stack. */ |
| ; |
| else if ((insn & 0xf800) == 0x2000) /* movs Rd, #imm */ |
| /* Recognize constant loads; even with small stacks these are necessary |
| on Thumb. */ |
| regs[bits (insn, 8, 10)] = pv_constant (bits (insn, 0, 7)); |
| else if ((insn & 0xf800) == 0x4800) /* ldr Rd, [pc, #imm] */ |
| { |
| /* Constant pool loads, for the same reason. */ |
| unsigned int constant; |
| CORE_ADDR loc; |
| |
| loc = start + 4 + bits (insn, 0, 7) * 4; |
| constant = read_memory_unsigned_integer (loc, 4, byte_order); |
| regs[bits (insn, 8, 10)] = pv_constant (constant); |
| } |
| else if (thumb_insn_size (insn) == 4) /* 32-bit Thumb-2 instructions. */ |
| { |
| unsigned short inst2; |
| |
| inst2 = read_code_unsigned_integer (start + 2, 2, |
| byte_order_for_code); |
| uint32_t whole_insn = (insn << 16) | inst2; |
| |
| if ((insn & 0xf800) == 0xf000 && (inst2 & 0xe800) == 0xe800) |
| { |
| /* BL, BLX. Allow some special function calls when |
| skipping the prologue; GCC generates these before |
| storing arguments to the stack. */ |
| CORE_ADDR nextpc; |
| int j1, j2, imm1, imm2; |
| |
| imm1 = sbits (insn, 0, 10); |
| imm2 = bits (inst2, 0, 10); |
| j1 = bit (inst2, 13); |
| j2 = bit (inst2, 11); |
| |
| offset = ((imm1 << 12) + (imm2 << 1)); |
| offset ^= ((!j2) << 22) | ((!j1) << 23); |
| |
| nextpc = start + 4 + offset; |
| /* For BLX make sure to clear the low bits. */ |
| if (bit (inst2, 12) == 0) |
| nextpc = nextpc & 0xfffffffc; |
| |
| if (!skip_prologue_function (gdbarch, nextpc, |
| bit (inst2, 12) != 0)) |
| break; |
| } |
| |
| else if ((insn & 0xffd0) == 0xe900 /* stmdb Rn{!}, |
| { registers } */ |
| && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) |
| { |
| pv_t addr = regs[bits (insn, 0, 3)]; |
| int regno; |
| |
| if (stack.store_would_trash (addr)) |
| break; |
| |
| /* Calculate offsets of saved registers. */ |
| for (regno = ARM_LR_REGNUM; regno >= 0; regno--) |
| if (inst2 & (1 << regno)) |
| { |
| addr = pv_add_constant (addr, -4); |
| stack.store (addr, 4, regs[regno]); |
| } |
| |
| if (insn & 0x0020) |
| regs[bits (insn, 0, 3)] = addr; |
| } |
| |
| /* vstmdb Rn{!}, { D-registers } (aka vpush). */ |
| else if ((insn & 0xff20) == 0xed20 |
| && (inst2 & 0x0f00) == 0x0b00 |
| && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) |
| { |
| /* Address SP points to. */ |
| pv_t addr = regs[bits (insn, 0, 3)]; |
| |
| /* Number of registers saved. */ |
| unsigned int number = bits (inst2, 0, 7) >> 1; |
| |
| /* First register to save. */ |
| int vd = bits (inst2, 12, 15) | (bits (insn, 6, 6) << 4); |
| |
| if (stack.store_would_trash (addr)) |
| break; |
| |
| /* Calculate offsets of saved registers. */ |
| for (; number > 0; number--) |
| { |
| addr = pv_add_constant (addr, -8); |
| stack.store (addr, 8, pv_register (ARM_D0_REGNUM |
| + vd + number, 0)); |
| } |
| |
| /* Writeback SP to account for the saved registers. */ |
| regs[bits (insn, 0, 3)] = addr; |
| } |
| |
| else if ((insn & 0xff50) == 0xe940 /* strd Rt, Rt2, |
| [Rn, #+/-imm]{!} */ |
| && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) |
| { |
| int regno1 = bits (inst2, 12, 15); |
| int regno2 = bits (inst2, 8, 11); |
| pv_t addr = regs[bits (insn, 0, 3)]; |
| |
| offset = inst2 & 0xff; |
| if (insn & 0x0080) |
| addr = pv_add_constant (addr, offset); |
| else |
| addr = pv_add_constant (addr, -offset); |
| |
| if (stack.store_would_trash (addr)) |
| break; |
| |
| stack.store (addr, 4, regs[regno1]); |
| stack.store (pv_add_constant (addr, 4), |
| 4, regs[regno2]); |
| |
| if (insn & 0x0020) |
| regs[bits (insn, 0, 3)] = addr; |
| } |
| |
| else if ((insn & 0xfff0) == 0xf8c0 /* str Rt,[Rn,+/-#imm]{!} */ |
| && (inst2 & 0x0c00) == 0x0c00 |
| && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) |
| { |
| int regno = bits (inst2, 12, 15); |
| pv_t addr = regs[bits (insn, 0, 3)]; |
| |
| offset = inst2 & 0xff; |
| if (inst2 & 0x0200) |
| addr = pv_add_constant (addr, offset); |
| else |
| addr = pv_add_constant (addr, -offset); |
| |
| if (stack.store_would_trash (addr)) |
| break; |
| |
| stack.store (addr, 4, regs[regno]); |
| |
| if (inst2 & 0x0100) |
| regs[bits (insn, 0, 3)] = addr; |
| } |
| |
| else if ((insn & 0xfff0) == 0xf8c0 /* str.w Rt,[Rn,#imm] */ |
| && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) |
| { |
| int regno = bits (inst2, 12, 15); |
| pv_t addr; |
| |
| offset = inst2 & 0xfff; |
| addr = pv_add_constant (regs[bits (insn, 0, 3)], offset); |
| |
| if (stack.store_would_trash (addr)) |
| break; |
| |
| stack.store (addr, 4, regs[regno]); |
| } |
| |
| else if ((insn & 0xffd0) == 0xf880 /* str{bh}.w Rt,[Rn,#imm] */ |
| && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) |
| /* Ignore stores of argument registers to the stack. */ |
| ; |
| |
| else if ((insn & 0xffd0) == 0xf800 /* str{bh} Rt,[Rn,#+/-imm] */ |
| && (inst2 & 0x0d00) == 0x0c00 |
| && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) |
| /* Ignore stores of argument registers to the stack. */ |
| ; |
| |
| else if ((insn & 0xffd0) == 0xe890 /* ldmia Rn[!], |
| { registers } */ |
| && (inst2 & 0x8000) == 0x0000 |
| && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) |
| /* Ignore block loads from the stack, potentially copying |
| parameters from memory. */ |
| ; |
| |
| else if ((insn & 0xff70) == 0xe950 /* ldrd Rt, Rt2, |
| [Rn, #+/-imm] */ |
| && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) |
| /* Similarly ignore dual loads from the stack. */ |
| ; |
| |
| else if ((insn & 0xfff0) == 0xf850 /* ldr Rt,[Rn,#+/-imm] */ |
| && (inst2 & 0x0d00) == 0x0c00 |
| && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) |
| /* Similarly ignore single loads from the stack. */ |
| ; |
| |
| else if ((insn & 0xfff0) == 0xf8d0 /* ldr.w Rt,[Rn,#imm] */ |
| && pv_is_register (regs[bits (insn, 0, 3)], ARM_SP_REGNUM)) |
| /* Similarly ignore single loads from the stack. */ |
| ; |
| |
| else if ((insn & 0xfbf0) == 0xf100 /* add.w Rd, Rn, #imm */ |
| && (inst2 & 0x8000) == 0x0000) |
| { |
| unsigned int imm = ((bits (insn, 10, 10) << 11) |
| | (bits (inst2, 12, 14) << 8) |
| | bits (inst2, 0, 7)); |
| |
| regs[bits (inst2, 8, 11)] |
| = pv_add_constant (regs[bits (insn, 0, 3)], |
| thumb_expand_immediate (imm)); |
| } |
| |
| else if ((insn & 0xfbf0) == 0xf200 /* addw Rd, Rn, #imm */ |
| && (inst2 & 0x8000) == 0x0000) |
| { |
| unsigned int imm = ((bits (insn, 10, 10) << 11) |
| | (bits (inst2, 12, 14) << 8) |
| | bits (inst2, 0, 7)); |
| |
| regs[bits (inst2, 8, 11)] |
| = pv_add_constant (regs[bits (insn, 0, 3)], imm); |
| } |
| |
| else if ((insn & 0xfbf0) == 0xf1a0 /* sub.w Rd, Rn, #imm */ |
| && (inst2 & 0x8000) == 0x0000) |
| { |
| unsigned int imm = ((bits (insn, 10, 10) << 11) |
| | (bits (inst2, 12, 14) << 8) |
| | bits (inst2, 0, 7)); |
| |
| regs[bits (inst2, 8, 11)] |
| = pv_add_constant (regs[bits (insn, 0, 3)], |
| - (CORE_ADDR) thumb_expand_immediate (imm)); |
| } |
| |
| else if ((insn & 0xfbf0) == 0xf2a0 /* subw Rd, Rn, #imm */ |
| && (inst2 & 0x8000) == 0x0000) |
| { |
| unsigned int imm = ((bits (insn, 10, 10) << 11) |
| | (bits (inst2, 12, 14) << 8) |
| | bits (inst2, 0, 7)); |
| |
| regs[bits (inst2, 8, 11)] |
| = pv_add_constant (regs[bits (insn, 0, 3)], - (CORE_ADDR) imm); |
| } |
| |
| else if ((insn & 0xfbff) == 0xf04f) /* mov.w Rd, #const */ |
| { |
| unsigned int imm = ((bits (insn, 10, 10) << 11) |
| | (bits (inst2, 12, 14) << 8) |
| | bits (inst2, 0, 7)); |
| |
| regs[bits (inst2, 8, 11)] |
| = pv_constant (thumb_expand_immediate (imm)); |
| } |
| |
| else if ((insn & 0xfbf0) == 0xf240) /* movw Rd, #const */ |
| { |
| unsigned int imm |
| = EXTRACT_MOVW_MOVT_IMM_T (insn, inst2); |
| |
| regs[bits (inst2, 8, 11)] = pv_constant (imm); |
| } |
| |
| else if (insn == 0xea5f /* mov.w Rd,Rm */ |
| && (inst2 & 0xf0f0) == 0) |
| { |
| int dst_reg = (inst2 & 0x0f00) >> 8; |
| int src_reg = inst2 & 0xf; |
| regs[dst_reg] = regs[src_reg]; |
| } |
| |
| else if ((insn & 0xff7f) == 0xf85f) /* ldr.w Rt,<label> */ |
| { |
| /* Constant pool loads. */ |
| unsigned int constant; |
| CORE_ADDR loc; |
| |
| offset = bits (inst2, 0, 11); |
| if (insn & 0x0080) |
| loc = start + 4 + offset; |
| else |
| loc = start + 4 - offset; |
| |
| constant = read_memory_unsigned_integer (loc, 4, byte_order); |
| regs[bits (inst2, 12, 15)] = pv_constant (constant); |
| } |
| |
| else if ((insn & 0xff7f) == 0xe95f) /* ldrd Rt,Rt2,<label> */ |
| { |
| /* Constant pool loads. */ |
| unsigned int constant; |
| CORE_ADDR loc; |
| |
| offset = bits (inst2, 0, 7) << 2; |
| if (insn & 0x0080) |
| loc = start + 4 + offset; |
| else |
| loc = start + 4 - offset; |
| |
| constant = read_memory_unsigned_integer (loc, 4, byte_order); |
| regs[bits (inst2, 12, 15)] = pv_constant (constant); |
| |
| constant = read_memory_unsigned_integer (loc + 4, 4, byte_order); |
| regs[bits (inst2, 8, 11)] = pv_constant (constant); |
| } |
| /* Start of ARMv8.1-m PACBTI extension instructions. */ |
| else if (IS_PAC (whole_insn)) |
| { |
| /* LR and SP are input registers. PAC is in R12. LR is |
| signed from this point onwards. NOP space. */ |
| ra_signed_state = true; |
| } |
| else if (IS_PACBTI (whole_insn)) |
| { |
| /* LR and SP are input registers. PAC is in R12 and PC is a |
| valid BTI landing pad. LR is signed from this point onwards. |
| NOP space. */ |
| ra_signed_state = true; |
| } |
| else if (IS_BTI (whole_insn)) |
| { |
| /* Valid BTI landing pad. NOP space. */ |
| } |
| else if (IS_PACG (whole_insn)) |
| { |
| /* Sign Rn using Rm and store the PAC in Rd. Rd is signed from |
| this point onwards. */ |
| ra_signed_state = true; |
| } |
| else if (IS_AUT (whole_insn) || IS_AUTG (whole_insn)) |
| { |
| /* These instructions appear close to the epilogue, when signed |
| pointers are getting authenticated. */ |
| ra_signed_state = false; |
| } |
| /* End of ARMv8.1-m PACBTI extension instructions */ |
| else if (thumb2_instruction_changes_pc (insn, inst2)) |
| { |
| /* Don't scan past anything that might change control flow. */ |
| break; |
| } |
| else |
| { |
| /* The optimizer might shove anything into the prologue, |
| so we just skip what we don't recognize. */ |
| unrecognized_pc = start; |
| } |
| |
| /* Make sure we are dealing with a target that supports ARMv8.1-m |
| PACBTI. */ |
| if (cache != nullptr && tdep->have_pacbti |
| && ra_signed_state.has_value ()) |
| { |
| arm_debug_printf ("Found pacbti instruction at %s", |
| paddress (gdbarch, start)); |
| arm_debug_printf ("RA is %s", |
| *ra_signed_state ? "signed" : "not signed"); |
| cache->ra_signed_state = ra_signed_state; |
| } |
| |
| start += 2; |
| } |
| else if (thumb_instruction_changes_pc (insn)) |
| { |
| /* Don't scan past anything that might change control flow. */ |
| break; |
| } |
| else |
| { |
| /* The optimizer might shove anything into the prologue, |
| so we just skip what we don't recognize. */ |
| unrecognized_pc = start; |
| } |
| |
| start += 2; |
| } |
| |
| arm_debug_printf ("Prologue scan stopped at %s", |
| paddress (gdbarch, start)); |
| |
| if (unrecognized_pc == 0) |
| unrecognized_pc = start; |
| |
| if (cache == NULL) |
| return unrecognized_pc; |
| |
| if (pv_is_register (regs[ARM_FP_REGNUM], ARM_SP_REGNUM)) |
| { |
| /* Frame pointer is fp. Frame size is constant. */ |
| cache->framereg = ARM_FP_REGNUM; |
| cache->framesize = -regs[ARM_FP_REGNUM].k; |
| } |
| else if (pv_is_register (regs[THUMB_FP_REGNUM], ARM_SP_REGNUM)) |
| { |
| /* Frame pointer is r7. Frame size is constant. */ |
| cache->framereg = THUMB_FP_REGNUM; |
| cache->framesize = -regs[THUMB_FP_REGNUM].k; |
| } |
| else |
| { |
| /* Try the stack pointer... this is a bit desperate. */ |
| cache->framereg = ARM_SP_REGNUM; |
| cache->framesize = -regs[ARM_SP_REGNUM].k; |
| } |
| |
| for (i = 0; i < gdbarch_num_regs (gdbarch); i++) |
| if (stack.find_reg (gdbarch, i, &offset)) |
| { |
| cache->saved_regs[i].set_addr (offset); |
| if (i == ARM_SP_REGNUM) |
| arm_cache_set_active_sp_value(cache, tdep, offset); |
| } |
| |
| return unrecognized_pc; |
| } |
| |
| |
| /* Try to analyze the instructions starting from PC, which load symbol |
| __stack_chk_guard. Return the address of instruction after loading this |
| symbol, set the dest register number to *BASEREG, and set the size of |
| instructions for loading symbol in OFFSET. Return 0 if instructions are |
| not recognized. */ |
| |
| static CORE_ADDR |
| arm_analyze_load_stack_chk_guard(CORE_ADDR pc, struct gdbarch *gdbarch, |
| unsigned int *destreg, int *offset) |
| { |
| enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); |
| int is_thumb = arm_pc_is_thumb (gdbarch, pc); |
| unsigned int low, high, address; |
| |
| address = 0; |
| if (is_thumb) |
| { |
| unsigned short insn1 |
| = read_code_unsigned_integer (pc, 2, byte_order_for_code); |
| |
| if ((insn1 & 0xf800) == 0x4800) /* ldr Rd, #immed */ |
| { |
| *destreg = bits (insn1, 8, 10); |
| *offset = 2; |
| 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 */ |
| { |
| unsigned short insn2 |
| = read_code_unsigned_integer (pc + 2, 2, byte_order_for_code); |
| |
| low = EXTRACT_MOVW_MOVT_IMM_T (insn1, insn2); |
| |
| insn1 |
| = read_code_unsigned_integer (pc + 4, 2, byte_order_for_code); |
| insn2 |
| = read_code_unsigned_integer (pc + 6, 2, byte_order_for_code); |
| |
| /* movt Rd, #const */ |
| if ((insn1 & 0xfbc0) == 0xf2c0) |
| { |
| high = EXTRACT_MOVW_MOVT_IMM_T (insn1, insn2); |
| *destreg = bits (insn2, 8, 11); |
| *offset = 8; |
| address = (high << 16 | low); |
| } |
| } |
| } |
| else |
| { |
| unsigned int insn |
| = read_code_unsigned_integer (pc, 4, byte_order_for_code); |
| |
| if ((insn & 0x0e5f0000) == 0x041f0000) /* ldr Rd, [PC, #immed] */ |
| { |
| 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; |
| } |
| else if ((insn & 0x0ff00000) == 0x03000000) /* movw Rd, #const */ |
| { |
| low = EXTRACT_MOVW_MOVT_IMM_A (insn); |
| |
| insn |
| = read_code_unsigned_integer (pc + 4, 4, byte_order_for_code); |
| |
| if ((insn & 0x0ff00000) == 0x03400000) /* movt Rd, #const */ |
| { |
| high = EXTRACT_MOVW_MOVT_IMM_A (insn); |
| *destreg = bits (insn, 12, 15); |
| *offset = 8; |
| address = (high << 16 | low); |
| } |
| } |
| } |
| |
| return address; |
| } |
| |
| /* Try to skip a sequence of instructions used for stack protector. If PC |
| points to the first instruction of this sequence, return the address of |
| first instruction after this sequence, otherwise, return original PC. |
| |
| On arm, this sequence of instructions is composed of mainly three steps, |
| Step 1: load symbol __stack_chk_guard, |
| Step 2: load from address of __stack_chk_guard, |
| Step 3: store it to somewhere else. |
| |
| Usually, instructions on step 2 and step 3 are the same on various ARM |
| architectures. On step 2, it is one instruction 'ldr Rx, [Rn, #0]', and |
| on step 3, it is also one instruction 'str Rx, [r7, #immd]'. However, |
| instructions in step 1 vary from different ARM architectures. On ARMv7, |
| they are, |
| |
| movw Rn, #:lower16:__stack_chk_guard |
| movt Rn, #:upper16:__stack_chk_guard |
| |
| On ARMv5t, it is, |
| |
| ldr Rn, .Label |
| .... |
| .Lable: |
| .word __stack_chk_guard |
| |
| Since ldr/str is a very popular instruction, we can't use them as |
| 'fingerprint' or 'signature' of stack protector sequence. Here we choose |
| sequence {movw/movt, ldr}/ldr/str plus symbol __stack_chk_guard, if not |
| stripped, as the 'fingerprint' of a stack protector cdoe sequence. */ |
| |
| 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 basereg; |
| int offset; |
| int is_thumb = arm_pc_is_thumb (gdbarch, pc); |
| CORE_ADDR addr; |
| |
| /* Try to parse the instructions in Step 1. */ |
| addr = arm_analyze_load_stack_chk_guard (pc, gdbarch, |
| &basereg, &offset); |
| if (!addr) |
| return pc; |
| |
| bound_minimal_symbol stack_chk_guard = lookup_minimal_symbol_by_pc (addr); |
| /* 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 |
| || !startswith (stack_chk_guard.minsym->linkage_name (), "__stack_chk_guard")) |
| return pc; |
| |
| if (is_thumb) |
| { |
| unsigned int destreg; |
| unsigned short insn |
| = read_code_unsigned_integer (pc + offset, 2, byte_order_for_code); |
| |
| /* Step 2: ldr Rd, [Rn, #immed], encoding T1. */ |
| if ((insn & 0xf800) != 0x6800) |
| return pc; |
| if (bits (insn, 3, 5) != basereg) |
| return pc; |
| destreg = bits (insn, 0, 2); |
| |
| insn = read_code_unsigned_integer (pc + offset + 2, 2, |
| byte_order_for_code); |
| /* Step 3: str Rd, [Rn, #immed], encoding T1. */ |
| if ((insn & 0xf800) != 0x6000) |
| return pc; |
| if (destreg != bits (insn, 0, 2)) |
| return pc; |
| } |
| else |
| { |
| unsigned int destreg; |
| unsigned int insn |
| = read_code_unsigned_integer (pc + offset, 4, byte_order_for_code); |
| |
| /* Step 2: ldr Rd, [Rn, #immed], encoding A1. */ |
| if ((insn & 0x0e500000) != 0x04100000) |
| return pc; |
| if (bits (insn, 16, 19) != basereg) |
| return pc; |
| destreg = bits (insn, 12, 15); |
| /* Step 3: str Rd, [Rn, #immed], encoding A1. */ |
| insn = read_code_unsigned_integer (pc + offset + 4, |
| 4, byte_order_for_code); |
| if ((insn & 0x0e500000) != 0x04000000) |
| return pc; |
| if (bits (insn, 12, 15) != destreg) |
| return pc; |
| } |
| /* The size of total two instructions ldr/str is 4 on Thumb-2, while 8 |
| on arm. */ |
| if (is_thumb) |
| return pc + offset + 4; |
| else |
| return pc + offset + 8; |
| } |
| |
| /* Advance the PC across any function entry prologue instructions to |
| reach some "real" code. |
| |
| The APCS (ARM Procedure Call Standard) defines the following |
| prologue: |
| |
| mov ip, sp |
| [stmfd sp!, {a1,a2,a3,a4}] |
| stmfd sp!, {...,fp,ip,lr,pc} |
| [stfe f7, [sp, #-12]!] |
| [stfe f6, [sp, #-12]!] |
| [stfe f5, [sp, #-12]!] |
| [stfe f4, [sp, #-12]!] |
| sub fp, ip, #nn @@ nn == 20 or 4 depending on second insn. */ |
| |
| static CORE_ADDR |
| arm_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| CORE_ADDR func_addr, func_end_addr, limit_pc; |
| |
| /* See if we can determine the end of the prologue via the symbol table. |
| If so, then return either PC, or the PC after the prologue, whichever |
| is greater. */ |
| bool func_addr_found |
| = find_pc_partial_function (pc, NULL, &func_addr, &func_end_addr); |
| |
| /* Whether the function is thumb mode or not. */ |
| bool func_is_thumb = false; |
| |
| if (func_addr_found) |
| { |
| CORE_ADDR post_prologue_pc |
| = skip_prologue_using_sal (gdbarch, func_addr); |
| struct compunit_symtab *cust = find_pc_compunit_symtab (func_addr); |
| |
| if (post_prologue_pc) |
| post_prologue_pc |
| = arm_skip_stack_protector (post_prologue_pc, gdbarch); |
| |
| |
| /* GCC always emits a line note before the prologue and another |
| one after, even if the two are at the same address or on the |
| same line. Take advantage of this so that we do not need to |
| know every instruction that might appear in the prologue. We |
| will have producer information for most binaries; if it is |
| missing (e.g. for -gstabs), assuming the GNU tools. */ |
| if (post_prologue_pc |
| && (cust == NULL |
| || cust->producer () == NULL |
| || startswith (cust->producer (), "GNU ") |
| || producer_is_llvm (cust->producer ()))) |
| return post_prologue_pc; |
| |
| if (post_prologue_pc != 0) |
| { |
| CORE_ADDR analyzed_limit; |
| |
| /* For non-GCC compilers, make sure the entire line is an |
| acceptable prologue; GDB will round this function's |
| return value up to the end of the following line so we |
| can not skip just part of a line (and we do not want to). |
| |
| RealView does not treat the prologue specially, but does |
| associate prologue code with the opening brace; so this |
| lets us skip the first line if we think it is the opening |
| brace. */ |
| func_is_thumb = arm_pc_is_thumb (gdbarch, func_addr); |
| if (func_is_thumb) |
| analyzed_limit = thumb_analyze_prologue (gdbarch, func_addr, |
| post_prologue_pc, NULL); |
| else |
| analyzed_limit |
| = arm_analyze_prologue (gdbarch, func_addr, post_prologue_pc, |
| NULL, target_arm_instruction_reader ()); |
| |
| if (analyzed_limit != post_prologue_pc) |
| return func_addr; |
| |
| return post_prologue_pc; |
| } |
| } |
| |
| /* Can't determine prologue from the symbol table, need to examine |
| instructions. */ |
| |
| /* Find an upper limit on the function prologue using the debug |
| information. If the debug information could not be used to provide |
| that bound, then use an arbitrary large number as the upper bound. */ |
| /* Like arm_scan_prologue, stop no later than pc + 64. */ |
| limit_pc = skip_prologue_using_sal (gdbarch, pc); |
| if (limit_pc == 0) |
| limit_pc = pc + 64; /* Magic. */ |
| |
| /* Set the correct adjustment based on whether the function is thumb mode or |
| not. We use it to get the address of the last instruction in the |
| function (as opposed to the first address of the next function). */ |
| CORE_ADDR adjustment = func_is_thumb ? 2 : 4; |
| |
| limit_pc |
| = func_end_addr == 0 ? limit_pc : std::min (limit_pc, |
| func_end_addr - adjustment); |
| |
| /* Check if this is Thumb code. */ |
| if (arm_pc_is_thumb (gdbarch, pc)) |
| return thumb_analyze_prologue (gdbarch, pc, limit_pc, NULL); |
| else |
| return arm_analyze_prologue (gdbarch, pc, limit_pc, NULL, |
| target_arm_instruction_reader ()); |
| } |
| |
| /* Function: thumb_scan_prologue (helper function for arm_scan_prologue) |
| This function decodes a Thumb function prologue to determine: |
| 1) the size of the stack frame |
| 2) which registers are saved on it |
| 3) the offsets of saved regs |
| 4) the offset from the stack pointer to the frame pointer |
| |
| A typical Thumb function prologue would create this stack frame |
| (offsets relative to FP) |
| old SP -> 24 stack parameters |
| 20 LR |
| 16 R7 |
| R7 -> 0 local variables (16 bytes) |
| SP -> -12 additional stack space (12 bytes) |
| The frame size would thus be 36 bytes, and the frame offset would be |
| 12 bytes. The frame register is R7. |
| |
| The comments for thumb_skip_prolog() describe the algorithm we use |
| to detect the end of the prolog. */ |
| |
| static void |
| thumb_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR prev_pc, |
| CORE_ADDR block_addr, struct arm_prologue_cache *cache) |
| { |
| CORE_ADDR prologue_start; |
| CORE_ADDR prologue_end; |
| |
| if (find_pc_partial_function (block_addr, NULL, &prologue_start, |
| &prologue_end)) |
| { |
| /* See comment in arm_scan_prologue for an explanation of |
| this heuristics. */ |
| if (prologue_end > prologue_start + 64) |
| { |
| prologue_end = prologue_start + 64; |
| } |
| } |
| else |
| /* We're in the boondocks: we have no idea where the start of the |
| function is. */ |
| return; |
| |
| prologue_end = std::min (prologue_end, prev_pc); |
| |
| thumb_analyze_prologue (gdbarch, prologue_start, prologue_end, cache); |
| } |
| |
| /* 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; |
| } |
| |
| /* Implement immediate value decoding, as described in section A5.2.4 |
| (Modified immediate constants in ARM instructions) of the ARM Architecture |
| Reference Manual (ARMv7-A and ARMv7-R edition). */ |
| |
| static uint32_t |
| arm_expand_immediate (uint32_t imm) |
| { |
| /* Immediate values are 12 bits long. */ |
| gdb_assert ((imm & 0xfffff000) == 0); |
| |
| uint32_t unrotated_value = imm & 0xff; |
| uint32_t rotate_amount = (imm & 0xf00) >> 7; |
| |
| if (rotate_amount == 0) |
| return unrotated_value; |
| |
| return ((unrotated_value >> rotate_amount) |
| | (unrotated_value << (32 - rotate_amount))); |
| } |
| |
| /* 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 |
| instruction. |
| |
| We recognize all the instructions typically found in ARM prologues, |
| plus harmless instructions which can be skipped (either for analysis |
| purposes, or a more restrictive set that can be skipped when finding |
| the end of the prologue). */ |
| |
| static CORE_ADDR |
| arm_analyze_prologue (struct gdbarch *gdbarch, |
| CORE_ADDR prologue_start, CORE_ADDR prologue_end, |
| struct arm_prologue_cache *cache, |
| const arm_instruction_reader &insn_reader) |
| { |
| enum bfd_endian byte_order_for_code = gdbarch_byte_order_for_code (gdbarch); |
| int regno; |
| CORE_ADDR offset, current_pc; |
| pv_t regs[ARM_FPS_REGNUM]; |
| CORE_ADDR unrecognized_pc = 0; |
| arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); |
| |
| /* Search the prologue looking for instructions that set up the |
| frame pointer, adjust the stack pointer, and save registers. |
| |
| Be careful, however, and if it doesn't look like a prologue, |
| don't try to scan it. If, for instance, a frameless function |
| begins with stmfd sp!, then we will tell ourselves there is |
| a frame, which will confuse stack traceback, as well as "finish" |
| and other operations that rely on a knowledge of the stack |
| traceback. */ |
| |
| for (regno = 0; regno < ARM_FPS_REGNUM; regno++) |
| regs[regno] = pv_register (regno, 0); |
| pv_area stack (ARM_SP_REGNUM, gdbarch_addr_bit (gdbarch)); |
| |
| for (current_pc = prologue_start; |
| current_pc < prologue_end; |
| current_pc += 4) |
| { |
| uint32_t insn = insn_reader.read (current_pc, byte_order_for_code); |
| |
| if (insn == 0xe1a0c00d) /* mov ip, sp */ |
| { |
| regs[ARM_IP_REGNUM] = regs[ARM_SP_REGNUM]; |
| continue; |
| } |
| else if ((insn & 0xfff00000) == 0xe2800000 /* add Rd, Rn, #n */ |
| && pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM)) |
| { |
| uint32_t imm = arm_expand_immediate (insn & 0xfff); |
| int rd = bits (insn, 12, 15); |
| regs[rd] = pv_add_constant (regs[bits (insn, 16, 19)], imm); |
| continue; |
| } |
| else if ((insn & 0xfff00000) == 0xe2400000 /* sub Rd, Rn, #n */ |
| && pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM)) |
| { |
| uint32_t imm = arm_expand_immediate (insn & 0xfff); |
| int rd = bits (insn, 12, 15); |
| regs[rd] = pv_add_constant (regs[bits (insn, 16, 19)], -imm); |
| continue; |
| } |
| else if ((insn & 0xffff0fff) == 0xe52d0004) /* str Rd, |
| [sp, #-4]! */ |
| { |
| if (stack.store_would_trash (regs[ARM_SP_REGNUM])) |
| break; |
| regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], -4); |
| stack.store (regs[ARM_SP_REGNUM], 4, |
| regs[bits (insn, 12, 15)]); |
| continue; |
| } |
| else if ((insn & 0xffff0000) == 0xe92d0000) |
| /* stmfd sp!, {..., fp, ip, lr, pc} |
| or |
| stmfd sp!, {a1, a2, a3, a4} */ |
| { |
| int mask = insn & 0xffff; |
| |
| if (stack.store_would_trash (regs[ARM_SP_REGNUM])) |
| break; |
| |
| /* Calculate offsets of saved registers. */ |
| for (regno = ARM_PC_REGNUM; regno >= 0; regno--) |
| if (mask & (1 << regno)) |
| { |
| regs[ARM_SP_REGNUM] |
| = pv_add_constant (regs[ARM_SP_REGNUM], -4); |
| stack.store (regs[ARM_SP_REGNUM], 4, regs[regno]); |
| } |
| } |
| else if ((insn & 0xffff0000) == 0xe54b0000 /* strb rx,[r11,#-n] */ |
| || (insn & 0xffff00f0) == 0xe14b00b0 /* strh rx,[r11,#-n] */ |
| || (insn & 0xffffc000) == 0xe50b0000) /* str rx,[r11,#-n] */ |
| { |
| /* No need to add this to saved_regs -- it's just an arg reg. */ |
| continue; |
| } |
| else if ((insn & 0xffff0000) == 0xe5cd0000 /* strb rx,[sp,#n] */ |
| || (insn & 0xffff00f0) == 0xe1cd00b0 /* strh rx,[sp,#n] */ |
| || (insn & 0xffffc000) == 0xe58d0000) /* str rx,[sp,#n] */ |
| { |
| /* No need to add this to saved_regs -- it's just an arg reg. */ |
| continue; |
| } |
| else if ((insn & 0xfff00000) == 0xe8800000 /* stm Rn, |
| { registers } */ |
| && pv_is_register (regs[bits (insn, 16, 19)], ARM_SP_REGNUM)) |
| { |
| /* No need to add this to saved_regs -- it's just arg regs. */ |
| continue; |
| } |
| else if ((insn & 0xfffff000) == 0xe24cb000) /* sub fp, ip #n */ |
| { |
| uint32_t imm = arm_expand_immediate (insn & 0xfff); |
| regs[ARM_FP_REGNUM] = pv_add_constant (regs[ARM_IP_REGNUM], -imm); |
| } |
| else if ((insn & 0xfffff000) == 0xe24dd000) /* sub sp, sp #n */ |
| { |
| uint32_t imm = arm_expand_immediate(insn & 0xfff); |
| regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], -imm); |
| } |
| else if ((insn & 0xffff7fff) == 0xed6d0103 /* stfe f?, |
| [sp, -#c]! */ |
| && tdep->have_fpa_registers) |
| { |
| if (stack.store_would_trash (regs[ARM_SP_REGNUM])) |
| break; |
| |
| regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], -12); |
| regno = ARM_F0_REGNUM + ((insn >> 12) & 0x07); |
| stack.store (regs[ARM_SP_REGNUM], 12, regs[regno]); |
| } |
| else if ((insn & 0xffbf0fff) == 0xec2d0200 /* sfmfd f0, 4, |
| [sp!] */ |
| && tdep->have_fpa_registers) |
| { |
| int n_saved_fp_regs; |
| unsigned int fp_start_reg, fp_bound_reg; |
| |
| if (stack.store_would_trash (regs[ARM_SP_REGNUM])) |
| break; |
| |
| if ((insn & 0x800) == 0x800) /* N0 is set */ |
| { |
| if ((insn & 0x40000) == 0x40000) /* N1 is set */ |
| n_saved_fp_regs = 3; |
| else |
| n_saved_fp_regs = 1; |
| } |
| else |
| { |
| if ((insn & 0x40000) == 0x40000) /* N1 is set */ |
| n_saved_fp_regs = 2; |
| else |
| n_saved_fp_regs = 4; |
| } |
| |
| fp_start_reg = ARM_F0_REGNUM + ((insn >> 12) & 0x7); |
| fp_bound_reg = fp_start_reg + n_saved_fp_regs; |
| for (; fp_start_reg < fp_bound_reg; fp_start_reg++) |
| { |
| regs[ARM_SP_REGNUM] = pv_add_constant (regs[ARM_SP_REGNUM], -12); |
| stack.store (regs[ARM_SP_REGNUM], 12, |
| regs[fp_start_reg++]); |
| } |
| } |
| else if ((insn & 0xff000000) == 0xeb000000 && cache == NULL) /* bl */ |
| { |
| /* Allow some special function calls when skipping the |
| prologue; GCC generates these before storing arguments to |
| the stack. */ |
| CORE_ADDR dest = BranchDest (current_pc, insn); |
| |
| if (skip_prologue_function (gdbarch, dest, 0)) |
| continue; |
| else |
| break; |
| } |
| else if ((insn & 0xf0000000) != 0xe0000000) |
| break; /* Condition not true, exit early. */ |
| else if (arm_instruction_changes_pc (insn)) |
| /* Don't scan past anything that might change control flow. */ |
| break; |
| else if (arm_instruction_restores_sp (insn)) |
| { |
| /* 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, 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; |
| if (cache != NULL) |
| continue; |
| else |
| break; |
| } |
| } |
| |
| if (unrecognized_pc == 0) |
| unrecognized_pc = current_pc; |
| |
| 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; |
| |
| for (regno = 0; regno < ARM_FPS_REGNUM; regno++) |
| if (stack.find_reg (gdbarch, regno, &offset)) |
| { |
| cache->saved_regs[regno].set_addr (offset); |
| if (regno == ARM_SP_REGNUM) |
| arm_cache_set_active_sp_value(cache, tdep, offset); |
| } |
| } |
| |
| arm_debug_printf ("Prologue scan stopped at %s", |
| paddress (gdbarch, unrecognized_pc)); |
| |
| return unrecognized_pc; |
| } |
| |
| static void |
| arm_scan_prologue (const frame_info_ptr &this_frame, |
| struct arm_prologue_cache *cache) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| CORE_ADDR prologue_start, prologue_end; |
| CORE_ADDR prev_pc = get_frame_pc (this_frame); |
| CORE_ADDR block_addr = get_frame_address_in_block (this_frame); |
| arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); |
| |
| /* Assume there is no frame until proven otherwise. */ |
| cache->framereg = ARM_SP_REGNUM; |
| cache->framesize = 0; |
| |
| /* Check for Thumb prologue. */ |
| if (arm_frame_is_thumb (this_frame)) |
| { |
| thumb_scan_prologue (gdbarch, prev_pc, block_addr, cache); |
| return; |
| } |
| |
| /* Find the function prologue. If we can't find the function in |
| the symbol table, peek in the stack frame to find the PC. */ |
| if (find_pc_partial_function (block_addr, NULL, &prologue_start, |
| &prologue_end)) |
| { |
| /* One way to find the end of the prologue (which works well |
| for unoptimized code) is to do the following: |
| |
| struct symtab_and_line sal = find_pc_line (prologue_start, 0); |
| |
| if (sal.line == 0) |
| prologue_end = prev_pc; |
| else if (sal.end < prologue_end) |
| prologue_end = sal.end; |
| |
| This mechanism is very accurate so long as the optimizer |
| doesn't move any instructions from the function body into the |
| prologue. If this happens, sal.end will be the last |
| instruction in the first hunk of prologue code just before |
| the first instruction that the scheduler has moved from |
| the body to the prologue. |
| |
| In order to make sure that we scan all of the prologue |
| instructions, we use a slightly less accurate mechanism which |
| may scan more than necessary. To help compensate for this |
| lack of accuracy, the prologue scanning loop below contains |
| several clauses which'll cause the loop to terminate early if |
| an implausible prologue instruction is encountered. |
| |
| The expression |
| |
| prologue_start + 64 |
| |
| is a suitable endpoint since it accounts for the largest |
| possible prologue plus up to five instructions inserted by |
| the scheduler. */ |
| |
| if (prologue_end > prologue_start + 64) |
| { |
| prologue_end = prologue_start + 64; /* See above. */ |
| } |
| } |
| else |
| { |
| /* We have no symbol information. Our only option is to assume this |
| function has a standard stack frame and the normal frame register. |
| Then, we can find the value of our frame pointer on entrance to |
| the callee (or at the present moment if this is the innermost frame). |
| The value stored there should be the address of the stmfd + 8. */ |
| CORE_ADDR frame_loc; |
| ULONGEST return_value; |
| |
| /* AAPCS does not use a frame register, so we can abort here. */ |
| if (tdep->arm_abi == ARM_ABI_AAPCS) |
| return; |
| |
| frame_loc = get_frame_register_unsigned (this_frame, ARM_FP_REGNUM); |
| if (!safe_read_memory_unsigned_integer (frame_loc, 4, byte_order, |
| &return_value)) |
| return; |
| else |
| { |
| prologue_start = gdbarch_addr_bits_remove |
| (gdbarch, return_value) - 8; |
| prologue_end = prologue_start + 64; /* See above. */ |
| } |
| } |
| |
| if (prev_pc < prologue_end) |
| prologue_end = prev_pc; |
| |
| arm_analyze_prologue (gdbarch, prologue_start, prologue_end, cache, |
| target_arm_instruction_reader ()); |
| } |
| |
| static struct arm_prologue_cache * |
| arm_make_prologue_cache (const frame_info_ptr &this_frame) |
| { |
| int reg; |
| struct arm_prologue_cache *cache; |
| CORE_ADDR unwound_fp, prev_sp; |
| |
| cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache); |
| arm_cache_init (cache, this_frame); |
| |
| arm_scan_prologue (this_frame, cache); |
| |
| unwound_fp = get_frame_register_unsigned (this_frame, cache->framereg); |
| if (unwound_fp == 0) |
| return cache; |
| |
| arm_gdbarch_tdep *tdep = |
| gdbarch_tdep<arm_gdbarch_tdep> (get_frame_arch (this_frame)); |
| |
| prev_sp = unwound_fp + cache->framesize; |
| arm_cache_set_active_sp_value (cache, tdep, prev_sp); |
| |
| /* Calculate actual addresses of saved registers using offsets |
| determined by arm_scan_prologue. */ |
| for (reg = 0; reg < gdbarch_num_regs (get_frame_arch (this_frame)); reg++) |
| if (cache->saved_regs[reg].is_addr ()) |
| cache->saved_regs[reg].set_addr (cache->saved_regs[reg].addr () + |
| prev_sp); |
| |
| return cache; |
| } |
| |
| /* Implementation of the stop_reason hook for arm_prologue frames. */ |
| |
| static enum unwind_stop_reason |
| arm_prologue_unwind_stop_reason (const frame_info_ptr &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 = (struct arm_prologue_cache *) *this_cache; |
| |
| /* This is meant to halt the backtrace at "_start". */ |
| pc = get_frame_pc (this_frame); |
| gdbarch *arch = get_frame_arch (this_frame); |
| arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (arch); |
| if (pc <= tdep->lowest_pc) |
| return UNWIND_OUTERMOST; |
| |
| /* If we've hit a wall, stop. */ |
| if (arm_cache_get_prev_sp_value (cache, tdep) == 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. */ |
| |
| static void |
| arm_prologue_this_id (const frame_info_ptr &this_frame, |
| void **this_cache, |
| struct frame_id *this_id) |
| { |
| struct arm_prologue_cache *cache; |
| struct frame_id id; |
| CORE_ADDR pc, func; |
| |
| if (*this_cache == NULL) |
| *this_cache = arm_make_prologue_cache (this_frame); |
| cache = (struct arm_prologue_cache *) *this_cache; |
| |
| arm_gdbarch_tdep *tdep |
| = gdbarch_tdep<arm_gdbarch_tdep> (get_frame_arch (this_frame)); |
| |
| /* 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; |
| |
| id = frame_id_build (arm_cache_get_prev_sp_value (cache, tdep), func); |
| *this_id = id; |
| } |
| |
| static struct value * |
| arm_prologue_prev_register (const frame_info_ptr &this_frame, |
| void **this_cache, |
| int prev_regnum) |
| { |
| struct gdbarch *gdbarch = get_frame_arch (this_frame); |
| struct arm_prologue_cache *cache; |
| CORE_ADDR sp_value; |
| |
| if (*this_cache == NULL) |
| *this_cache = arm_make_prologue_cache (this_frame); |
| cache = (struct arm_prologue_cache *) *this_cache; |
| |
| arm_gdbarch_tdep *tdep = gdbarch_tdep<arm_gdbarch_tdep> (gdbarch); |
| |
| /* If this frame has signed the return address, mark it as so. */ |
| if (tdep->have_pacbti && cache->ra_signed_state.has_value () |
| && *cache->ra_signed_state) |
| set_frame_previous_pc_masked (this_frame); |
| |
| /* If we are asked to unwind the PC, then we need to return the LR |
| instead. The prologue may save PC, but it will point into this |
| frame's prologue, not the next frame's resume location. Also |
| strip the saved T bit. A valid LR may have the low bit set, but |
| a valid PC never does. */ |
| if (prev_regnum == ARM_PC_REGNUM) |
| { |
| CORE_ADDR lr; |
| |
| lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM); |
| return frame_unwind_got_constant (this_frame, prev_regnum, |
| arm_addr_bits_remove (gdbarch, lr)); |
| } |
| |
| /* SP is generally not saved to the stack, but this frame is |
| identified by the next frame's stack pointer at the time of the call. |
| The value was already reconstructed into PREV_SP. */ |
| if (prev_regnum == ARM_SP_REGNUM) |
| return frame_unwind_got_constant (this_frame, prev_regnum, |
| arm_cache_get_prev_sp_value (cache, tdep)); |
| |
| /* The value might be one of the alternative SP, if so, use the |
| value already constructed. */ |
| if (arm_is_alternative_sp_register (tdep, prev_regnum)) |
| { |
| sp_value = arm_cache_get_sp_register (cache, tdep, prev_regnum); |
| return frame_unwind_got_constant (this_frame, prev_regnum, sp_value); |
| } |
| |
| /* The CPSR may have been changed by the call instruction and by the |
| called function. The only bit we can reconstruct is the T bit, |
| by checking the low bit of LR as of the call. This is a reliable |
| indicator of Thumb-ness except for some ARM v4T pre-interworking |
| Thumb code, which could get away with a clear low bit as long as |
| the called function did not use bx. Guess that all other |
| bits are unchanged; the condition flags are presumably lost, |
| but the processor status is likely valid. */ |
| if (prev_regnum == ARM_PS_REGNUM) |
| { |
| ULONGEST cpsr = get_frame_register_unsigned (this_frame, prev_regnum); |
| CORE_ADDR lr = frame_unwind_register_unsigned (this_frame, ARM_LR_REGNUM); |
| |
| cpsr = reconstruct_t_bit (gdbarch, lr, cpsr); |
| return frame_unwind_got_constant (this_frame, prev_regnum, cpsr); |
| } |
| |
| return trad_frame_get_prev_register (this_frame, cache->saved_regs, |
| prev_regnum); |
| } |
| |
| static frame_unwind arm_prologue_unwind = { |
| "arm prologue", |
| NORMAL_FRAME, |
| arm_prologue_unwind_stop_reason, |
| arm_prologue_this_id, |
| arm_prologue_prev_register, |
| NULL, |
| default_frame_sniffer |
| }; |
| |
| /* Maintain a list of ARM exception table entries per objfile, similar to the |
| list of mapping symbols. We only cache entries for standard ARM-defined |
| personality routines; the cache will contain only the frame unwinding |
| instructions associated with the entry (not the descriptors). */ |
| |
| struct arm_exidx_entry |
| { |
| CORE_ADDR addr; |
| gdb_byte *entry; |
| |
| bool operator< (const arm_exidx_entry &other) const |
| { |
| return addr < other.addr; |
| } |
| }; |
| |
| struct arm_exidx_data |
| { |
| std::vector<std::vector<arm_exidx_entry>> section_maps; |
| }; |
| |
| /* Per-BFD key to store exception handling information. */ |
| static const registry<bfd>::key<arm_exidx_data> arm_exidx_data_key; |
| |
| static struct obj_section * |
| arm_obj_section_from_vma (struct objfile *objfile, bfd_vma vma) |
| { |
| for (obj_section *osect : objfile->sections ()) |
| if (bfd_section_flags (osect->the_bfd_section) & SEC_ALLOC) |
| { |
| bfd_vma start, size; |
| start = bfd_section_vma (osect->the_bfd_section); |
| size = bfd_section_size (osect->the_bfd_section); |
| |
| if (start <= vma && vma < start + size) |
| return osect; |
| } |
| |
| return NULL; |
| } |
| |
| /* Parse contents of exception table and exception index sections |
| of OBJFILE, and fill in the exception table entry cache. |
| |
| For each entry that refers to a standard ARM-defined personality |
| routine, extract the frame unwinding instructions (from either |
| the index or the table section). The unwinding instructions |
| are normalized by: |
| - extracting them from the rest of the table data |
| - converting to host endianness |
| - appending the implicit 0xb0 ("Finish") code |
| |
| The extracted and normalized instructions are stored for later |
| retrieval by the arm_find_exidx_entry routine. */ |
| |
| static void |
| arm_exidx_new_objfile (struct objfile *objfile) |
| { |
| struct arm_exidx_data *data; |
| asection *exidx, *extab; |
| bfd_vma exidx_vma = 0, extab_vma = 0; |
| LONGEST i; |
| |
| /* If we've already touched this file, do nothing. */ |
| if (arm_exidx_data_key.get (objfile->obfd.get ()) != nullptr) |
| return; |
| |
| /* Read contents of exception table and index. */ |
| exidx = bfd_get_section_by_name (objfile->obfd.get (), |
| ELF_STRING_ARM_unwind); |
| gdb::byte_vector exidx_data; |
| if (exidx) |
| { |
| exidx_vma = bfd_section_vma (exidx); |
| exidx_data.resize (bfd_section_size (exidx)); |
| |
| if (!bfd_get_section_contents (objfile->obfd.get (), exidx, |
| exidx_data.data (), 0, |
| exidx_data.size ())) |
| return; |
| } |
| |
| extab = bfd_get_section_by_name (objfile->obfd.get (), ".ARM.extab"); |
| gdb::byte_vector extab_data; |
| if (extab) |
| { |
| extab_vma = bfd_section_vma (extab); |
| extab_data.resize (bfd_section_size (extab)); |
| |
| if (!bfd_get_section_contents (objfile->obfd.get (), extab, |
| extab_data.data (), 0, |
| extab_data.size ())) |
| return; |
| } |
| |
| /* Allocate exception table data structure. */ |
| data = arm_exidx_data_key.emplace (objfile->obfd.get ()); |
| data->section_maps.resize (objfile->obfd->section_count); |
| |
| /* Fill in exception table. */ |
| for (i = 0; i < exidx_data.size () / 8; i++) |
| { |
| struct arm_exidx_entry new_exidx_entry; |
| bfd_vma idx = bfd_h_get_32 (objfile->obfd, exidx_data.data () + i * 8); |
| bfd_vma val = bfd_h_get_32 (objfile->obfd, |
| exidx_data.data () + i * 8 + 4); |
| bfd_vma addr = 0, word = 0; |
| int n_bytes = 0, n_words = 0; |
| struct obj_section *sec; |
| gdb_byte *entry = NULL; |
| |
| /* Extract address of start of function. */ |
| idx = ((idx & 0x7fffffff) ^ 0x40000000) - 0x40000000; |
| idx += exidx_vma + i * 8; |
| |
| /* Find section containing function and compute section offset. */ |
| sec = arm_obj_section_from_vma (objfile, idx); |
| if (sec == NULL) |
| continue; |
| idx -= bfd_section_vma (sec->the_bfd_section); |
| |
| /* Determine address of exception table entry. */ |
| if (val == 1) |
| { |
| /* EXIDX_CANTUNWIND -- no exception table entry present. */ |
| } |
| else if ((val & 0xff000000) == 0x80000000) |
| { |
| /* Exception table entry embedded in .ARM.exidx |
| -- must be short form. */ |
| word = val; |
| n_bytes = 3; |
| } |
| else if (!(val & 0x80000000)) |
| { |
| /* Exception table entry in .ARM.extab. */ |
| addr = ((val & 0x7fffffff) ^ 0x40000000) - 0x40000000; |
| addr += exidx_vma + i * 8 + 4; |
| |
| if (addr >= extab_vma && addr + 4 <= extab_vma + extab_data.size ()) |
| { |
| word = bfd_h_get_32 (objfile->obfd, |
| extab_data.data () + addr - extab_vma); |
| addr += 4; |
| |
| if ((word & 0xff000000) == 0x80000000) |
| { |
| /* Short form. */ |
| n_bytes = 3; |
| } |
| else if ((word & 0xff000000) == 0x81000000 |
| || (word & 0xff000000) == 0x82000000) |
| { |
| /* Long form. */ |
| n_bytes = 2; |
| n_words = ((word >> 16) & 0xff); |
| } |
| else if (!(word & 0x80000000)) |
| { |
| bfd_vma pers; |
| struct obj_section *pers_sec; |
| int gnu_personality = 0; |
| |
| /* Custom personality routine. */ |
| pers = ((word & 0x7fffffff) ^ 0x40000000) - 0x40000000; |
| pers = UNMAKE_THUMB_ADDR (pers + addr - 4); |
| |
| /* Check whether we've got one of the variants of the |
| GNU personality routines. */ |
| pers_sec = arm_obj_section_from_vma (objfile, pers); |
| if (pers_sec) |
| { |
| static const char *personality[] = |
| { |
| "__gcc_personality_v0", |
| "__gxx_personality_v0", |
| "__gcj_personality_v0", |
| "__gnu_objc_personality_v0", |
| NULL |
| }; |
| |
| CORE_ADDR pc = pers + pers_sec->offset (); |
| int k; |
| |
| for (k = 0; personality[k]; k++) |
| if (lookup_minimal_symbol_by_pc_name |
| (pc, personality[k], objfile)) |
| { |
| gnu_personality = 1; |
| break; |
| } |
| } |
| |
| /* If so, the next word contains a word count in the high |
| byte, followed by the same unwind instructions as the |
| pre-defined forms. */ |
| if (gnu_personality |
| && addr + 4 <= extab_vma + extab_data.size ()) |
| { |
| word = bfd_h_get_32 (objfile->obfd, |
| (extab_data.data () |
| + addr - extab_vma)); |
| addr += 4; |
| n_bytes = 3; |
| n_words = ((word >> 24) & 0xff); |
| } |
| } |
| } |
| } |
| |
| /* Sanity check address. */ |
| if (n_words) |
| if (addr < extab_vma |
| || addr + 4 * n_words > extab_vma + extab_data.size ()) |
| n_words = n_bytes = 0; |
| |
| /* The unwind instructions reside in WORD (only the N_BYTES least |
| significant bytes are valid), followed by N_WORDS words in the |
| extab section starting at ADDR. */ |
| if (n_bytes || n_words) |
| { |
| gdb_byte *p = entry |
| = (gdb_byte *) obstack_alloc (&objfile->per_bfd->storage_obstack, |
| n_bytes + n_words * 4 + 1); |
| |
| while (n_bytes--) |
| *p++ = (gdb_byte) ((word >> (8 * n_bytes)) & 0xff); |
| |
| while (n_words--) |
| { |
| word = bfd_h_get_32 (objfile->obfd, |
| extab_data.data () + addr - extab_vma); |
| addr += 4; |
| |
| *p++ = (gdb_byte) ((word >> 24) & 0xff); |
| *p++ = (gdb_byte) ((word >> 16) & 0xff); |
| *p++ = (gdb_byte) ((word >> 8) & 0xff); |
| *p++ = (gdb_byte) (word & 0xff); |
| } |
| |
| /* Implied "Finish" to terminate the list. */ |
| *p++ = 0xb0; |
| } |
| |
| /* Push entry onto vector. They are guaranteed to always |
| appear in order of increasing addresses. */ |
| new_exidx_entry.addr = idx; |
| new_exidx_entry.entry = entry; |
| data->section_maps[sec->the_bfd_section->index].push_back |
| (new_exidx_entry); |
| } |
| } |
| |
| /* Search for the exception table entry covering MEMADDR. If one is found, |
| return a pointer to its data. Otherwise, return 0. If START is non-NULL, |
| set *START to the start of the region covered by this entry. */ |
| |
| static gdb_byte * |
| arm_find_exidx_entry (CORE_ADDR memaddr, CORE_ADDR *start) |
| { |
| struct obj_section *sec; |
| |
| sec = find_pc_section (memaddr); |
| if (sec != NULL) |
| { |
| struct arm_exidx_data *data; |
| struct arm_exidx_entry map_key = { memaddr - sec->addr (), 0 }; |
| |
| data = arm_exidx_data_key.get (sec->objfile->obfd.get ()); |
| if (data != NULL) |
| { |
| std::vector<arm_exidx_entry> &map |
| = data->section_maps[sec->the_bfd_section->index]; |
| if (!map.empty ()) |
| { |
| auto idx = std::lower_bound (map.begin (), map.end (), map_key); |
| |
| /* std::lower_bound finds the earliest ordered insertion |
| point. If the following symbol starts at this exact |
| address, we use that; otherwise, the preceding |
| exception table entry covers this address. */ |
| if (idx < map.end ()) |
| { |
| if (idx->addr == map_key.addr) |
| { |
| if (start) |
| *start = idx->addr + sec->addr (); |
| return idx->entry; |
| } |
| } |
| |
| if (idx > map.begin ()) |
| { |
| idx = idx - 1; |
| if (start) |
| *start = idx->addr + sec->addr (); |
| return idx->entry; |
| } |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /* Given the current frame THIS_FRAME, and its associated frame unwinding |
| instruction list from the ARM exception table entry ENTRY, allocate and |
| return a prologue cache structure describing how to unwind this frame. |
| |
| Return NULL if the unwinding instruction list contains a "spare", |
| "reserved" or "refuse to unwind" instruction as defined in section |
| "9.3 Frame unwinding instructions" of the "Exception Handling ABI |
| for the ARM Architecture" document. */ |
| |
| static struct arm_prologue_cache * |
| arm_exidx_fill_cache (const frame_info_ptr &this_frame, gdb_byte *entry) |
| { |
| CORE_ADDR vsp = 0; |
| int vsp_valid = 0; |
| |
| struct arm_prologue_cache *cache; |
| cache = FRAME_OBSTACK_ZALLOC (struct arm_prologue_cache); |
| arm_cache_init (cache, this_frame); |
| |
| for (;;) |
| { |
| gdb_byte insn; |
| |
| /* Whenever we reload SP, we actually have to retrieve its |
| actual value in the current frame. */ |
| if (!vsp_valid) |
| { |
| if (cache->saved_regs[ARM_SP_REGNUM].is_realreg ()) |
| { |
| int reg = cache->saved_regs[ARM_SP_REGNUM].realreg (); |
| vsp = get_frame_register_unsigned (this_frame, reg); |
| } |
| else |
| { |
| CORE_ADDR addr = cache->saved_regs[ARM_SP_REGNUM].addr (); |
| vsp = get_frame_memory_unsigned (this_frame, addr, 4); |
| } |
| |
| vsp_valid = 1; |
| } |
| |
| /* Decode next unwind instruction. */ |
| insn = *entry++; |
| |
| if ((insn & 0xc0) == 0) |
| { |
| int offset = insn & 0x3f; |
| vsp += (offset << 2) + 4; |
| } |
| else if ((insn & 0xc0) == 0x40) |
| { |
| int offset = insn & 0x3f; |
| vsp -= (offset << 2) + 4; |
| } |
| else if ((insn & 0xf0) == 0x80) |
| { |
| int mask = ((insn & 0xf) << 8) | *entry++; |
| int i; |
| |
| /* The special case of an all-zero mask identifies |
| "Refuse to unwind". We return NULL to fall back |
| to the prologue analyzer. */ |
| if (mask == 0) |
| return NULL; |
| |
| /* Pop registers r4..r15 under mask. */ |
| for (i = 0; i < 12; i++) |
| if (mask & (1 << i)) |
| { |
| cache->saved_regs[4 + i].set_addr (vsp); |
| vsp += 4; |
| } |
| |
| /* Special-case popping SP -- we need to reload vsp. */ |
| if (mask & (1 << (ARM_SP_REGNUM - 4))) |
| vsp_valid = 0; |
| } |
| else if ((insn & 0xf0) == 0x90) |
| { |
| int reg = insn & 0xf; |
| |
| /* Reserved cases. */ |
| if (reg == ARM_SP_REGNUM || reg == ARM_PC_REGNUM) |
| return NULL; |
| |
| /* Set SP from another register and mark VSP for reload. */ |
| cache->saved_regs[ARM_SP_REGNUM] = cache->saved_regs[reg]; |
| vsp_valid = 0; |
| } |
| else if ((insn & 0xf0) == 0xa0) |
| { |
| int count = insn & 0x7; |
| int pop_lr = (insn & 0x8) != 0; |
| int i; |
| |
| /* Pop r4..r[4+count]. */ |
| for (i = 0; i <= count; i++) |
| { |
| cache->saved_regs[4 + i].set_addr (vsp); |
| vsp += 4; |
| } |
| |
| /* If indicated by flag, pop LR as well. */ |
| if (pop_lr) |
| { |
| cache->saved_regs[ARM_LR_REGNUM].set_addr (vsp); |
| vsp += 4; |
| } |
| } |
| else if (insn == 0xb0) |
| { |
| /* We could only have updated PC by popping into it; if so, it |
| will show up as address. Otherwise, copy LR into PC. */ |
| if (!cache->saved_regs[ARM_PC_REGNUM].is_addr ()) |
| cache->saved_regs[ARM_PC_REGNUM] |
| = cache->saved_regs[ARM_LR_REGNUM]; |
| |
| /* We're done. */ |
| break; |
| } |
| else if (insn == 0xb1) |
| { |
| int mask = *entry++; |
| int i; |
| |
| /* All-zero mask and mask >= 16 is "spare". */ |
| if (mask == 0 || mask >= 16) |
| return NULL; |
| |
| /* Pop r0..r3 under mask. */ |
| for (i = 0; i < 4; i++) |
| if (mask & (1 << i)) |
| { |
| cache->saved_regs[i].set_addr (vsp); |
| vsp += 4; |
| } |
| } |
| else if (insn == 0xb2) |
| { |
| ULONGEST offset = 0; |
| unsigned shift = 0; |
| |
| do |
| { |
| offset |= (*entry & 0x7f) << shift; |
| shift += 7; |
| } |
| while (*entry++ & 0x80); |
| |
| vsp += 0x204 + (offset << 2); |
| } |
| else if (insn == 0xb3) |
| { |
| int start = *entry >> 4; |
| int count = (*entry++) & 0xf; |
| int i; |
| |
| /* Only registers D0..D15 are valid here. */ |
| if (start + count >= 16) |
| return NULL; |
| |
| /* Pop VFP double-precision registers D[start]..D[start+count]. */ |
| for (i = 0; i <= count; i++) |
| { |
| cache->saved_regs[ARM_D0_REGNUM + start + i].set_addr (vsp); |
| vsp += 8; |
| } |
| |
| /* Add an extra 4 bytes for FSTMFDX-style stack. */ |
| vsp += 4; |
| } |
| else if ((insn & 0xf8) == 0xb8) |
| { |
| int count = insn & 0x7; |
| int i; |
| |
| /* Pop VFP double-precision registers D[8]..D[8+count]. */ |
| for (i = 0; i <= count; i++) |
| { |
| cache->saved_regs[ARM_D0_REGNUM + 8 + i].set_addr (vsp); |
| vsp += 8; |
| } |
| |
| /* Add an extra 4 bytes for FSTMFDX-style stack. */ |
| vsp += 4; |
| } |
| else if (insn == 0xc6) |
| { |
| int start = *entry >> 4; |
| int count = (*entry++) & 0xf; |
| int i; |
| |
| /* Only registers WR0..WR15 are valid. */ |
| if (start + count >= 16) |
| return NULL; |
| |
| /* Pop iwmmx registers WR[start]..WR[start+count]. */ |
| for (i = 0; i <= count; i++) |
| { |
| cache->saved_regs[ARM_WR0_REGNUM + start + i].set_addr (vsp); |
| vsp += 8; |
| } |
| } |
| else if (insn == 0xc7) |
| { |
| int mask = *entry++; |
| int i; |
| |
| /* All-zero mask and mask >= 16 is "spare". */ |
| if (mask == 0 || mask >= 16) |
| return NULL; |
| |
| /* Pop iwmmx general-purpose registers WCGR0..WCGR3 under mask. */ |
| for (i = 0; i < 4; i++) |
| if (mask & (1 << i)) |
| { |
| cache->saved_regs[ARM_WCGR0_REGNUM + i].set_addr (vsp); |
| vsp += 4; |
| } |
| } |
| else if ((insn & 0xf8) == 0xc0) |
| { |
| int count = insn & 0x7; |
| int i; |
| |
| /* Pop iwmmx registers WR[10]..WR[10+count]. */ |
| for (i = 0; i <= count; i++) |
| { |
| cache->saved_regs[ARM_WR0_REGNUM + 10 + i].set_addr (vsp); |
| vsp += 8; |
| } |
| } |
| else if (insn == 0xc8) |
| { |
| int start = *entry >> 4; |
| int count = (*entry++) & 0xf; |
| int i; |
| |
| /* Only registers D0..D31 are valid. */ |
| if (start + count >= 16) |
| return NULL; |
| |
| /* Pop VFP double-precision registers |
| D[16+start]..D[16+start+count]. */ |
| for (i = 0; i <= count; i++) |
| |