/* Target-dependent code for Xilinx MicroBlaze.
- Copyright 2009, 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 2009-2019 Free Software Foundation, Inc.
This file is part of GDB.
#include "inferior.h"
#include "regcache.h"
#include "target.h"
-#include "frame.h"
#include "frame-base.h"
#include "frame-unwind.h"
#include "dwarf2-frame.h"
#include "osabi.h"
-
-#include "gdb_assert.h"
-#include "gdb_string.h"
#include "target-descriptions.h"
-#include "opcodes/microblaze-opcm.h"
-#include "opcodes/microblaze-dis.h"
+#include "../opcodes/microblaze-opcm.h"
+#include "../opcodes/microblaze-dis.h"
#include "microblaze-tdep.h"
+#include "remote.h"
+
+#include "features/microblaze-with-stack-protect.c"
+#include "features/microblaze.c"
\f
/* Instruction macros used for analyzing the prologue. */
/* This set of instruction macros need to be changed whenever the
This set also needs to be verified if it is complete. */
#define IS_RETURN(op) (op == rtsd || op == rtid)
#define IS_UPDATE_SP(op, rd, ra) \
- ((op == addik || op == addi) && rd == REG_SP && ra == REG_SP)
+ ((op == addik || op == addi) && rd == REG_SP && ra == REG_SP)
#define IS_SPILL_SP(op, rd, ra) \
- ((op == swi || op == sw) && rd == REG_SP && ra == REG_SP)
+ ((op == swi || op == sw) && rd == REG_SP && ra == REG_SP)
#define IS_SPILL_REG(op, rd, ra) \
- ((op == swi || op == sw) && rd != REG_SP && ra == REG_SP)
+ ((op == swi || op == sw) && rd != REG_SP && ra == REG_SP)
#define IS_ALSO_SPILL_REG(op, rd, ra, rb) \
- ((op == swi || op == sw) && rd != REG_SP && ra == 0 && rb == REG_SP)
+ ((op == swi || op == sw) && rd != REG_SP && ra == 0 && rb == REG_SP)
#define IS_SETUP_FP(op, ra, rb) \
- ((op == add || op == addik || op == addk) && ra == REG_SP && rb == 0)
+ ((op == add || op == addik || op == addk) && ra == REG_SP && rb == 0)
#define IS_SPILL_REG_FP(op, rd, ra, fpregnum) \
- ((op == swi || op == sw) && rd != REG_SP && ra == fpregnum && ra != 0)
+ ((op == swi || op == sw) && rd != REG_SP && ra == fpregnum && ra != 0)
#define IS_SAVE_HIDDEN_PTR(op, rd, ra, rb) \
- ((op == add || op == addik) && ra == MICROBLAZE_FIRST_ARGREG && rb == 0)
+ ((op == add || op == addik) && ra == MICROBLAZE_FIRST_ARGREG && rb == 0)
/* The registers of the Xilinx microblaze processor. */
"rpc", "rmsr", "rear", "resr", "rfsr", "rbtr",
"rpvr0", "rpvr1", "rpvr2", "rpvr3", "rpvr4", "rpvr5", "rpvr6",
"rpvr7", "rpvr8", "rpvr9", "rpvr10", "rpvr11",
- "redr", "rpid", "rzpr", "rtlbx", "rtlbsx", "rtlblo", "rtlbhi"
+ "redr", "rpid", "rzpr", "rtlbx", "rtlbsx", "rtlblo", "rtlbhi",
+ "rslr", "rshr"
};
#define MICROBLAZE_NUM_REGS ARRAY_SIZE (microblaze_register_names)
\f
-static int microblaze_debug_flag = 0;
+static unsigned int microblaze_debug_flag = 0;
-void
+static void ATTRIBUTE_PRINTF (1, 2)
microblaze_debug (const char *fmt, ...)
{
if (microblaze_debug_flag)
\f
/* Fetch the instruction at PC. */
-unsigned long
+static unsigned long
microblaze_fetch_instruction (CORE_ADDR pc)
{
- enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
gdb_byte buf[4];
/* If we can't read the instruction at PC, return zero. */
- if (target_read_memory (pc, buf, sizeof (buf)))
+ if (target_read_code (pc, buf, sizeof (buf)))
return 0;
return extract_unsigned_integer (buf, 4, byte_order);
}
\f
+constexpr gdb_byte microblaze_break_insn[] = MICROBLAZE_BREAKPOINT;
-static CORE_ADDR
-microblaze_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp,
- CORE_ADDR funcaddr,
- struct value **args, int nargs,
- struct type *value_type,
- CORE_ADDR *real_pc, CORE_ADDR *bp_addr,
- struct regcache *regcache)
-{
- error (_("push_dummy_code not implemented"));
- return sp;
-}
-
+typedef BP_MANIPULATION (microblaze_break_insn) microblaze_breakpoint;
-static CORE_ADDR
-microblaze_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, CORE_ADDR struct_addr)
-{
- error (_("store_arguments not implemented"));
- return sp;
-}
-
-static const gdb_byte *
-microblaze_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pc,
- int *len)
-{
- static gdb_byte break_insn[] = MICROBLAZE_BREAKPOINT;
-
- *len = sizeof (break_insn);
- return break_insn;
-}
\f
/* Allocate and initialize a frame cache. */
microblaze_alloc_frame_cache (void)
{
struct microblaze_frame_cache *cache;
- int i;
cache = FRAME_OBSTACK_ZALLOC (struct microblaze_frame_cache);
/* Analyze the prologue to determine where registers are saved,
the end of the prologue, etc. Return the address of the first line
- of "real" code (i.e., the end of the prologue). */
+ of "real" code (i.e., the end of the prologue). */
static CORE_ADDR
microblaze_analyze_prologue (struct gdbarch *gdbarch, CORE_ADDR pc,
CORE_ADDR current_pc,
struct microblaze_frame_cache *cache)
{
- char *name;
+ const char *name;
CORE_ADDR func_addr, func_end, addr, stop, prologue_end_addr = 0;
unsigned long insn;
- int rn, rd, ra, rb, imm;
+ int rd, ra, rb, imm;
enum microblaze_instr op;
int flags = 0;
int save_hidden_pointer_found = 0;
int non_stack_instruction_found = 0;
- /* Find the start of this function. */
+ /* Find the start of this function. */
find_pc_partial_function (pc, &name, &func_addr, &func_end);
if (func_addr < pc)
pc = func_addr;
If we're about to return, our frame has already been deallocated.
If we are stopped at the first instruction of a prologue,
- then our frame has not yet been set up. */
+ then our frame has not yet been set up. */
/* Get the first insn from memory. */
else if (IS_SETUP_FP(op, ra, rb))
{
/* We have a frame pointer. Note the register which is
- acting as the frame pointer. */
+ acting as the frame pointer. */
flags |= MICROBLAZE_MY_FRAME_IN_FP;
flags &= ~MICROBLAZE_MY_FRAME_IN_SP;
cache->fp_regnum = rd;
/* When optimizations are enabled, it is not guaranteed that prologue
instructions are not mixed in with other instructions from the
- program. Some programs show this behavior at -O2. This can be
+ program. Some programs show this behavior at -O2. This can be
avoided by adding -fno-schedule-insns2 switch as of now (edk 8.1)
In such cases, we scan the function until we see the first control
instruction. */
{
- unsigned op = (unsigned)insn >> 26;
+ unsigned ctrl_op = (unsigned)insn >> 26;
/* continue if not control flow (branch, return). */
- if (op != 0x26 && op != 0x27 && op != 0x2d && op != 0x2e && op != 0x2f)
+ if (ctrl_op != 0x26 && ctrl_op != 0x27 && ctrl_op != 0x2d
+ && ctrl_op != 0x2e && ctrl_op != 0x2f)
continue;
- else if (op == 0x2c)
+ else if (ctrl_op == 0x2c)
continue; /* continue if imm. */
}
- /* This is not a prologue insn, so stop here. */
+ /* This is not a prologue insn, so stop here. */
microblaze_debug ("insn is not a prologue insn -- ending scan\n");
break;
}
/* Return PC of first real instruction of the function starting at
START_PC. */
-CORE_ADDR
+static CORE_ADDR
microblaze_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc)
{
struct symtab_and_line sal;
/* Normal frames. */
-struct microblaze_frame_cache *
+static struct microblaze_frame_cache *
microblaze_frame_cache (struct frame_info *next_frame, void **this_cache)
{
struct microblaze_frame_cache *cache;
struct gdbarch *gdbarch = get_frame_arch (next_frame);
- CORE_ADDR func, pc, fp;
int rn;
if (*this_cache)
- return *this_cache;
+ return (struct microblaze_frame_cache *) *this_cache;
cache = microblaze_alloc_frame_cache ();
*this_cache = cache;
for (rn = 0; rn < gdbarch_num_regs (gdbarch); rn++)
cache->register_offsets[rn] = -1;
- func = get_frame_func (next_frame);
+ /* Call for side effects. */
+ get_frame_func (next_frame);
cache->pc = get_frame_address_in_block (next_frame);
regnum = 15;
if (regnum == MICROBLAZE_SP_REGNUM)
regnum = 1;
- return trad_frame_get_prev_register (this_frame, cache->saved_regs, regnum);
+ return trad_frame_get_prev_register (this_frame,
+ cache->saved_regs, regnum);
}
else
return trad_frame_get_prev_register (this_frame, cache->saved_regs,
static const struct frame_unwind microblaze_frame_unwind =
{
NORMAL_FRAME,
+ default_frame_unwind_stop_reason,
microblaze_frame_this_id,
microblaze_frame_prev_register,
NULL,
};
\f
static CORE_ADDR
-microblaze_frame_base_address (struct frame_info *next_frame, void **this_cache)
+microblaze_frame_base_address (struct frame_info *next_frame,
+ void **this_cache)
{
struct microblaze_frame_cache *cache =
microblaze_frame_cache (next_frame, this_cache);
switch (TYPE_LENGTH (type))
{
case 1: /* return last byte in the register. */
- regcache_cooked_read (regcache, MICROBLAZE_RETVAL_REGNUM, buf);
+ regcache->cooked_read (MICROBLAZE_RETVAL_REGNUM, buf);
memcpy(valbuf, buf + MICROBLAZE_REGISTER_SIZE - 1, 1);
return;
case 2: /* return last 2 bytes in register. */
+ regcache->cooked_read (MICROBLAZE_RETVAL_REGNUM, buf);
memcpy(valbuf, buf + MICROBLAZE_REGISTER_SIZE - 2, 2);
return;
case 4: /* for sizes 4 or 8, copy the required length. */
case 8:
- regcache_cooked_read (regcache, MICROBLAZE_RETVAL_REGNUM, buf);
- regcache_cooked_read (regcache, MICROBLAZE_RETVAL_REGNUM+1, buf+4);
+ regcache->cooked_read (MICROBLAZE_RETVAL_REGNUM, buf);
+ regcache->cooked_read (MICROBLAZE_RETVAL_REGNUM + 1, buf+4);
memcpy (valbuf, buf, TYPE_LENGTH (type));
return;
default:
Longs are stored in r3 (most significant word) and r4 (least
significant word).
- Small structures are always returned on stack.
-*/
+ Small structures are always returned on stack. */
static void
microblaze_store_return_value (struct type *type, struct regcache *regcache,
{
gdb_assert (len == 8);
memcpy (buf, valbuf, 8);
- regcache_cooked_write (regcache, MICROBLAZE_RETVAL_REGNUM+1, buf + 4);
+ regcache->cooked_write (MICROBLAZE_RETVAL_REGNUM+1, buf + 4);
}
else
/* ??? Do we need to do any sign-extension here? */
memcpy (buf + 4 - len, valbuf, len);
- regcache_cooked_write (regcache, MICROBLAZE_RETVAL_REGNUM, buf);
+ regcache->cooked_write (MICROBLAZE_RETVAL_REGNUM, buf);
}
static enum return_value_convention
-microblaze_return_value (struct gdbarch *gdbarch, struct type *func_type,
+microblaze_return_value (struct gdbarch *gdbarch, struct value *function,
struct type *type, struct regcache *regcache,
gdb_byte *readbuf, const gdb_byte *writebuf)
{
return (TYPE_LENGTH (type) == 16);
}
-static void
-microblaze_write_pc (struct regcache *regcache, CORE_ADDR pc)
-{
- regcache_cooked_write_unsigned (regcache, MICROBLAZE_PC_REGNUM, pc);
-}
\f
static int dwarf2_to_reg_map[78] =
{ 0 /* r0 */, 1 /* r1 */, 2 /* r2 */, 3 /* r3 */, /* 0- 3 */
static int
microblaze_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int reg)
{
- gdb_assert (reg < sizeof (dwarf2_to_reg_map));
- return dwarf2_to_reg_map[reg];
+ if (reg >= 0 && reg < sizeof (dwarf2_to_reg_map))
+ return dwarf2_to_reg_map[reg];
+ return -1;
+}
+
+static void
+microblaze_register_g_packet_guesses (struct gdbarch *gdbarch)
+{
+ register_remote_g_packet_guess (gdbarch,
+ 4 * MICROBLAZE_NUM_CORE_REGS,
+ tdesc_microblaze);
+
+ register_remote_g_packet_guess (gdbarch,
+ 4 * MICROBLAZE_NUM_REGS,
+ tdesc_microblaze_with_stack_protect);
}
static struct gdbarch *
{
struct gdbarch_tdep *tdep;
struct gdbarch *gdbarch;
+ struct tdesc_arch_data *tdesc_data = NULL;
+ const struct target_desc *tdesc = info.target_desc;
/* If there is already a candidate, use it. */
arches = gdbarch_list_lookup_by_info (arches, &info);
if (arches != NULL)
return arches->gdbarch;
+ if (tdesc == NULL)
+ tdesc = tdesc_microblaze;
+
+ /* Check any target description for validity. */
+ if (tdesc_has_registers (tdesc))
+ {
+ const struct tdesc_feature *feature;
+ int valid_p;
+ int i;
+
+ feature = tdesc_find_feature (tdesc,
+ "org.gnu.gdb.microblaze.core");
+ if (feature == NULL)
+ return NULL;
+ tdesc_data = tdesc_data_alloc ();
+
+ valid_p = 1;
+ for (i = 0; i < MICROBLAZE_NUM_CORE_REGS; i++)
+ valid_p &= tdesc_numbered_register (feature, tdesc_data, i,
+ microblaze_register_names[i]);
+ feature = tdesc_find_feature (tdesc,
+ "org.gnu.gdb.microblaze.stack-protect");
+ if (feature != NULL)
+ {
+ valid_p = 1;
+ valid_p &= tdesc_numbered_register (feature, tdesc_data,
+ MICROBLAZE_SLR_REGNUM,
+ "rslr");
+ valid_p &= tdesc_numbered_register (feature, tdesc_data,
+ MICROBLAZE_SHR_REGNUM,
+ "rshr");
+ }
+
+ if (!valid_p)
+ {
+ tdesc_data_cleanup (tdesc_data);
+ return NULL;
+ }
+ }
/* Allocate space for the new architecture. */
- tdep = XMALLOC (struct gdbarch_tdep);
+ tdep = XCNEW (struct gdbarch_tdep);
gdbarch = gdbarch_alloc (&info, tdep);
set_gdbarch_long_double_bit (gdbarch, 128);
/* Call dummy code. */
set_gdbarch_call_dummy_location (gdbarch, ON_STACK);
- set_gdbarch_push_dummy_code (gdbarch, microblaze_push_dummy_code);
- set_gdbarch_push_dummy_call (gdbarch, microblaze_push_dummy_call);
set_gdbarch_return_value (gdbarch, microblaze_return_value);
set_gdbarch_stabs_argument_has_addr
/* Stack grows downward. */
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
- set_gdbarch_breakpoint_from_pc (gdbarch, microblaze_breakpoint_from_pc);
+ set_gdbarch_breakpoint_kind_from_pc (gdbarch,
+ microblaze_breakpoint::kind_from_pc);
+ set_gdbarch_sw_breakpoint_from_kind (gdbarch,
+ microblaze_breakpoint::bp_from_kind);
set_gdbarch_frame_args_skip (gdbarch, 8);
- set_gdbarch_print_insn (gdbarch, print_insn_microblaze);
-
- set_gdbarch_write_pc (gdbarch, microblaze_write_pc);
-
set_gdbarch_unwind_pc (gdbarch, microblaze_unwind_pc);
+ microblaze_register_g_packet_guesses (gdbarch);
+
frame_base_set_default (gdbarch, µblaze_frame_base);
/* Hook in ABI-specific overrides, if they have been registered. */
gdbarch_init_osabi (info, gdbarch);
- /* Unwind the frame. */
+ /* Unwind the frame. */
dwarf2_append_unwinders (gdbarch);
frame_unwind_append_unwinder (gdbarch, µblaze_frame_unwind);
frame_base_append_sniffer (gdbarch, dwarf2_frame_base_sniffer);
+ if (tdesc_data != NULL)
+ tdesc_use_registers (gdbarch, tdesc, tdesc_data);
return gdbarch;
}
-/* Provide a prototype to silence -Wmissing-prototypes. */
-void _initialize_microblaze_tdep (void);
-
void
_initialize_microblaze_tdep (void)
{
register_gdbarch_init (bfd_arch_microblaze, microblaze_gdbarch_init);
+ initialize_tdesc_microblaze_with_stack_protect ();
+ initialize_tdesc_microblaze ();
/* Debug this files internals. */
- add_setshow_zinteger_cmd ("microblaze", class_maintenance,
- µblaze_debug_flag, _("\
+ add_setshow_zuinteger_cmd ("microblaze", class_maintenance,
+ µblaze_debug_flag, _("\
Set microblaze debugging."), _("\
Show microblaze debugging."), _("\
When non-zero, microblaze specific debugging is enabled."),
- NULL,
- NULL,
- &setdebuglist, &showdebuglist);
+ NULL,
+ NULL,
+ &setdebuglist, &showdebuglist);
}