-/* Target dependent code for ARC arhitecture, for GDB.
+/* Target dependent code for ARC architecture, for GDB.
- Copyright 2005-2017 Free Software Foundation, Inc.
+ Copyright 2005-2020 Free Software Foundation, Inc.
Contributed by Synopsys Inc.
This file is part of GDB.
#include "defs.h"
#include "arch-utils.h"
#include "disasm.h"
-#include "dwarf2-frame.h"
+#include "dwarf2/frame.h"
#include "frame-base.h"
#include "frame-unwind.h"
#include "gdbcore.h"
#include "gdbcmd.h"
#include "objfiles.h"
+#include "osabi.h"
#include "prologue-value.h"
+#include "target-descriptions.h"
#include "trad-frame.h"
/* ARC header files. */
#include "opcode/arc.h"
#include "opcodes/arc-dis.h"
#include "arc-tdep.h"
+#include "arch/arc.h"
/* Standard headers. */
#include <algorithm>
-/* Default target descriptions. */
-#include "features/arc-v2.c"
-#include "features/arc-arcompact.c"
-
/* The frame unwind cache for ARC. */
struct arc_frame_cache
"lp_count", "reserved", "limm", "pcl",
};
+static char *arc_disassembler_options = NULL;
+
+/* Possible arc target descriptors. */
+static struct target_desc *tdesc_arc_list[ARC_SYS_TYPE_NUM];
+
/* Functions are sorted in the order as they are used in the
_initialize_arc_tdep (), which uses the same order as gdbarch.h. Static
functions are defined before the first invocation. */
/* Get register with base address of memory operation. */
-int
+static int
arc_insn_get_memory_base_reg (const struct arc_instruction &insn)
{
/* POP_S and PUSH_S have SP as an implicit argument in a disassembler. */
/* Get offset of a memory operation INSN. */
-CORE_ADDR
+static CORE_ADDR
arc_insn_get_memory_offset (const struct arc_instruction &insn)
{
/* POP_S and PUSH_S have offset as an implicit argument in a
/* Dump INSN into gdb_stdlog. */
-void
+static void
arc_insn_dump (const struct arc_instruction &insn)
{
struct gdbarch *gdbarch = target_gdbarch ();
static void
arc_write_pc (struct regcache *regcache, CORE_ADDR new_pc)
{
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch *gdbarch = regcache->arch ();
if (arc_debug)
debug_printf ("arc: Writing PC, new value=%s\n",
*offset_ptr = 0;
}
-/* Implement the "dummy_id" gdbarch method.
-
- Tear down a dummy frame created by arc_push_dummy_call (). This data has
- to be constructed manually from the data in our hand. The stack pointer
- and program counter can be obtained from the frame info. */
-
-static struct frame_id
-arc_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
-{
- return frame_id_build (get_frame_sp (this_frame),
- get_frame_pc (this_frame));
-}
-
/* Implement the "push_dummy_call" gdbarch method.
Stack Frame Layout
static CORE_ADDR
arc_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
struct regcache *regcache, CORE_ADDR bp_addr, int nargs,
- struct value **args, CORE_ADDR sp, int struct_return,
+ struct value **args, CORE_ADDR sp,
+ function_call_return_method return_method,
CORE_ADDR struct_addr)
{
if (arc_debug)
value return? If so, struct_addr is the address of the reserved space for
the return structure to be written on the stack, and that address is
passed to that function as a hidden first argument. */
- if (struct_return)
+ if (return_method == return_method_struct)
{
/* Pass the return address in the first argument register. */
regcache_cooked_write_unsigned (regcache, arg_reg, struct_addr);
/* Note we don't use write_unsigned here, since that would convert
the byte order, but we are already in the correct byte order. */
- regcache_cooked_write (regcache, arg_reg, data);
+ regcache->cooked_write (arg_reg, data);
data += ARC_REGISTER_SIZE;
total_space -= ARC_REGISTER_SIZE;
function passes a hidden first parameter to the callee (in R0). That
parameter is the address at which the value being returned should be
stored. Otherwise, the result is returned in registers. */
- int is_struct_return = (TYPE_CODE (valtype) == TYPE_CODE_STRUCT
- || TYPE_CODE (valtype) == TYPE_CODE_UNION
+ int is_struct_return = (valtype->code () == TYPE_CODE_STRUCT
+ || valtype->code () == TYPE_CODE_UNION
|| TYPE_LENGTH (valtype) > 2 * ARC_REGISTER_SIZE);
if (arc_debug)
/* Store of some register. May or may not update base address register. */
if (insn.insn_class == STORE || insn.insn_class == PUSH)
{
- /* There is definetely at least one operand - register/value being
+ /* There is definitely at least one operand - register/value being
stored. */
gdb_assert (insn.operands_count > 0);
addr = pv_add_constant (regs[base_reg],
arc_insn_get_memory_offset (insn));
- if (pv_area_store_would_trash (stack, addr))
+ if (stack->store_would_trash (addr))
return false;
if (insn.data_size_mode != ARC_SCALING_D)
else
size = ARC_REGISTER_SIZE;
- pv_area_store (stack, addr, size, store_value);
+ stack->store (addr, size, store_value);
}
else
{
/* If this is a double store, than write N+1 register as well. */
pv_t store_value1 = regs[insn.operands[0].value];
pv_t store_value2 = regs[insn.operands[0].value + 1];
- pv_area_store (stack, addr, ARC_REGISTER_SIZE, store_value1);
- pv_area_store (stack,
- pv_add_constant (addr, ARC_REGISTER_SIZE),
- ARC_REGISTER_SIZE, store_value2);
+ stack->store (addr, ARC_REGISTER_SIZE, store_value1);
+ stack->store (pv_add_constant (addr, ARC_REGISTER_SIZE),
+ ARC_REGISTER_SIZE, store_value2);
}
else
{
pv_t store_value
= pv_constant (arc_insn_get_operand_value (insn, 0));
- pv_area_store (stack, addr, ARC_REGISTER_SIZE * 2, store_value);
+ stack->store (addr, ARC_REGISTER_SIZE * 2, store_value);
}
}
/* Assume that if the last register (closest to new SP) can be written,
then it is possible to write all of them. */
- if (pv_area_store_would_trash (stack, new_sp))
+ if (stack->store_would_trash (new_sp))
return false;
/* Current store address. */
if (is_fp_saved)
{
addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
- pv_area_store (stack, addr, ARC_REGISTER_SIZE, regs[ARC_FP_REGNUM]);
+ stack->store (addr, ARC_REGISTER_SIZE, regs[ARC_FP_REGNUM]);
}
/* Registers are stored in backward order: from GP (R26) to R13. */
for (int i = ARC_R13_REGNUM + regs_saved - 1; i >= ARC_R13_REGNUM; i--)
{
addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
- pv_area_store (stack, addr, ARC_REGISTER_SIZE, regs[i]);
+ stack->store (addr, ARC_REGISTER_SIZE, regs[i]);
}
if (is_blink_saved)
{
addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
- pv_area_store (stack, addr, ARC_REGISTER_SIZE,
- regs[ARC_BLINK_REGNUM]);
+ stack->store (addr, ARC_REGISTER_SIZE,
+ regs[ARC_BLINK_REGNUM]);
}
gdb_assert (pv_is_identical (addr, new_sp));
If CACHE is not NULL, then it will be filled with information about saved
registers.
- There are several variations of prologue which GDB may encouter. "Full"
+ There are several variations of prologue which GDB may encounter. "Full"
prologue looks like this:
sub sp,sp,<imm> ; Space for variadic arguments.
store, that doesn't update SP. Like this:
- sub sp,sp,8 ; Create space for calee-saved registers.
+ sub sp,sp,8 ; Create space for callee-saved registers.
st r13,[sp,4] ; Store callee saved registers (up to R26/GP).
st r14,[sp,0]
pv_t regs[ARC_LAST_CORE_REGNUM + 1];
for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
regs[i] = pv_register (i, 0);
- struct pv_area *stack = make_pv_area (ARC_SP_REGNUM,
- gdbarch_addr_bit (gdbarch));
- struct cleanup *back_to = make_cleanup_free_pv_area (stack);
+ pv_area stack (ARC_SP_REGNUM, gdbarch_addr_bit (gdbarch));
CORE_ADDR current_prologue_end = entrypoint;
/* If this instruction is in the prologue, fields in the cache will be
updated, and the saved registers mask may be updated. */
- if (!arc_is_in_prologue (gdbarch, insn, regs, stack))
+ if (!arc_is_in_prologue (gdbarch, insn, regs, &stack))
{
/* Found an instruction that is not in the prologue. */
if (arc_debug)
for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
{
CORE_ADDR offset;
- if (pv_area_find_reg (stack, gdbarch, i, &offset))
+ if (stack.find_reg (gdbarch, i, &offset))
cache->saved_regs[i].addr = offset;
}
}
- do_cleanups (back_to);
return current_prologue_end;
}
int
arc_delayed_print_insn (bfd_vma addr, struct disassemble_info *info)
{
- int (*print_insn) (bfd_vma, struct disassemble_info *);
- /* exec_bfd may be null, if GDB is run without a target BFD file. Opcodes
- will handle NULL value gracefully. */
- print_insn = arc_get_disassembler (exec_bfd);
- gdb_assert (print_insn != NULL);
- return print_insn (addr, info);
+ /* Standard BFD "machine number" field allows libopcodes disassembler to
+ distinguish ARC 600, 700 and v2 cores, however v2 encompasses both ARC EM
+ and HS, which have some difference between. There are two ways to specify
+ what is the target core:
+ 1) via the disassemble_info->disassembler_options;
+ 2) otherwise libopcodes will use private (architecture-specific) ELF
+ header.
+
+ Using disassembler_options is preferable, because it comes directly from
+ GDBserver which scanned an actual ARC core identification info. However,
+ not all GDBservers report core architecture, so as a fallback GDB still
+ should support analysis of ELF header. The libopcodes disassembly code
+ uses the section to find the BFD and the BFD to find the ELF header,
+ therefore this function should set disassemble_info->section properly.
+
+ disassembler_options was already set by non-target specific code with
+ proper options obtained via gdbarch_disassembler_options ().
+
+ This function might be called multiple times in a sequence, reusing same
+ disassemble_info. */
+ if ((info->disassembler_options == NULL) && (info->section == NULL))
+ {
+ struct obj_section *s = find_pc_section (addr);
+ if (s != NULL)
+ info->section = s->the_bfd_section;
+ }
+
+ return default_print_insn (addr, info);
}
/* Baremetal breakpoint instructions.
}
}
-/* Implement the "unwind_pc" gdbarch method. */
-
-static CORE_ADDR
-arc_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
-{
- int pc_regnum = gdbarch_pc_regnum (gdbarch);
- CORE_ADDR pc = frame_unwind_register_unsigned (next_frame, pc_regnum);
-
- if (arc_debug)
- debug_printf ("arc: unwind PC: %s\n", paddress (gdbarch, pc));
-
- return pc;
-}
-
-/* Implement the "unwind_sp" gdbarch method. */
-
-static CORE_ADDR
-arc_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
-{
- int sp_regnum = gdbarch_sp_regnum (gdbarch);
- CORE_ADDR sp = frame_unwind_register_unsigned (next_frame, sp_regnum);
-
- if (arc_debug)
- debug_printf ("arc: unwind SP: %s\n", paddress (gdbarch, sp));
-
- return sp;
-}
-
/* Implement the "frame_align" gdbarch method. */
static CORE_ADDR
/* Dump the frame info. Used for internal debugging only. */
static void
-arc_print_frame_cache (struct gdbarch *gdbarch, char *message,
+arc_print_frame_cache (struct gdbarch *gdbarch, const char *message,
struct arc_frame_cache *cache, int addresses_known)
{
debug_printf ("arc: frame_info %s\n", message);
Returns TRUE if input tdesc was valid and in this case it will assign TDESC
and TDESC_DATA output parameters. */
-static int
+static bool
arc_tdesc_init (struct gdbarch_info info, const struct target_desc **tdesc,
struct tdesc_arch_data **tdesc_data)
{
tag. */
/* Cannot use arc_mach_is_arcv2 (), because gdbarch is not created yet. */
const int is_arcv2 = (info.bfd_arch_info->mach == bfd_mach_arc_arcv2);
- int is_reduced_rf;
+ bool is_reduced_rf;
const char *const *core_regs;
const char *core_feature_name;
- /* If target doesn't provide a description - use default one. */
+ /* If target doesn't provide a description, use the default ones. */
if (!tdesc_has_registers (tdesc_loc))
{
if (is_arcv2)
- {
- tdesc_loc = tdesc_arc_v2;
- if (arc_debug)
- debug_printf ("arc: Using default register set for ARC v2.\n");
- }
+ tdesc_loc = arc_read_description (ARC_SYS_TYPE_ARCV2);
else
- {
- tdesc_loc = tdesc_arc_arcompact;
- if (arc_debug)
- debug_printf ("arc: Using default register set for ARCompact.\n");
- }
+ tdesc_loc = arc_read_description (ARC_SYS_TYPE_ARCOMPACT);
}
else
{
{
arc_print (_("Error: ARC v2 target description supplied for "
"non-ARCv2 target.\n"));
- return FALSE;
+ return false;
}
- is_reduced_rf = FALSE;
+ is_reduced_rf = false;
core_feature_name = core_v2_feature_name;
core_regs = core_v2_register_names;
}
{
arc_print (_("Error: ARC v2 target description supplied for "
"non-ARCv2 target.\n"));
- return FALSE;
+ return false;
}
- is_reduced_rf = TRUE;
+ is_reduced_rf = true;
core_feature_name = core_reduced_v2_feature_name;
core_regs = core_v2_register_names;
}
{
arc_print (_("Error: ARCompact target description supplied "
"for non-ARCompact target.\n"));
- return FALSE;
+ return false;
}
- is_reduced_rf = FALSE;
+ is_reduced_rf = false;
core_feature_name = core_arcompact_feature_name;
core_regs = core_arcompact_register_names;
}
{
arc_print (_("Error: Couldn't find core register feature in "
"supplied target description."));
- return FALSE;
+ return false;
}
}
}
arc_print (_("Error: Cannot find required register `%s' in "
"feature `%s'.\n"), core_regs[i], core_feature_name);
tdesc_data_cleanup (tdesc_data_loc);
- return FALSE;
+ return false;
}
}
- /* Mandatory AUX registeres are intentionally few and are common between
+ /* Mandatory AUX registers are intentionally few and are common between
ARCompact and ARC v2, so same code can be used for both. */
feature = tdesc_find_feature (tdesc_loc, aux_minimal_feature_name);
if (feature == NULL)
arc_print (_("Error: Cannot find required feature `%s' in supplied "
"target description.\n"), aux_minimal_feature_name);
tdesc_data_cleanup (tdesc_data_loc);
- return FALSE;
+ return false;
}
for (int i = ARC_FIRST_AUX_REGNUM; i <= ARC_LAST_AUX_REGNUM; i++)
"in feature `%s'.\n"),
name, tdesc_feature_name (feature));
tdesc_data_cleanup (tdesc_data_loc);
- return FALSE;
+ return false;
}
}
*tdesc = tdesc_loc;
*tdesc_data = tdesc_data_loc;
- return TRUE;
+ return true;
+}
+
+/* Implement the type_align gdbarch function. */
+
+static ULONGEST
+arc_type_align (struct gdbarch *gdbarch, struct type *type)
+{
+ switch (type->code ())
+ {
+ case TYPE_CODE_PTR:
+ case TYPE_CODE_FUNC:
+ case TYPE_CODE_FLAGS:
+ case TYPE_CODE_INT:
+ case TYPE_CODE_RANGE:
+ case TYPE_CODE_FLT:
+ case TYPE_CODE_ENUM:
+ case TYPE_CODE_REF:
+ case TYPE_CODE_RVALUE_REF:
+ case TYPE_CODE_CHAR:
+ case TYPE_CODE_BOOL:
+ case TYPE_CODE_DECFLOAT:
+ case TYPE_CODE_METHODPTR:
+ case TYPE_CODE_MEMBERPTR:
+ type = check_typedef (type);
+ return std::min<ULONGEST> (4, TYPE_LENGTH (type));
+ default:
+ return 0;
+ }
}
/* Implement the "init" gdbarch method. */
set_gdbarch_int_bit (gdbarch, 32);
set_gdbarch_long_bit (gdbarch, 32);
set_gdbarch_long_long_bit (gdbarch, 64);
- set_gdbarch_long_long_align_bit (gdbarch, 32);
+ set_gdbarch_type_align (gdbarch, arc_type_align);
set_gdbarch_float_bit (gdbarch, 32);
set_gdbarch_float_format (gdbarch, floatformats_ieee_single);
set_gdbarch_double_bit (gdbarch, 64);
set_gdbarch_ps_regnum (gdbarch, ARC_STATUS32_REGNUM);
set_gdbarch_fp0_regnum (gdbarch, -1); /* No FPU registers. */
- set_gdbarch_dummy_id (gdbarch, arc_dummy_id);
set_gdbarch_push_dummy_call (gdbarch, arc_push_dummy_call);
set_gdbarch_push_dummy_code (gdbarch, arc_push_dummy_code);
else
set_gdbarch_decr_pc_after_break (gdbarch, 2);
- set_gdbarch_unwind_pc (gdbarch, arc_unwind_pc);
- set_gdbarch_unwind_sp (gdbarch, arc_unwind_sp);
-
set_gdbarch_frame_align (gdbarch, arc_frame_align);
set_gdbarch_print_insn (gdbarch, arc_delayed_print_insn);
if (tdep->jb_pc >= 0)
set_gdbarch_get_longjmp_target (gdbarch, arc_get_longjmp_target);
+ /* Disassembler options. Enforce CPU if it was specified in XML target
+ description, otherwise use default method of determining CPU (ELF private
+ header). */
+ if (info.target_desc != NULL)
+ {
+ const struct bfd_arch_info *tdesc_arch
+ = tdesc_architecture (info.target_desc);
+ if (tdesc_arch != NULL)
+ {
+ xfree (arc_disassembler_options);
+ /* FIXME: It is not really good to change disassembler options
+ behind the scene, because that might override options
+ specified by the user. However as of now ARC doesn't support
+ `set disassembler-options' hence this code is the only place
+ where options are changed. It also changes options for all
+ existing gdbarches, which also can be problematic, if
+ arc_gdbarch_init will start reusing existing gdbarch
+ instances. */
+ /* Target description specifies a BFD architecture, which is
+ different from ARC cpu, as accepted by disassembler (and most
+ other ARC tools), because cpu values are much more fine grained -
+ there can be multiple cpu values per single BFD architecture. As
+ a result this code should translate architecture to some cpu
+ value. Since there is no info on exact cpu configuration, it is
+ best to use the most feature-rich CPU, so that disassembler will
+ recognize all instructions available to the specified
+ architecture. */
+ switch (tdesc_arch->mach)
+ {
+ case bfd_mach_arc_arc601:
+ arc_disassembler_options = xstrdup ("cpu=arc601");
+ break;
+ case bfd_mach_arc_arc600:
+ arc_disassembler_options = xstrdup ("cpu=arc600");
+ break;
+ case bfd_mach_arc_arc700:
+ arc_disassembler_options = xstrdup ("cpu=arc700");
+ break;
+ case bfd_mach_arc_arcv2:
+ /* Machine arcv2 has three arches: ARCv2, EM and HS; where ARCv2
+ is treated as EM. */
+ if (arc_arch_is_hs (tdesc_arch))
+ arc_disassembler_options = xstrdup ("cpu=hs38_linux");
+ else
+ arc_disassembler_options = xstrdup ("cpu=em4_fpuda");
+ break;
+ default:
+ arc_disassembler_options = NULL;
+ break;
+ }
+ set_gdbarch_disassembler_options (gdbarch,
+ &arc_disassembler_options);
+ }
+ }
+
tdesc_use_registers (gdbarch, tdesc, tdesc_data);
return gdbarch;
fprintf_unfiltered (file, "arc_dump_tdep: jb_pc = %i\n", tdep->jb_pc);
}
-/* Wrapper for "maintenance print arc" list of commands. */
-
-static void
-maintenance_print_arc_command (char *args, int from_tty)
-{
- cmd_show_list (maintenance_print_arc_list, from_tty, "");
-}
-
/* This command accepts single argument - address of instruction to
disassemble. */
static void
-dump_arc_instruction_command (char *args, int from_tty)
+dump_arc_instruction_command (const char *args, int from_tty)
{
struct value *val;
if (args != NULL && strlen (args) > 0)
arc_insn_dump (insn);
}
-/* Suppress warning from -Wmissing-prototypes. */
-extern initialize_file_ftype _initialize_arc_tdep;
+/* See arc-tdep.h. */
+const target_desc *
+arc_read_description (arc_sys_type sys_type)
+{
+ if (arc_debug)
+ debug_printf ("arc: Reading target description for \"%s\".\n",
+ arc_sys_type_to_str (sys_type));
+
+ gdb_assert ((sys_type >= 0) && (sys_type < ARC_SYS_TYPE_NUM));
+ struct target_desc *tdesc = tdesc_arc_list[sys_type];
+
+ if (tdesc == nullptr)
+ {
+ tdesc = arc_create_target_description (sys_type);
+ tdesc_arc_list[sys_type] = tdesc;
+
+ if (arc_debug)
+ {
+ const char *arch = tdesc_architecture_name (tdesc);
+ const char *abi = tdesc_osabi_name (tdesc);
+ arch = arch != NULL ? arch : "";
+ abi = abi != NULL ? abi : "";
+ debug_printf ("arc: Created target description for "
+ "\"%s\": arch=\"%s\", ABI=\"%s\"\n",
+ arc_sys_type_to_str (sys_type), arch, abi);
+ }
+ }
+
+ return tdesc;
+}
+
+void _initialize_arc_tdep ();
void
-_initialize_arc_tdep (void)
+_initialize_arc_tdep ()
{
gdbarch_register (bfd_arch_arc, arc_gdbarch_init, arc_dump_tdep);
- initialize_tdesc_arc_v2 ();
- initialize_tdesc_arc_arcompact ();
-
/* Register ARC-specific commands with gdb. */
/* Add root prefix command for "maintenance print arc" commands. */
- add_prefix_cmd ("arc", class_maintenance, maintenance_print_arc_command,
- _("ARC-specific maintenance commands for printing GDB "
- "internal state."),
- &maintenance_print_arc_list, "maintenance print arc ", 0,
- &maintenanceprintlist);
+ add_show_prefix_cmd ("arc", class_maintenance,
+ _("ARC-specific maintenance commands for printing GDB "
+ "internal state."),
+ &maintenance_print_arc_list, "maintenance print arc ",
+ 0, &maintenanceprintlist);
add_cmd ("arc-instruction", class_maintenance,
dump_arc_instruction_command,