/* Common target dependent code for GDB on ARM systems.
- Copyright (C) 1988-2019 Free Software Foundation, Inc.
+ Copyright (C) 1988-2020 Free Software Foundation, Inc.
This file is part of GDB.
#include "coff/internal.h"
#include "elf/arm.h"
-#include "common/vec.h"
-
#include "record.h"
#include "record-full.h"
#include <algorithm>
-#include "features/arm/arm-with-m.c"
-#include "features/arm/arm-with-m-fpa-layout.c"
-#include "features/arm/arm-with-m-vfp-d16.c"
-#include "features/arm/arm-with-iwmmxt.c"
-#include "features/arm/arm-with-vfpv2.c"
-#include "features/arm/arm-with-vfpv3.c"
-#include "features/arm/arm-with-neon.c"
-
#if GDB_SELF_TEST
-#include "common/selftest.h"
+#include "gdbsupport/selftest.h"
#endif
-static int arm_debug;
+static bool arm_debug;
/* 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
typedef std::vector<arm_mapping_symbol> arm_mapping_symbol_vec;
-struct arm_per_objfile
+struct arm_per_bfd
{
- explicit arm_per_objfile (size_t num_sections)
+ 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_objfile);
+ DISABLE_COPY_AND_ASSIGN (arm_per_bfd);
/* Information about mapping symbols ($a, $d, $t) in the objfile.
std::unique_ptr<bool[]> section_maps_sorted;
};
-/* Per-objfile data used for mapping symbols. */
-static objfile_key<arm_per_objfile> arm_objfile_data_key;
+/* Per-bfd data used for mapping symbols. */
+static 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;
/* 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];
+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,
#define DISPLACED_STEPPING_ARCH_VERSION 5
-/* Set to true if the 32-bit mode is in use. */
+/* See arm-tdep.h. */
-int arm_apcs_32 = 1;
+bool arm_apcs_32 = true;
/* Return the bit mask in ARM_PS_REGNUM that indicates Thumb mode. */
sec = find_pc_section (memaddr);
if (sec != NULL)
{
- arm_per_objfile *data = arm_objfile_data_key.get (sec->objfile);
+ arm_per_bfd *data = arm_bfd_data_key.get (sec->objfile->obfd);
if (data != NULL)
{
unsigned int section_idx = sec->the_bfd_section->index;
msym = lookup_minimal_symbol_by_pc (pc);
if (msym.minsym != NULL
&& BMSYMBOL_VALUE_ADDRESS (msym) == pc
- && MSYMBOL_LINKAGE_NAME (msym.minsym) != NULL)
+ && msym.minsym->linkage_name () != NULL)
{
- const char *name = MSYMBOL_LINKAGE_NAME (msym.minsym);
+ const char *name = msym.minsym->linkage_name ();
/* The GNU linker's Thumb call stub to foo is named
__foo_from_thumb. */
/* 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 (MSYMBOL_LINKAGE_NAME (stack_chk_guard.minsym), "__stack_chk_guard"))
+ || !startswith (stack_chk_guard.minsym->linkage_name (), "__stack_chk_guard"))
return pc;
if (is_thumb)
personality routines; the cache will contain only the frame unwinding
instructions associated with the entry (not the descriptors). */
-static const struct objfile_data *arm_exidx_data_key;
-
struct arm_exidx_entry
{
bfd_vma addr;
gdb_byte *entry;
+
+ bool operator< (const arm_exidx_entry &other) const
+ {
+ return addr < other.addr;
+ }
};
-typedef struct arm_exidx_entry arm_exidx_entry_s;
-DEF_VEC_O(arm_exidx_entry_s);
struct arm_exidx_data
{
- VEC(arm_exidx_entry_s) **section_maps;
+ std::vector<std::vector<arm_exidx_entry>> section_maps;
};
-static void
-arm_exidx_data_free (struct objfile *objfile, void *arg)
-{
- struct arm_exidx_data *data = (struct arm_exidx_data *) arg;
- unsigned int i;
-
- for (i = 0; i < objfile->obfd->section_count; i++)
- VEC_free (arm_exidx_entry_s, data->section_maps[i]);
-}
-
-static inline int
-arm_compare_exidx_entries (const struct arm_exidx_entry *lhs,
- const struct arm_exidx_entry *rhs)
-{
- return lhs->addr < rhs->addr;
-}
+/* Per-BFD key to store exception handling information. */
+static const struct bfd_key<arm_exidx_data> arm_exidx_data_key;
static struct obj_section *
arm_obj_section_from_vma (struct objfile *objfile, bfd_vma vma)
struct obj_section *osect;
ALL_OBJFILE_OSECTIONS (objfile, osect)
- if (bfd_get_section_flags (objfile->obfd,
- osect->the_bfd_section) & SEC_ALLOC)
+ if (bfd_section_flags (osect->the_bfd_section) & SEC_ALLOC)
{
bfd_vma start, size;
- start = bfd_get_section_vma (objfile->obfd, osect->the_bfd_section);
- size = bfd_get_section_size (osect->the_bfd_section);
+ start = bfd_section_vma (osect->the_bfd_section);
+ size = bfd_section_size (osect->the_bfd_section);
if (start <= vma && vma < start + size)
return osect;
LONGEST i;
/* If we've already touched this file, do nothing. */
- if (!objfile || objfile_data (objfile, arm_exidx_data_key) != NULL)
+ if (!objfile || arm_exidx_data_key.get (objfile->obfd) != NULL)
return;
/* Read contents of exception table and index. */
gdb::byte_vector exidx_data;
if (exidx)
{
- exidx_vma = bfd_section_vma (objfile->obfd, exidx);
- exidx_data.resize (bfd_get_section_size (exidx));
+ exidx_vma = bfd_section_vma (exidx);
+ exidx_data.resize (bfd_section_size (exidx));
if (!bfd_get_section_contents (objfile->obfd, exidx,
exidx_data.data (), 0,
gdb::byte_vector extab_data;
if (extab)
{
- extab_vma = bfd_section_vma (objfile->obfd, extab);
- extab_data.resize (bfd_get_section_size (extab));
+ extab_vma = bfd_section_vma (extab);
+ extab_data.resize (bfd_section_size (extab));
if (!bfd_get_section_contents (objfile->obfd, extab,
extab_data.data (), 0,
}
/* Allocate exception table data structure. */
- data = OBSTACK_ZALLOC (&objfile->objfile_obstack, struct arm_exidx_data);
- set_objfile_data (objfile, arm_exidx_data_key, data);
- data->section_maps = OBSTACK_CALLOC (&objfile->objfile_obstack,
- objfile->obfd->section_count,
- VEC(arm_exidx_entry_s) *);
+ data = arm_exidx_data_key.emplace (objfile->obfd);
+ data->section_maps.resize (objfile->obfd->section_count);
/* Fill in exception table. */
for (i = 0; i < exidx_data.size () / 8; i++)
sec = arm_obj_section_from_vma (objfile, idx);
if (sec == NULL)
continue;
- idx -= bfd_get_section_vma (objfile->obfd, sec->the_bfd_section);
+ idx -= bfd_section_vma (sec->the_bfd_section);
/* Determine address of exception table entry. */
if (val == 1)
appear in order of increasing addresses. */
new_exidx_entry.addr = idx;
new_exidx_entry.entry = entry;
- VEC_safe_push (arm_exidx_entry_s,
- data->section_maps[sec->the_bfd_section->index],
- &new_exidx_entry);
+ data->section_maps[sec->the_bfd_section->index].push_back
+ (new_exidx_entry);
}
}
if (sec != NULL)
{
struct arm_exidx_data *data;
- VEC(arm_exidx_entry_s) *map;
struct arm_exidx_entry map_key = { memaddr - obj_section_addr (sec), 0 };
- unsigned int idx;
- data = ((struct arm_exidx_data *)
- objfile_data (sec->objfile, arm_exidx_data_key));
+ data = arm_exidx_data_key.get (sec->objfile->obfd);
if (data != NULL)
{
- map = data->section_maps[sec->the_bfd_section->index];
- if (!VEC_empty (arm_exidx_entry_s, map))
+ std::vector<arm_exidx_entry> &map
+ = data->section_maps[sec->the_bfd_section->index];
+ if (!map.empty ())
{
- struct arm_exidx_entry *map_sym;
+ auto idx = std::lower_bound (map.begin (), map.end (), map_key);
- idx = VEC_lower_bound (arm_exidx_entry_s, map, &map_key,
- arm_compare_exidx_entries);
-
- /* VEC_lower_bound finds the earliest ordered insertion
+ /* 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 < VEC_length (arm_exidx_entry_s, map))
+ if (idx < map.end ())
{
- map_sym = VEC_index (arm_exidx_entry_s, map, idx);
- if (map_sym->addr == map_key.addr)
+ if (idx->addr == map_key.addr)
{
if (start)
- *start = map_sym->addr + obj_section_addr (sec);
- return map_sym->entry;
+ *start = idx->addr + obj_section_addr (sec);
+ return idx->entry;
}
}
- if (idx > 0)
+ if (idx > map.begin ())
{
- map_sym = VEC_index (arm_exidx_entry_s, map, idx - 1);
+ idx = idx - 1;
if (start)
- *start = map_sym->addr + obj_section_addr (sec);
- return map_sym->entry;
+ *start = idx->addr + obj_section_addr (sec);
+ return idx->entry;
}
}
}
}
}
- /* Push stack padding for dowubleword alignment. */
+ /* Push stack padding for doubleword alignment. */
if (nstack & (align - 1))
{
si = push_stack_item (si, val, ARM_INT_REGISTER_SIZE);
if (dsc->u.branch.link)
{
/* The value of LR should be the next insn of current one. In order
- not to confuse logic hanlding later insn `bx lr', if current insn mode
+ not to confuse logic handling later insn `bx lr', if current insn mode
is Thumb, the bit 0 of LR value should be set to 1. */
ULONGEST next_insn_addr = dsc->insn_addr + dsc->insn_size;
Before this sequence of instructions:
r0 is the PC value got from displaced_read_reg, so r0 = from + 8;
- r2 is the Rn value got from dispalced_read_reg.
+ r2 is the Rn value got from displaced_read_reg.
Insn1: push {pc} Write address of STR instruction + offset on stack
Insn2: pop {r4} Read it back from stack, r4 = addr(Insn1) + offset
}
-/* Common copy routine for svc instruciton. */
+/* Common copy routine for svc instruction. */
static int
install_svc (struct gdbarch *gdbarch, struct regcache *regs,
if (bit_4 == 0) /* STC/STC2. */
return thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
"stc/stc2", dsc);
- else /* LDC/LDC2 {literal, immeidate}. */
+ else /* LDC/LDC2 {literal, immediate}. */
return thumb2_copy_copro_load_store (gdbarch, insn1, insn2,
regs, dsc);
}
return 0;
}
-/* Copy Thumb cbnz/cbz insruction. */
+/* Copy Thumb cbnz/cbz instruction. */
static int
thumb_copy_cbnz_cbz (struct gdbarch *gdbarch, uint16_t insn1,
case 0:
if (bit (insn1, 6))
{
- /* Load/store {dual, execlusive}, table branch. */
+ /* Load/store {dual, exclusive}, table branch. */
if (bits (insn1, 7, 8) == 1 && bits (insn1, 4, 5) == 1
&& bits (insn2, 5, 7) == 0)
err = thumb2_copy_table_branch (gdbarch, insn1, insn2, regs,
err = thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
"dp/pb", dsc);
}
- else /* Data processing (modified immeidate) */
+ else /* Data processing (modified immediate) */
err = thumb_copy_unmodified_32bit (gdbarch, insn1, insn2,
"dp/mi", dsc);
}
*pc = extract_unsigned_integer (buf, ARM_INT_REGISTER_SIZE, byte_order);
return 1;
}
+/* A call to cmse secure entry function "foo" at "a" is modified by
+ GNU ld as "b".
+ a) bl xxxx <foo>
+
+ <foo>
+ xxxx:
+
+ b) bl yyyy <__acle_se_foo>
+
+ section .gnu.sgstubs:
+ <foo>
+ yyyy: sg // secure gateway
+ b.w xxxx <__acle_se_foo> // original_branch_dest
+
+ <__acle_se_foo>
+ xxxx:
+
+ When the control at "b", the pc contains "yyyy" (sg address) which is a
+ trampoline and does not exist in source code. This function returns the
+ target pc "xxxx". For more details please refer to section 5.4
+ (Entry functions) and section 3.4.4 (C level development flow of secure code)
+ of "armv8-m-security-extensions-requirements-on-development-tools-engineering-specification"
+ document on www.developer.arm.com. */
+
+static CORE_ADDR
+arm_skip_cmse_entry (CORE_ADDR pc, const char *name, struct objfile *objfile)
+{
+ int target_len = strlen (name) + strlen ("__acle_se_") + 1;
+ char *target_name = (char *) alloca (target_len);
+ xsnprintf (target_name, target_len, "%s%s", "__acle_se_", name);
+
+ struct bound_minimal_symbol minsym
+ = lookup_minimal_symbol (target_name, NULL, objfile);
+
+ if (minsym.minsym != nullptr)
+ return BMSYMBOL_VALUE_ADDRESS (minsym);
+
+ return 0;
+}
+
+/* Return true when SEC points to ".gnu.sgstubs" section. */
+
+static bool
+arm_is_sgstubs_section (struct obj_section *sec)
+{
+ return (sec != nullptr
+ && sec->the_bfd_section != nullptr
+ && sec->the_bfd_section->name != nullptr
+ && streq (sec->the_bfd_section->name, ".gnu.sgstubs"));
+}
/* Recognize GCC and GNU ld's trampolines. If we are in a trampoline,
return the target PC. Otherwise return 0. */
return 0;
}
+ struct obj_section *section = find_pc_section (pc);
+
+ /* Check whether SECTION points to the ".gnu.sgstubs" section. */
+ if (arm_is_sgstubs_section (section))
+ return arm_skip_cmse_entry (pc, name, section->objfile);
+
return 0; /* not a stub */
}
asymbol *sym)
{
const char *name = bfd_asymbol_name (sym);
- struct arm_per_objfile *data;
+ struct arm_per_bfd *data;
struct arm_mapping_symbol new_map_sym;
gdb_assert (name[0] == '$');
if (name[1] != 'a' && name[1] != 't' && name[1] != 'd')
return;
- data = arm_objfile_data_key.get (objfile);
+ data = arm_bfd_data_key.get (objfile->obfd);
if (data == NULL)
- data = arm_objfile_data_key.emplace (objfile,
- objfile->obfd->section_count);
+ data = arm_bfd_data_key.emplace (objfile->obfd,
+ objfile->obfd->section_count);
arm_mapping_symbol_vec &map
- = data->section_maps[bfd_get_section (sym)->index];
+ = data->section_maps[bfd_asymbol_section (sym)->index];
new_map_sym.value = sym->value;
new_map_sym.type = name[1];
return default_register_reggroup_p (gdbarch, regnum, group);
}
-\f
/* For backward-compatibility we allow two 'g' packet lengths with
the remote protocol depending on whether FPA registers are
supplied. M-profile targets do not have FPA registers, but some
{
if (gdbarch_tdep (gdbarch)->is_m)
{
+ const target_desc *tdesc;
+
/* If we know from the executable this is an M-profile target,
cater for remote targets whose register set layout is the
same as the FPA layout. */
+ tdesc = arm_read_mprofile_description (ARM_M_TYPE_WITH_FPA);
register_remote_g_packet_guess (gdbarch,
ARM_CORE_REGS_SIZE + ARM_FP_REGS_SIZE,
- tdesc_arm_with_m_fpa_layout);
+ tdesc);
/* The regular M-profile layout. */
+ tdesc = arm_read_mprofile_description (ARM_M_TYPE_M_PROFILE);
register_remote_g_packet_guess (gdbarch, ARM_CORE_REGS_SIZE,
- tdesc_arm_with_m);
+ tdesc);
/* M-profile plus M4F VFP. */
+ tdesc = arm_read_mprofile_description (ARM_M_TYPE_VFP_D16);
register_remote_g_packet_guess (gdbarch,
ARM_CORE_REGS_SIZE + ARM_VFP2_REGS_SIZE,
- tdesc_arm_with_m_vfp_d16);
+ tdesc);
}
/* Otherwise we don't have a useful guess. */
}
/* Add standard register aliases. We add aliases even for those
- nanes which are used by the current architecture - it's simpler,
+ names which are used by the current architecture - it's simpler,
and does no harm, since nothing ever lists user registers. */
for (i = 0; i < ARRAY_SIZE (arm_register_aliases); i++)
user_reg_add (gdbarch, arm_register_aliases[i].name,
/* Add ourselves to objfile event chain. */
gdb::observers::new_objfile.attach (arm_exidx_new_objfile);
- arm_exidx_data_key
- = register_objfile_data_with_cleanup (NULL, arm_exidx_data_free);
/* Register an ELF OS ABI sniffer for ARM binaries. */
gdbarch_register_osabi_sniffer (bfd_arch_arm,
bfd_target_elf_flavour,
arm_elf_osabi_sniffer);
- /* Initialize the standard target descriptions. */
- initialize_tdesc_arm_with_m ();
- initialize_tdesc_arm_with_m_fpa_layout ();
- initialize_tdesc_arm_with_m_vfp_d16 ();
- initialize_tdesc_arm_with_iwmmxt ();
- initialize_tdesc_arm_with_vfpv2 ();
- initialize_tdesc_arm_with_vfpv3 ();
- initialize_tdesc_arm_with_neon ();
-
/* Add root prefix command for all "set arm"/"show arm" commands. */
add_prefix_cmd ("arm", no_class, set_arm_command,
_("Various ARM-specific commands."),
{
reg_dest = bits (arm_insn_r->arm_insn, 12, 15);
/* LDR insn has a capability to do branching, if
- MOV LR, PC is precedded by LDR insn having Rn as R15
+ MOV LR, PC is preceded by LDR insn having Rn as R15
in that case, it emulates branch and link insn, and hence we
need to save CSPR and PC as well. */
if (15 != reg_dest)
} // namespace
/* Extracts arm/thumb/thumb2 insn depending on the size, and returns 0 on success
-and positive val on fauilure. */
+and positive val on failure. */
static int
extract_arm_insn (abstract_memory_reader& reader,
return ret;
}
+
+/* See arm-tdep.h. */
+
+const target_desc *
+arm_read_description (arm_fp_type fp_type)
+{
+ struct target_desc *tdesc = tdesc_arm_list[fp_type];
+
+ if (tdesc == nullptr)
+ {
+ tdesc = arm_create_target_description (fp_type);
+ tdesc_arm_list[fp_type] = tdesc;
+ }
+
+ return tdesc;
+}
+
+/* See arm-tdep.h. */
+
+const target_desc *
+arm_read_mprofile_description (arm_m_profile_type m_type)
+{
+ struct target_desc *tdesc = tdesc_arm_mprofile_list[m_type];
+
+ if (tdesc == nullptr)
+ {
+ tdesc = arm_create_mprofile_target_description (m_type);
+ tdesc_arm_mprofile_list[m_type] = tdesc;
+ }
+
+ return tdesc;
+}