-/* ARC target-dependent stuff.
- Copyright (C) 1995, 1997 Free Software Foundation, Inc.
+/* Target dependent code for ARC arhitecture, for GDB.
-This file is part of GDB.
+ Copyright 2005-2019 Free Software Foundation, Inc.
+ Contributed by Synopsys Inc.
-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 2 of the License, or
-(at your option) any later version.
+ This file is part of GDB.
-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.
+ 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.
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+ 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/>. */
+
+/* GDB header files. */
#include "defs.h"
-#include "frame.h"
-#include "inferior.h"
+#include "arch-utils.h"
+#include "disasm.h"
+#include "dwarf2-frame.h"
+#include "frame-base.h"
+#include "frame-unwind.h"
#include "gdbcore.h"
-#include "target.h"
-#include "floatformat.h"
-#include "symtab.h"
#include "gdbcmd.h"
+#include "objfiles.h"
+#include "prologue-value.h"
+#include "trad-frame.h"
+
+/* ARC header files. */
+#include "opcode/arc.h"
+#include "../opcodes/arc-dis.h"
+#include "arc-tdep.h"
+
+/* Standard headers. */
+#include <algorithm>
+
+/* Default target descriptions. */
+#include "features/arc-v2.c"
+#include "features/arc-arcompact.c"
-/* Current CPU, set with the "set cpu" command. */
-static int arc_bfd_mach_type;
-char *arc_cpu_type;
-char *tmp_arc_cpu_type;
-
-/* Table of cpu names. */
-struct {
- char *name;
- int value;
-} arc_cpu_type_table[] = {
- { "base", bfd_mach_arc_base },
- { NULL, 0 }
+/* The frame unwind cache for ARC. */
+
+struct arc_frame_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 prev_sp;
+
+ /* Register that is a base for this frame - FP for normal frame, SP for
+ non-FP frames. */
+ int frame_base_reg;
+
+ /* Offset from the previous SP to the current frame base. If GCC uses
+ `SUB SP,SP,offset` to allocate space for local variables, then it will be
+ done after setting up a frame pointer, but it still will be considered
+ part of prologue, therefore SP will be lesser than FP at the end of the
+ prologue analysis. In this case that would be an offset from old SP to a
+ new FP. But in case of non-FP frames, frame base is an SP and thus that
+ would be an offset from old SP to new SP. What is important is that this
+ is an offset from old SP to a known register, so it can be used to find
+ old SP.
+
+ Using FP is preferable, when possible, because SP can change in function
+ body after prologue due to alloca, variadic arguments or other shenanigans.
+ If that is the case in the caller frame, then PREV_SP will point to SP at
+ the moment of function call, but it will be different from SP value at the
+ end of the caller prologue. As a result it will not be possible to
+ reconstruct caller's frame and go past it in the backtrace. Those things
+ are unlikely to happen to FP - FP value at the moment of function call (as
+ stored on stack in callee prologue) is also an FP value at the end of the
+ caller's prologue. */
+
+ LONGEST frame_base_offset;
+
+ /* Store addresses for registers saved in prologue. During prologue analysis
+ GDB stores offsets relatively to "old SP", then after old SP is evaluated,
+ offsets are replaced with absolute addresses. */
+ struct trad_frame_saved_reg *saved_regs;
};
-/* Used by simulator. */
-int display_pipeline_p;
-int cpu_timer;
-/* This one must have the same type as used in the emulator.
- It's currently an enum so this should be ok for now. */
-int debug_pipeline_p;
-
-#define ARC_CALL_SAVED_REG(r) ((r) >= 16 && (r) < 24)
-
-#define OPMASK 0xf8000000
-
-/* Instruction field accessor macros.
- See the Programmer's Reference Manual. */
-#define X_OP(i) (((i) >> 27) & 0x1f)
-#define X_A(i) (((i) >> 21) & 0x3f)
-#define X_B(i) (((i) >> 15) & 0x3f)
-#define X_C(i) (((i) >> 9) & 0x3f)
-#define X_D(i) ((((i) & 0x1ff) ^ 0x100) - 0x100)
-#define X_L(i) (((((i) >> 5) & 0x3ffffc) ^ 0x200000) - 0x200000)
-#define X_N(i) (((i) >> 5) & 3)
-#define X_Q(i) ((i) & 0x1f)
-
-/* Return non-zero if X is a short immediate data indicator. */
-#define SHIMM_P(x) ((x) == 61 || (x) == 63)
-
-/* Return non-zero if X is a "long" (32 bit) immediate data indicator. */
-#define LIMM_P(x) ((x) == 62)
-
-/* Build a simple instruction. */
-#define BUILD_INSN(op, a, b, c, d) \
- ((((op) & 31) << 27) \
- | (((a) & 63) << 21) \
- | (((b) & 63) << 15) \
- | (((c) & 63) << 9) \
- | ((d) & 511))
-\f
-/* Codestream stuff. */
-static void codestream_read PARAMS ((unsigned int *, int));
-static void codestream_seek PARAMS ((CORE_ADDR));
-static unsigned int codestream_fill PARAMS ((int));
-
-#define CODESTREAM_BUFSIZ 16
-static CORE_ADDR codestream_next_addr;
-static CORE_ADDR codestream_addr;
-static unsigned int codestream_buf[CODESTREAM_BUFSIZ];
-static int codestream_off;
-static int codestream_cnt;
-
-#define codestream_tell() \
- (codestream_addr + codestream_off * sizeof (codestream_buf[0]))
-#define codestream_peek() \
- (codestream_cnt == 0 \
- ? codestream_fill (1) \
- : codestream_buf[codestream_off])
-#define codestream_get() \
- (codestream_cnt-- == 0 \
- ? codestream_fill (0) \
- : codestream_buf[codestream_off++])
-
-static unsigned int
-codestream_fill (peek_flag)
- int peek_flag;
-{
- codestream_addr = codestream_next_addr;
- codestream_next_addr += CODESTREAM_BUFSIZ * sizeof (codestream_buf[0]);
- codestream_off = 0;
- codestream_cnt = CODESTREAM_BUFSIZ;
- read_memory (codestream_addr, (char *) codestream_buf,
- CODESTREAM_BUFSIZ * sizeof (codestream_buf[0]));
- /* FIXME: check return code? */
-
- /* Handle byte order differences. */
- if (HOST_BYTE_ORDER != TARGET_BYTE_ORDER)
- {
- register unsigned int i, j, n = sizeof (codestream_buf[0]);
- register char tmp, *p;
- for (i = 0, p = (char *) codestream_buf; i < CODESTREAM_BUFSIZ;
- ++i, p += n)
- for (j = 0; j < n / 2; ++j)
- tmp = p[j], p[j] = p[n - 1 - j], p[n - 1 - j] = tmp;
- }
-
- if (peek_flag)
- return codestream_peek ();
- else
- return codestream_get ();
+/* Global debug flag. */
+
+int arc_debug;
+
+/* List of "maintenance print arc" commands. */
+
+static struct cmd_list_element *maintenance_print_arc_list = NULL;
+
+/* XML target description features. */
+
+static const char core_v2_feature_name[] = "org.gnu.gdb.arc.core.v2";
+static const char
+ core_reduced_v2_feature_name[] = "org.gnu.gdb.arc.core-reduced.v2";
+static const char
+ core_arcompact_feature_name[] = "org.gnu.gdb.arc.core.arcompact";
+static const char aux_minimal_feature_name[] = "org.gnu.gdb.arc.aux-minimal";
+
+/* XML target description known registers. */
+
+static const char *const core_v2_register_names[] = {
+ "r0", "r1", "r2", "r3",
+ "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11",
+ "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19",
+ "r20", "r21", "r22", "r23",
+ "r24", "r25", "gp", "fp",
+ "sp", "ilink", "r30", "blink",
+ "r32", "r33", "r34", "r35",
+ "r36", "r37", "r38", "r39",
+ "r40", "r41", "r42", "r43",
+ "r44", "r45", "r46", "r47",
+ "r48", "r49", "r50", "r51",
+ "r52", "r53", "r54", "r55",
+ "r56", "r57", "accl", "acch",
+ "lp_count", "reserved", "limm", "pcl",
+};
+
+static const char *const aux_minimal_register_names[] = {
+ "pc", "status32",
+};
+
+static const char *const core_arcompact_register_names[] = {
+ "r0", "r1", "r2", "r3",
+ "r4", "r5", "r6", "r7",
+ "r8", "r9", "r10", "r11",
+ "r12", "r13", "r14", "r15",
+ "r16", "r17", "r18", "r19",
+ "r20", "r21", "r22", "r23",
+ "r24", "r25", "gp", "fp",
+ "sp", "ilink1", "ilink2", "blink",
+ "r32", "r33", "r34", "r35",
+ "r36", "r37", "r38", "r39",
+ "r40", "r41", "r42", "r43",
+ "r44", "r45", "r46", "r47",
+ "r48", "r49", "r50", "r51",
+ "r52", "r53", "r54", "r55",
+ "r56", "r57", "r58", "r59",
+ "lp_count", "reserved", "limm", "pcl",
+};
+
+static char *arc_disassembler_options = NULL;
+
+/* 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. */
+
+/* Returns an unsigned value of OPERAND_NUM in instruction INSN.
+ For relative branch instructions returned value is an offset, not an actual
+ branch target. */
+
+static ULONGEST
+arc_insn_get_operand_value (const struct arc_instruction &insn,
+ unsigned int operand_num)
+{
+ switch (insn.operands[operand_num].kind)
+ {
+ case ARC_OPERAND_KIND_LIMM:
+ gdb_assert (insn.limm_p);
+ return insn.limm_value;
+ case ARC_OPERAND_KIND_SHIMM:
+ return insn.operands[operand_num].value;
+ default:
+ /* Value in instruction is a register number. */
+ struct regcache *regcache = get_current_regcache ();
+ ULONGEST value;
+ regcache_cooked_read_unsigned (regcache,
+ insn.operands[operand_num].value,
+ &value);
+ return value;
+ }
}
-static void
-codestream_seek (place)
- CORE_ADDR place;
+/* Like arc_insn_get_operand_value, but returns a signed value. */
+
+static LONGEST
+arc_insn_get_operand_value_signed (const struct arc_instruction &insn,
+ unsigned int operand_num)
{
- codestream_next_addr = place / CODESTREAM_BUFSIZ;
- codestream_next_addr *= CODESTREAM_BUFSIZ;
- codestream_cnt = 0;
- codestream_fill (1);
- while (codestream_tell () != place)
- codestream_get ();
+ switch (insn.operands[operand_num].kind)
+ {
+ case ARC_OPERAND_KIND_LIMM:
+ gdb_assert (insn.limm_p);
+ /* Convert unsigned raw value to signed one. This assumes 2's
+ complement arithmetic, but so is the LONG_MIN value from generic
+ defs.h and that assumption is true for ARC. */
+ gdb_static_assert (sizeof (insn.limm_value) == sizeof (int));
+ return (((LONGEST) insn.limm_value) ^ INT_MIN) - INT_MIN;
+ case ARC_OPERAND_KIND_SHIMM:
+ /* Sign conversion has been done by binutils. */
+ return insn.operands[operand_num].value;
+ default:
+ /* Value in instruction is a register number. */
+ struct regcache *regcache = get_current_regcache ();
+ LONGEST value;
+ regcache_cooked_read_signed (regcache,
+ insn.operands[operand_num].value,
+ &value);
+ return value;
+ }
}
-/* This function is currently unused but leave in for now. */
+/* Get register with base address of memory operation. */
-static void
-codestream_read (buf, count)
- unsigned int *buf;
- int count;
+int
+arc_insn_get_memory_base_reg (const struct arc_instruction &insn)
{
- unsigned int *p;
- int i;
- p = buf;
- for (i = 0; i < count; i++)
- *p++ = codestream_get ();
+ /* POP_S and PUSH_S have SP as an implicit argument in a disassembler. */
+ if (insn.insn_class == PUSH || insn.insn_class == POP)
+ return ARC_SP_REGNUM;
+
+ gdb_assert (insn.insn_class == LOAD || insn.insn_class == STORE);
+
+ /* Other instructions all have at least two operands: operand 0 is data,
+ operand 1 is address. Operand 2 is offset from address. However, see
+ comment to arc_instruction.operands - in some cases, third operand may be
+ missing, namely if it is 0. */
+ gdb_assert (insn.operands_count >= 2);
+ return insn.operands[1].value;
}
-\f
-/* Set up prologue scanning and return the first insn. */
-static unsigned int
-setup_prologue_scan (pc)
- CORE_ADDR pc;
+/* Get offset of a memory operation INSN. */
+
+CORE_ADDR
+arc_insn_get_memory_offset (const struct arc_instruction &insn)
{
- unsigned int insn;
+ /* POP_S and PUSH_S have offset as an implicit argument in a
+ disassembler. */
+ if (insn.insn_class == POP)
+ return 4;
+ else if (insn.insn_class == PUSH)
+ return -4;
+
+ gdb_assert (insn.insn_class == LOAD || insn.insn_class == STORE);
+
+ /* Other instructions all have at least two operands: operand 0 is data,
+ operand 1 is address. Operand 2 is offset from address. However, see
+ comment to arc_instruction.operands - in some cases, third operand may be
+ missing, namely if it is 0. */
+ if (insn.operands_count < 3)
+ return 0;
+
+ CORE_ADDR value = arc_insn_get_operand_value (insn, 2);
+ /* Handle scaling. */
+ if (insn.writeback_mode == ARC_WRITEBACK_AS)
+ {
+ /* Byte data size is not valid for AS. Halfword means shift by 1 bit.
+ Word and double word means shift by 2 bits. */
+ gdb_assert (insn.data_size_mode != ARC_SCALING_B);
+ if (insn.data_size_mode == ARC_SCALING_H)
+ value <<= 1;
+ else
+ value <<= 2;
+ }
+ return value;
+}
+
+CORE_ADDR
+arc_insn_get_branch_target (const struct arc_instruction &insn)
+{
+ gdb_assert (insn.is_control_flow);
+
+ /* BI [c]: PC = nextPC + (c << 2). */
+ if (insn.insn_class == BI)
+ {
+ ULONGEST reg_value = arc_insn_get_operand_value (insn, 0);
+ return arc_insn_get_linear_next_pc (insn) + (reg_value << 2);
+ }
+ /* BIH [c]: PC = nextPC + (c << 1). */
+ else if (insn.insn_class == BIH)
+ {
+ ULONGEST reg_value = arc_insn_get_operand_value (insn, 0);
+ return arc_insn_get_linear_next_pc (insn) + (reg_value << 1);
+ }
+ /* JLI and EI. */
+ /* JLI and EI depend on optional AUX registers. Not supported right now. */
+ else if (insn.insn_class == JLI)
+ {
+ fprintf_unfiltered (gdb_stderr,
+ "JLI_S instruction is not supported by the GDB.");
+ return 0;
+ }
+ else if (insn.insn_class == EI)
+ {
+ fprintf_unfiltered (gdb_stderr,
+ "EI_S instruction is not supported by the GDB.");
+ return 0;
+ }
+ /* LEAVE_S: PC = BLINK. */
+ else if (insn.insn_class == LEAVE)
+ {
+ struct regcache *regcache = get_current_regcache ();
+ ULONGEST value;
+ regcache_cooked_read_unsigned (regcache, ARC_BLINK_REGNUM, &value);
+ return value;
+ }
+ /* BBIT0/1, BRcc: PC = currentPC + operand. */
+ else if (insn.insn_class == BBIT0 || insn.insn_class == BBIT1
+ || insn.insn_class == BRCC)
+ {
+ /* Most instructions has branch target as their sole argument. However
+ conditional brcc/bbit has it as a third operand. */
+ CORE_ADDR pcrel_addr = arc_insn_get_operand_value (insn, 2);
- codestream_seek (pc);
- insn = codestream_get ();
+ /* Offset is relative to the 4-byte aligned address of the current
+ instruction, hence last two bits should be truncated. */
+ return pcrel_addr + align_down (insn.address, 4);
+ }
+ /* B, Bcc, BL, BLcc, LP, LPcc: PC = currentPC + operand. */
+ else if (insn.insn_class == BRANCH || insn.insn_class == LOOP)
+ {
+ CORE_ADDR pcrel_addr = arc_insn_get_operand_value (insn, 0);
- return insn;
+ /* Offset is relative to the 4-byte aligned address of the current
+ instruction, hence last two bits should be truncated. */
+ return pcrel_addr + align_down (insn.address, 4);
+ }
+ /* J, Jcc, JL, JLcc: PC = operand. */
+ else if (insn.insn_class == JUMP)
+ {
+ /* All jumps are single-operand. */
+ return arc_insn_get_operand_value (insn, 0);
+ }
+
+ /* This is some new and unknown instruction. */
+ gdb_assert_not_reached ("Unknown branch instruction.");
}
-/*
- * Find & return amount a local space allocated, and advance codestream to
- * first register push (if any).
- * If entry sequence doesn't make sense, return -1, and leave
- * codestream pointer random.
- */
+/* Dump INSN into gdb_stdlog. */
-static long
-arc_get_frame_setup (pc)
- CORE_ADDR pc;
+void
+arc_insn_dump (const struct arc_instruction &insn)
{
- unsigned int insn;
- /* Size of frame or -1 if unrecognizable prologue. */
- int frame_size = -1;
- /* An initial "sub sp,sp,N" may or may not be for a stdarg fn. */
- int maybe_stdarg_decr = -1;
+ struct gdbarch *gdbarch = target_gdbarch ();
+
+ arc_print ("Dumping arc_instruction at %s\n",
+ paddress (gdbarch, insn.address));
+ arc_print ("\tlength = %u\n", insn.length);
+
+ if (!insn.valid)
+ {
+ arc_print ("\tThis is not a valid ARC instruction.\n");
+ return;
+ }
- insn = setup_prologue_scan (pc);
+ arc_print ("\tlength_with_limm = %u\n", insn.length + (insn.limm_p ? 4 : 0));
+ arc_print ("\tcc = 0x%x\n", insn.condition_code);
+ arc_print ("\tinsn_class = %u\n", insn.insn_class);
+ arc_print ("\tis_control_flow = %i\n", insn.is_control_flow);
+ arc_print ("\thas_delay_slot = %i\n", insn.has_delay_slot);
- /* The authority for what appears here is the home-grown ABI.
- The most recent version is 1.2. */
+ CORE_ADDR next_pc = arc_insn_get_linear_next_pc (insn);
+ arc_print ("\tlinear_next_pc = %s\n", paddress (gdbarch, next_pc));
- /* First insn may be "sub sp,sp,N" if stdarg fn. */
- if ((insn & BUILD_INSN (-1, -1, -1, -1, 0))
- == BUILD_INSN (10, SP_REGNUM, SP_REGNUM, SHIMM_REGNUM, 0))
+ if (insn.is_control_flow)
{
- maybe_stdarg_decr = X_D (insn);
- insn = codestream_get ();
+ CORE_ADDR t = arc_insn_get_branch_target (insn);
+ arc_print ("\tbranch_target = %s\n", paddress (gdbarch, t));
}
- if ((insn & BUILD_INSN (-1, 0, -1, -1, -1)) /* st blink,[sp,4] */
- == BUILD_INSN (2, 0, SP_REGNUM, BLINK_REGNUM, 4))
+ arc_print ("\tlimm_p = %i\n", insn.limm_p);
+ if (insn.limm_p)
+ arc_print ("\tlimm_value = 0x%08x\n", insn.limm_value);
+
+ if (insn.insn_class == STORE || insn.insn_class == LOAD
+ || insn.insn_class == PUSH || insn.insn_class == POP)
+ {
+ arc_print ("\twriteback_mode = %u\n", insn.writeback_mode);
+ arc_print ("\tdata_size_mode = %u\n", insn.data_size_mode);
+ arc_print ("\tmemory_base_register = %s\n",
+ gdbarch_register_name (gdbarch,
+ arc_insn_get_memory_base_reg (insn)));
+ /* get_memory_offset returns an unsigned CORE_ADDR, but treat it as a
+ LONGEST for a nicer representation. */
+ arc_print ("\taddr_offset = %s\n",
+ plongest (arc_insn_get_memory_offset (insn)));
+ }
+
+ arc_print ("\toperands_count = %u\n", insn.operands_count);
+ for (unsigned int i = 0; i < insn.operands_count; ++i)
{
- insn = codestream_get ();
- /* Frame may not be necessary, even though blink is saved.
- At least this is something we recognize. */
- frame_size = 0;
+ int is_reg = (insn.operands[i].kind == ARC_OPERAND_KIND_REG);
+
+ arc_print ("\toperand[%u] = {\n", i);
+ arc_print ("\t\tis_reg = %i\n", is_reg);
+ if (is_reg)
+ arc_print ("\t\tregister = %s\n",
+ gdbarch_register_name (gdbarch, insn.operands[i].value));
+ /* Don't know if this value is signed or not, so print both
+ representations. This tends to look quite ugly, especially for big
+ numbers. */
+ arc_print ("\t\tunsigned value = %s\n",
+ pulongest (arc_insn_get_operand_value (insn, i)));
+ arc_print ("\t\tsigned value = %s\n",
+ plongest (arc_insn_get_operand_value_signed (insn, i)));
+ arc_print ("\t}\n");
}
+}
+
+CORE_ADDR
+arc_insn_get_linear_next_pc (const struct arc_instruction &insn)
+{
+ /* In ARC long immediate is always 4 bytes. */
+ return (insn.address + insn.length + (insn.limm_p ? 4 : 0));
+}
+
+/* Implement the "write_pc" gdbarch method.
+
+ In ARC PC register is a normal register so in most cases setting PC value
+ is a straightforward process: debugger just writes PC value. However it
+ gets trickier in case when current instruction is an instruction in delay
+ slot. In this case CPU will execute instruction at current PC value, then
+ will set PC to the current value of BTA register; also current instruction
+ cannot be branch/jump and some of the other instruction types. Thus if
+ debugger would try to just change PC value in this case, this instruction
+ will get executed, but then core will "jump" to the original branch target.
+
+ Whether current instruction is a delay-slot instruction or not is indicated
+ by DE bit in STATUS32 register indicates if current instruction is a delay
+ slot instruction. This bit is writable by debug host, which allows debug
+ host to prevent core from jumping after the delay slot instruction. It
+ also works in another direction: setting this bit will make core to treat
+ any current instructions as a delay slot instruction and to set PC to the
+ current value of BTA register.
+
+ To workaround issues with changing PC register while in delay slot
+ instruction, debugger should check for the STATUS32.DE bit and reset it if
+ it is set. No other change is required in this function. Most common
+ case, where this function might be required is calling inferior functions
+ from debugger. Generic GDB logic handles this pretty well: current values
+ of registers are stored, value of PC is changed (that is the job of this
+ function), and after inferior function is executed, GDB restores all
+ registers, include BTA and STATUS32, which also means that core is returned
+ to its original state of being halted on delay slot instructions.
+
+ This method is useless for ARC 600, because it doesn't have externally
+ exposed BTA register. In the case of ARC 600 it is impossible to restore
+ core to its state in all occasions thus core should never be halted (from
+ the perspective of debugger host) in the delay slot. */
+
+static void
+arc_write_pc (struct regcache *regcache, CORE_ADDR new_pc)
+{
+ struct gdbarch *gdbarch = regcache->arch ();
- if ((insn & BUILD_INSN (-1, 0, -1, -1, -1)) /* st fp,[sp] */
- == BUILD_INSN (2, 0, SP_REGNUM, FP_REGNUM, 0))
- {
- insn = codestream_get ();
- if ((insn & BUILD_INSN (-1, -1, -1, -1, 0))
- != BUILD_INSN (12, FP_REGNUM, SP_REGNUM, SP_REGNUM, 0))
- return -1;
+ if (arc_debug)
+ debug_printf ("arc: Writing PC, new value=%s\n",
+ paddress (gdbarch, new_pc));
- /* Check for stack adjustment sub sp,sp,N. */
- insn = codestream_peek ();
- if ((insn & BUILD_INSN (-1, -1, -1, 0, 0))
- == BUILD_INSN (10, SP_REGNUM, SP_REGNUM, 0, 0))
+ regcache_cooked_write_unsigned (regcache, gdbarch_pc_regnum (gdbarch),
+ new_pc);
+
+ ULONGEST status32;
+ regcache_cooked_read_unsigned (regcache, gdbarch_ps_regnum (gdbarch),
+ &status32);
+
+ /* Mask for DE bit is 0x40. */
+ if (status32 & 0x40)
+ {
+ if (arc_debug)
{
- if (LIMM_P (X_C (insn)))
- frame_size = codestream_get ();
- else if (SHIMM_P (X_C (insn)))
- frame_size = X_D (insn);
- else
- return -1;
- if (frame_size < 0)
- return -1;
+ debug_printf ("arc: Changing PC while in delay slot. Will "
+ "reset STATUS32.DE bit to zero. Value of STATUS32 "
+ "register is 0x%s\n",
+ phex (status32, ARC_REGISTER_SIZE));
+ }
+
+ /* Reset bit and write to the cache. */
+ status32 &= ~0x40;
+ regcache_cooked_write_unsigned (regcache, gdbarch_ps_regnum (gdbarch),
+ status32);
+ }
+}
+
+/* Implement the "virtual_frame_pointer" gdbarch method.
+
+ According to ABI the FP (r27) is used to point to the middle of the current
+ stack frame, just below the saved FP and before local variables, register
+ spill area and outgoing args. However for optimization levels above O2 and
+ in any case in leaf functions, the frame pointer is usually not set at all.
+ The exception being when handling nested functions.
+
+ We use this function to return a "virtual" frame pointer, marking the start
+ of the current stack frame as a register-offset pair. If the FP is not
+ being used, then it should return SP, with an offset of the frame size.
+
+ The current implementation doesn't actually know the frame size, nor
+ whether the FP is actually being used, so for now we just return SP and an
+ offset of zero. This is no worse than other architectures, but is needed
+ to avoid assertion failures.
+
+ TODO: Can we determine the frame size to get a correct offset?
+
+ PC is a program counter where we need the virtual FP. REG_PTR is the base
+ register used for the virtual FP. OFFSET_PTR is the offset used for the
+ virtual FP. */
+
+static void
+arc_virtual_frame_pointer (struct gdbarch *gdbarch, CORE_ADDR pc,
+ int *reg_ptr, LONGEST *offset_ptr)
+{
+ *reg_ptr = gdbarch_sp_regnum (gdbarch);
+ *offset_ptr = 0;
+}
+
+/* Implement the "push_dummy_call" gdbarch method.
+
+ Stack Frame Layout
+
+ This shows the layout of the stack frame for the general case of a
+ function call; a given function might not have a variable number of
+ arguments or local variables, or might not save any registers, so it would
+ not have the corresponding frame areas. Additionally, a leaf function
+ (i.e. one which calls no other functions) does not need to save the
+ contents of the BLINK register (which holds its return address), and a
+ function might not have a frame pointer.
+
+ The stack grows downward, so SP points below FP in memory; SP always
+ points to the last used word on the stack, not the first one.
+
+ | | |
+ | arg word N | | caller's
+ | : | | frame
+ | arg word 10 | |
+ | arg word 9 | |
+ old SP ---> +-----------------------+ --+
+ | | |
+ | callee-saved | |
+ | registers | |
+ | including fp, blink | |
+ | | | callee's
+ new FP ---> +-----------------------+ | frame
+ | | |
+ | local | |
+ | variables | |
+ | | |
+ | register | |
+ | spill area | |
+ | | |
+ | outgoing args | |
+ | | |
+ new SP ---> +-----------------------+ --+
+ | |
+ | unused |
+ | |
+ |
+ |
+ V
+ downwards
+
+ The list of arguments to be passed to a function is considered to be a
+ sequence of _N_ words (as though all the parameters were stored in order in
+ memory with each parameter occupying an integral number of words). Words
+ 1..8 are passed in registers 0..7; if the function has more than 8 words of
+ arguments then words 9..@em N are passed on the stack in the caller's frame.
+
+ If the function has a variable number of arguments, e.g. it has a form such
+ as `function (p1, p2, ...);' and _P_ words are required to hold the values
+ of the named parameters (which are passed in registers 0..@em P -1), then
+ the remaining 8 - _P_ words passed in registers _P_..7 are spilled into the
+ top of the frame so that the anonymous parameter words occupy a continuous
+ region.
+
+ Any arguments are already in target byte order. We just need to store
+ them!
+
+ BP_ADDR is the return address where breakpoint must be placed. NARGS is
+ the number of arguments to the function. ARGS is the arguments values (in
+ target byte order). SP is the Current value of SP register. STRUCT_RETURN
+ is TRUE if structures are returned by the function. STRUCT_ADDR is the
+ hidden address for returning a struct. Returns SP of a new frame. */
+
+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,
+ function_call_return_method return_method,
+ CORE_ADDR struct_addr)
+{
+ if (arc_debug)
+ debug_printf ("arc: push_dummy_call (nargs = %d)\n", nargs);
+
+ int arg_reg = ARC_FIRST_ARG_REGNUM;
- codestream_get ();
+ /* Push the return address. */
+ regcache_cooked_write_unsigned (regcache, ARC_BLINK_REGNUM, bp_addr);
+
+ /* Are we returning a value using a structure return instead of a normal
+ 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 (return_method == return_method_struct)
+ {
+ /* Pass the return address in the first argument register. */
+ regcache_cooked_write_unsigned (regcache, arg_reg, struct_addr);
+
+ if (arc_debug)
+ debug_printf ("arc: struct return address %s passed in R%d",
+ print_core_address (gdbarch, struct_addr), arg_reg);
+
+ arg_reg++;
+ }
- /* This sequence is used to get the address of the return
- buffer for a function that returns a structure. */
- insn = codestream_peek ();
- if ((insn & OPMASK) == 0x60000000)
- codestream_get ();
+ if (nargs > 0)
+ {
+ unsigned int total_space = 0;
+
+ /* How much space do the arguments occupy in total? Must round each
+ argument's size up to an integral number of words. */
+ for (int i = 0; i < nargs; i++)
+ {
+ unsigned int len = TYPE_LENGTH (value_type (args[i]));
+ unsigned int space = align_up (len, 4);
+
+ total_space += space;
+
+ if (arc_debug)
+ debug_printf ("arc: arg %d: %u bytes -> %u\n", i, len, space);
}
- /* Frameless fn. */
- else
+
+ /* Allocate a buffer to hold a memory image of the arguments. */
+ gdb_byte *memory_image = XCNEWVEC (gdb_byte, total_space);
+
+ /* Now copy all of the arguments into the buffer, correctly aligned. */
+ gdb_byte *data = memory_image;
+ for (int i = 0; i < nargs; i++)
+ {
+ unsigned int len = TYPE_LENGTH (value_type (args[i]));
+ unsigned int space = align_up (len, 4);
+
+ memcpy (data, value_contents (args[i]), (size_t) len);
+ if (arc_debug)
+ debug_printf ("arc: copying arg %d, val 0x%08x, len %d to mem\n",
+ i, *((int *) value_contents (args[i])), len);
+
+ data += space;
+ }
+
+ /* Now load as much as possible of the memory image into registers. */
+ data = memory_image;
+ while (arg_reg <= ARC_LAST_ARG_REGNUM)
+ {
+ if (arc_debug)
+ debug_printf ("arc: passing 0x%02x%02x%02x%02x in register R%d\n",
+ data[0], data[1], data[2], data[3], arg_reg);
+
+ /* 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 (arg_reg, data);
+
+ data += ARC_REGISTER_SIZE;
+ total_space -= ARC_REGISTER_SIZE;
+
+ /* All the data is now in registers. */
+ if (total_space == 0)
+ break;
+
+ arg_reg++;
+ }
+
+ /* If there is any data left, push it onto the stack (in a single write
+ operation). */
+ if (total_space > 0)
{
- frame_size = 0;
+ if (arc_debug)
+ debug_printf ("arc: passing %d bytes on stack\n", total_space);
+
+ sp -= total_space;
+ write_memory (sp, data, (int) total_space);
}
+
+ xfree (memory_image);
}
- /* If we found a "sub sp,sp,N" and nothing else, it may or may not be a
- stdarg fn. The stdarg decrement is not treated as part of the frame size,
- so we have a dilemma: what do we return? For now, if we get a
- "sub sp,sp,N" and nothing else assume this isn't a stdarg fn. One way
- to fix this completely would be to add a bit to the function descriptor
- that says the function is a stdarg function. */
+ /* Finally, update the SP register. */
+ regcache_cooked_write_unsigned (regcache, gdbarch_sp_regnum (gdbarch), sp);
- if (frame_size < 0 && maybe_stdarg_decr > 0)
- return maybe_stdarg_decr;
- return frame_size;
+ return sp;
}
-/* Given a pc value, skip it forward past the function prologue by
- disassembling instructions that appear to be a prologue.
+/* Implement the "push_dummy_code" gdbarch method.
- If FRAMELESS_P is set, we are only testing to see if the function
- is frameless. If it is a frameless function, return PC unchanged.
- This allows a quicker answer. */
+ We don't actually push any code. We just identify where a breakpoint can
+ be inserted to which we are can return and the resume address where we
+ should be called.
-CORE_ADDR
-arc_skip_prologue (pc, frameless_p)
- CORE_ADDR pc;
- int frameless_p;
+ ARC does not necessarily have an executable stack, so we can't put the
+ return breakpoint there. Instead we put it at the entry point of the
+ function. This means the SP is unchanged.
+
+ SP is a current stack pointer FUNADDR is an address of the function to be
+ called. ARGS is arguments to pass. NARGS is a number of args to pass.
+ VALUE_TYPE is a type of value returned. REAL_PC is a resume address when
+ the function is called. BP_ADDR is an address where breakpoint should be
+ set. Returns the updated stack pointer. */
+
+static CORE_ADDR
+arc_push_dummy_code (struct gdbarch *gdbarch, CORE_ADDR sp, CORE_ADDR funaddr,
+ struct value **args, int nargs, struct type *value_type,
+ CORE_ADDR *real_pc, CORE_ADDR *bp_addr,
+ struct regcache *regcache)
{
- unsigned int insn;
- int i, frame_size;
+ *real_pc = funaddr;
+ *bp_addr = entry_point_address ();
+ return sp;
+}
+
+/* Implement the "cannot_fetch_register" gdbarch method. */
- if ((frame_size = arc_get_frame_setup (pc)) < 0)
- return (pc);
+static int
+arc_cannot_fetch_register (struct gdbarch *gdbarch, int regnum)
+{
+ /* Assume that register is readable if it is unknown. LIMM and RESERVED are
+ not real registers, but specific register numbers. They are available as
+ regnums to align architectural register numbers with GDB internal regnums,
+ but they shouldn't appear in target descriptions generated by
+ GDB-servers. */
+ switch (regnum)
+ {
+ case ARC_RESERVED_REGNUM:
+ case ARC_LIMM_REGNUM:
+ return true;
+ default:
+ return false;
+ }
+}
- if (frameless_p)
- return frame_size == 0 ? pc : codestream_tell ();
+/* Implement the "cannot_store_register" gdbarch method. */
- /* Skip over register saves. */
- for (i = 0; i < 8; i++)
+static int
+arc_cannot_store_register (struct gdbarch *gdbarch, int regnum)
+{
+ /* Assume that register is writable if it is unknown. See comment in
+ arc_cannot_fetch_register about LIMM and RESERVED. */
+ switch (regnum)
{
- insn = codestream_peek ();
- if ((insn & BUILD_INSN (-1, 0, -1, 0, 0))
- != BUILD_INSN (2, 0, SP_REGNUM, 0, 0))
- break; /* not st insn */
- if (! ARC_CALL_SAVED_REG (X_C (insn)))
- break;
- codestream_get ();
+ case ARC_RESERVED_REGNUM:
+ case ARC_LIMM_REGNUM:
+ case ARC_PCL_REGNUM:
+ return true;
+ default:
+ return false;
}
+}
+
+/* Get the return value of a function from the registers/memory used to
+ return it, according to the convention used by the ABI - 4-bytes values are
+ in the R0, while 8-byte values are in the R0-R1.
+
+ TODO: This implementation ignores the case of "complex double", where
+ according to ABI, value is returned in the R0-R3 registers.
+
+ TYPE is a returned value's type. VALBUF is a buffer for the returned
+ value. */
+
+static void
+arc_extract_return_value (struct gdbarch *gdbarch, struct type *type,
+ struct regcache *regcache, gdb_byte *valbuf)
+{
+ unsigned int len = TYPE_LENGTH (type);
+
+ if (arc_debug)
+ debug_printf ("arc: extract_return_value\n");
+
+ if (len <= ARC_REGISTER_SIZE)
+ {
+ ULONGEST val;
- return codestream_tell ();
+ /* Get the return value from one register. */
+ regcache_cooked_read_unsigned (regcache, ARC_R0_REGNUM, &val);
+ store_unsigned_integer (valbuf, (int) len,
+ gdbarch_byte_order (gdbarch), val);
+
+ if (arc_debug)
+ debug_printf ("arc: returning 0x%s\n", phex (val, ARC_REGISTER_SIZE));
+ }
+ else if (len <= ARC_REGISTER_SIZE * 2)
+ {
+ ULONGEST low, high;
+
+ /* Get the return value from two registers. */
+ regcache_cooked_read_unsigned (regcache, ARC_R0_REGNUM, &low);
+ regcache_cooked_read_unsigned (regcache, ARC_R1_REGNUM, &high);
+
+ store_unsigned_integer (valbuf, ARC_REGISTER_SIZE,
+ gdbarch_byte_order (gdbarch), low);
+ store_unsigned_integer (valbuf + ARC_REGISTER_SIZE,
+ (int) len - ARC_REGISTER_SIZE,
+ gdbarch_byte_order (gdbarch), high);
+
+ if (arc_debug)
+ debug_printf ("arc: returning 0x%s%s\n",
+ phex (high, ARC_REGISTER_SIZE),
+ phex (low, ARC_REGISTER_SIZE));
+ }
+ else
+ error (_("arc: extract_return_value: type length %u too large"), len);
}
-/* Return the return address for a frame.
- This is used to implement FRAME_SAVED_PC.
- This is taken from frameless_look_for_prologue. */
-CORE_ADDR
-arc_frame_saved_pc (frame)
- struct frame_info *frame;
+/* Store the return value of a function into the registers/memory used to
+ return it, according to the convention used by the ABI.
+
+ TODO: This implementation ignores the case of "complex double", where
+ according to ABI, value is returned in the R0-R3 registers.
+
+ TYPE is a returned value's type. VALBUF is a buffer with the value to
+ return. */
+
+static void
+arc_store_return_value (struct gdbarch *gdbarch, struct type *type,
+ struct regcache *regcache, const gdb_byte *valbuf)
+{
+ unsigned int len = TYPE_LENGTH (type);
+
+ if (arc_debug)
+ debug_printf ("arc: store_return_value\n");
+
+ if (len <= ARC_REGISTER_SIZE)
+ {
+ ULONGEST val;
+
+ /* Put the return value into one register. */
+ val = extract_unsigned_integer (valbuf, (int) len,
+ gdbarch_byte_order (gdbarch));
+ regcache_cooked_write_unsigned (regcache, ARC_R0_REGNUM, val);
+
+ if (arc_debug)
+ debug_printf ("arc: storing 0x%s\n", phex (val, ARC_REGISTER_SIZE));
+ }
+ else if (len <= ARC_REGISTER_SIZE * 2)
+ {
+ ULONGEST low, high;
+
+ /* Put the return value into two registers. */
+ low = extract_unsigned_integer (valbuf, ARC_REGISTER_SIZE,
+ gdbarch_byte_order (gdbarch));
+ high = extract_unsigned_integer (valbuf + ARC_REGISTER_SIZE,
+ (int) len - ARC_REGISTER_SIZE,
+ gdbarch_byte_order (gdbarch));
+
+ regcache_cooked_write_unsigned (regcache, ARC_R0_REGNUM, low);
+ regcache_cooked_write_unsigned (regcache, ARC_R1_REGNUM, high);
+
+ if (arc_debug)
+ debug_printf ("arc: storing 0x%s%s\n",
+ phex (high, ARC_REGISTER_SIZE),
+ phex (low, ARC_REGISTER_SIZE));
+ }
+ else
+ error (_("arc_store_return_value: type length too large."));
+}
+
+/* Implement the "get_longjmp_target" gdbarch method. */
+
+static int
+arc_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
{
- CORE_ADDR func_start;
- unsigned int insn;
+ if (arc_debug)
+ debug_printf ("arc: get_longjmp_target\n");
+
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int pc_offset = tdep->jb_pc * ARC_REGISTER_SIZE;
+ gdb_byte buf[ARC_REGISTER_SIZE];
+ CORE_ADDR jb_addr = get_frame_register_unsigned (frame, ARC_FIRST_ARG_REGNUM);
+
+ if (target_read_memory (jb_addr + pc_offset, buf, ARC_REGISTER_SIZE))
+ return 0; /* Failed to read from memory. */
- func_start = get_pc_function_start (frame->pc) + FUNCTION_START_OFFSET;
- if (func_start == 0)
+ *pc = extract_unsigned_integer (buf, ARC_REGISTER_SIZE,
+ gdbarch_byte_order (gdbarch));
+ return 1;
+}
+
+/* Implement the "return_value" gdbarch method. */
+
+static enum return_value_convention
+arc_return_value (struct gdbarch *gdbarch, struct value *function,
+ struct type *valtype, struct regcache *regcache,
+ gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+ /* If the return type is a struct, or a union, or would occupy more than two
+ registers, the ABI uses the "struct return convention": the calling
+ 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
+ || TYPE_LENGTH (valtype) > 2 * ARC_REGISTER_SIZE);
+
+ if (arc_debug)
+ debug_printf ("arc: return_value (readbuf = %s, writebuf = %s)\n",
+ host_address_to_string (readbuf),
+ host_address_to_string (writebuf));
+
+ if (writebuf != NULL)
+ {
+ /* Case 1. GDB should not ask us to set a struct return value: it
+ should know the struct return location and write the value there
+ itself. */
+ gdb_assert (!is_struct_return);
+ arc_store_return_value (gdbarch, valtype, regcache, writebuf);
+ }
+ else if (readbuf != NULL)
{
- /* Best guess. */
- return ARC_PC_TO_REAL_ADDRESS (read_memory_integer (FRAME_FP (frame) + 4, 4));
+ /* Case 2. GDB should not ask us to get a struct return value: it
+ should know the struct return location and read the value from there
+ itself. */
+ gdb_assert (!is_struct_return);
+ arc_extract_return_value (gdbarch, valtype, regcache, readbuf);
}
- /* The authority for what appears here is the home-grown ABI.
- The most recent version is 1.2. */
+ return (is_struct_return
+ ? RETURN_VALUE_STRUCT_CONVENTION
+ : RETURN_VALUE_REGISTER_CONVENTION);
+}
- insn = setup_prologue_scan (func_start);
+/* Return the base address of the frame. For ARC, the base address is the
+ frame pointer. */
- /* First insn may be "sub sp,sp,N" if stdarg fn. */
- if ((insn & BUILD_INSN (-1, -1, -1, -1, 0))
- == BUILD_INSN (10, SP_REGNUM, SP_REGNUM, SHIMM_REGNUM, 0))
- insn = codestream_get ();
+static CORE_ADDR
+arc_frame_base_address (struct frame_info *this_frame, void **prologue_cache)
+{
+ return (CORE_ADDR) get_frame_register_unsigned (this_frame, ARC_FP_REGNUM);
+}
- /* If the next insn is "st blink,[sp,4]" we can get blink from there.
- Otherwise this is a leaf function and we can use blink. Note that
- this still allows for the case where a leaf function saves/clobbers/
- restores blink. */
+/* Helper function that returns valid pv_t for an instruction operand:
+ either a register or a constant. */
- if ((insn & BUILD_INSN (-1, 0, -1, -1, -1)) /* st blink,[sp,4] */
- != BUILD_INSN (2, 0, SP_REGNUM, BLINK_REGNUM, 4))
- return ARC_PC_TO_REAL_ADDRESS (read_register (BLINK_REGNUM));
+static pv_t
+arc_pv_get_operand (pv_t *regs, const struct arc_instruction &insn, int operand)
+{
+ if (insn.operands[operand].kind == ARC_OPERAND_KIND_REG)
+ return regs[insn.operands[operand].value];
else
- return ARC_PC_TO_REAL_ADDRESS (read_memory_integer (FRAME_FP (frame) + 4, 4));
+ return pv_constant (arc_insn_get_operand_value (insn, operand));
}
-/*
- * Parse the first few instructions of the function to see
- * what registers were stored.
- *
- * The startup sequence can be at the start of the function.
- * 'st blink,[sp+4], st fp,[sp], mov fp,sp'
- *
- * Local space is allocated just below by sub sp,sp,nnn.
- * Next, the registers used by this function are stored (as offsets from sp).
- */
+/* Determine whether the given disassembled instruction may be part of a
+ function prologue. If it is, the information in the frame unwind cache will
+ be updated. */
-void
-frame_find_saved_regs (fip, fsrp)
- struct frame_info *fip;
- struct frame_saved_regs *fsrp;
+static bool
+arc_is_in_prologue (struct gdbarch *gdbarch, const struct arc_instruction &insn,
+ pv_t *regs, struct pv_area *stack)
{
- long locals;
- unsigned int insn;
- CORE_ADDR dummy_bottom;
- CORE_ADDR adr;
- int i, regnum, offset;
+ /* It might be that currently analyzed address doesn't contain an
+ instruction, hence INSN is not valid. It likely means that address points
+ to a data, non-initialized memory, or middle of a 32-bit instruction. In
+ practice this may happen if GDB connects to a remote target that has
+ non-zeroed memory. GDB would read PC value and would try to analyze
+ prologue, but there is no guarantee that memory contents at the address
+ specified in PC is address is a valid instruction. There is not much that
+ that can be done about that. */
+ if (!insn.valid)
+ return false;
+
+ /* Branch/jump or a predicated instruction. */
+ if (insn.is_control_flow || insn.condition_code != ARC_CC_AL)
+ return false;
+
+ /* 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
+ stored. */
+ gdb_assert (insn.operands_count > 0);
+
+ /* Store at some constant address. */
+ if (insn.operands_count > 1
+ && insn.operands[1].kind != ARC_OPERAND_KIND_REG)
+ return false;
+
+ /* Writeback modes:
+ Mode Address used Writeback value
+ --------------------------------------------------
+ No reg + offset no
+ A/AW reg + offset reg + offset
+ AB reg reg + offset
+ AS reg + (offset << scaling) no
+
+ "PUSH reg" is an alias to "ST.AW reg, [SP, -4]" encoding. However
+ 16-bit PUSH_S is a distinct instruction encoding, where offset and
+ base register are implied through opcode. */
+
+ /* Register with base memory address. */
+ int base_reg = arc_insn_get_memory_base_reg (insn);
+
+ /* Address where to write. arc_insn_get_memory_offset returns scaled
+ value for ARC_WRITEBACK_AS. */
+ pv_t addr;
+ if (insn.writeback_mode == ARC_WRITEBACK_AB)
+ addr = regs[base_reg];
+ else
+ addr = pv_add_constant (regs[base_reg],
+ arc_insn_get_memory_offset (insn));
- memset (fsrp, 0, sizeof *fsrp);
+ if (stack->store_would_trash (addr))
+ return false;
- /* If frame is the end of a dummy, compute where the beginning would be. */
- dummy_bottom = fip->frame - 4 - REGISTER_BYTES - CALL_DUMMY_LENGTH;
+ if (insn.data_size_mode != ARC_SCALING_D)
+ {
+ /* Find the value being stored. */
+ pv_t store_value = arc_pv_get_operand (regs, insn, 0);
+
+ /* What is the size of a the stored value? */
+ CORE_ADDR size;
+ if (insn.data_size_mode == ARC_SCALING_B)
+ size = 1;
+ else if (insn.data_size_mode == ARC_SCALING_H)
+ size = 2;
+ else
+ size = ARC_REGISTER_SIZE;
- /* Check if the PC is in the stack, in a dummy frame. */
- if (dummy_bottom <= fip->pc && fip->pc <= fip->frame)
+ stack->store (addr, size, store_value);
+ }
+ else
+ {
+ if (insn.operands[0].kind == ARC_OPERAND_KIND_REG)
+ {
+ /* 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];
+ 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));
+ stack->store (addr, ARC_REGISTER_SIZE * 2, store_value);
+ }
+ }
+
+ /* Is base register updated? */
+ if (insn.writeback_mode == ARC_WRITEBACK_A
+ || insn.writeback_mode == ARC_WRITEBACK_AB)
+ regs[base_reg] = pv_add_constant (regs[base_reg],
+ arc_insn_get_memory_offset (insn));
+
+ return true;
+ }
+ else if (insn.insn_class == MOVE)
{
- /* all regs were saved by push_call_dummy () */
- adr = fip->frame;
- for (i = 0; i < NUM_REGS; i++)
+ gdb_assert (insn.operands_count == 2);
+
+ /* Destination argument can be "0", so nothing will happen. */
+ if (insn.operands[0].kind == ARC_OPERAND_KIND_REG)
{
- adr -= REGISTER_RAW_SIZE (i);
- fsrp->regs[i] = adr;
+ int dst_regnum = insn.operands[0].value;
+ regs[dst_regnum] = arc_pv_get_operand (regs, insn, 1);
}
- return;
+ return true;
}
+ else if (insn.insn_class == SUB)
+ {
+ gdb_assert (insn.operands_count == 3);
- locals = arc_get_frame_setup (get_pc_function_start (fip->pc));
+ /* SUB 0,b,c. */
+ if (insn.operands[0].kind != ARC_OPERAND_KIND_REG)
+ return true;
- if (locals >= 0)
+ int dst_regnum = insn.operands[0].value;
+ regs[dst_regnum] = pv_subtract (arc_pv_get_operand (regs, insn, 1),
+ arc_pv_get_operand (regs, insn, 2));
+ return true;
+ }
+ else if (insn.insn_class == ENTER)
{
- /* Set `adr' to the value of `sp'. */
- adr = fip->frame - locals;
- for (i = 0; i < 8; i++)
+ /* ENTER_S is a prologue-in-instruction - it saves all callee-saved
+ registers according to given arguments thus greatly reducing code
+ size. Which registers will be actually saved depends on arguments.
+
+ ENTER_S {R13-...,FP,BLINK} stores registers in following order:
+
+ new SP ->
+ BLINK
+ R13
+ R14
+ R15
+ ...
+ FP
+ old SP ->
+
+ There are up to three arguments for this opcode, as presented by ARC
+ disassembler:
+ 1) amount of general-purpose registers to be saved - this argument is
+ always present even when it is 0;
+ 2) FP register number (27) if FP has to be stored, otherwise argument
+ is not present;
+ 3) BLINK register number (31) if BLINK has to be stored, otherwise
+ argument is not present. If both FP and BLINK are stored, then FP
+ is present before BLINK in argument list. */
+ gdb_assert (insn.operands_count > 0);
+
+ int regs_saved = arc_insn_get_operand_value (insn, 0);
+
+ bool is_fp_saved;
+ if (insn.operands_count > 1)
+ is_fp_saved = (insn.operands[1].value == ARC_FP_REGNUM);
+ else
+ is_fp_saved = false;
+
+ bool is_blink_saved;
+ if (insn.operands_count > 1)
+ is_blink_saved = (insn.operands[insn.operands_count - 1].value
+ == ARC_BLINK_REGNUM);
+ else
+ is_blink_saved = false;
+
+ /* Amount of bytes to be allocated to store specified registers. */
+ CORE_ADDR st_size = ((regs_saved + is_fp_saved + is_blink_saved)
+ * ARC_REGISTER_SIZE);
+ pv_t new_sp = pv_add_constant (regs[ARC_SP_REGNUM], -st_size);
+
+ /* Assume that if the last register (closest to new SP) can be written,
+ then it is possible to write all of them. */
+ if (stack->store_would_trash (new_sp))
+ return false;
+
+ /* Current store address. */
+ pv_t addr = regs[ARC_SP_REGNUM];
+
+ if (is_fp_saved)
{
- insn = codestream_get ();
- if ((insn & BUILD_INSN (-1, 0, -1, 0, 0))
- != BUILD_INSN (2, 0, SP_REGNUM, 0, 0))
- break;
- regnum = X_C (insn);
- offset = X_D (insn);
- fsrp->regs[regnum] = adr + offset;
+ addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
+ 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);
+ stack->store (addr, ARC_REGISTER_SIZE, regs[i]);
}
+
+ if (is_blink_saved)
+ {
+ addr = pv_add_constant (addr, -ARC_REGISTER_SIZE);
+ stack->store (addr, ARC_REGISTER_SIZE,
+ regs[ARC_BLINK_REGNUM]);
+ }
+
+ gdb_assert (pv_is_identical (addr, new_sp));
+
+ regs[ARC_SP_REGNUM] = new_sp;
+
+ if (is_fp_saved)
+ regs[ARC_FP_REGNUM] = regs[ARC_SP_REGNUM];
+
+ return true;
}
- fsrp->regs[PC_REGNUM] = fip->frame + 4;
- fsrp->regs[FP_REGNUM] = fip->frame;
+ /* Some other architectures, like nds32 or arm, try to continue as far as
+ possible when building a prologue cache (as opposed to when skipping
+ prologue), so that cache will be as full as possible. However current
+ code for ARC doesn't recognize some instructions that may modify SP, like
+ ADD, AND, OR, etc, hence there is no way to guarantee that SP wasn't
+ clobbered by the skipped instruction. Potential existence of extension
+ instruction, which may do anything they want makes this even more complex,
+ so it is just better to halt on a first unrecognized instruction. */
+
+ return false;
}
-void
-push_dummy_frame ()
+/* Copy of gdb_buffered_insn_length_fprintf from disasm.c. */
+
+static int ATTRIBUTE_PRINTF (2, 3)
+arc_fprintf_disasm (void *stream, const char *format, ...)
{
- CORE_ADDR sp = read_register (SP_REGNUM);
- int regnum;
- char regbuf[MAX_REGISTER_RAW_SIZE];
+ return 0;
+}
- read_register_gen (PC_REGNUM, regbuf);
- write_memory (sp+4, regbuf, REGISTER_SIZE);
- read_register_gen (FP_REGNUM, regbuf);
- write_memory (sp, regbuf, REGISTER_SIZE);
- write_register (FP_REGNUM, sp);
- for (regnum = 0; regnum < NUM_REGS; regnum++)
+struct disassemble_info
+arc_disassemble_info (struct gdbarch *gdbarch)
+{
+ struct disassemble_info di;
+ init_disassemble_info (&di, &null_stream, arc_fprintf_disasm);
+ di.arch = gdbarch_bfd_arch_info (gdbarch)->arch;
+ di.mach = gdbarch_bfd_arch_info (gdbarch)->mach;
+ di.endian = gdbarch_byte_order (gdbarch);
+ di.read_memory_func = [](bfd_vma memaddr, gdb_byte *myaddr,
+ unsigned int len, struct disassemble_info *info)
{
- read_register_gen (regnum, regbuf);
- sp = push_bytes (sp, regbuf, REGISTER_RAW_SIZE (regnum));
- }
- sp += (2*REGISTER_SIZE);
- write_register (SP_REGNUM, sp);
+ return target_read_code (memaddr, myaddr, len);
+ };
+ return di;
}
-void
-pop_frame ()
-{
- struct frame_info *frame = get_current_frame ();
- CORE_ADDR fp;
- int regnum;
- struct frame_saved_regs fsr;
- char regbuf[MAX_REGISTER_RAW_SIZE];
-
- fp = FRAME_FP (frame);
- get_frame_saved_regs (frame, &fsr);
- for (regnum = 0; regnum < NUM_REGS; regnum++)
- {
- CORE_ADDR adr;
- adr = fsr.regs[regnum];
- if (adr)
+/* Analyze the prologue and update the corresponding frame cache for the frame
+ unwinder for unwinding frames that doesn't have debug info. In such
+ situation GDB attempts to parse instructions in the prologue to understand
+ where each register is saved.
+
+ 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"
+ prologue looks like this:
+
+ sub sp,sp,<imm> ; Space for variadic arguments.
+ push blink ; Store return address.
+ push r13 ; Store callee saved registers (up to R26/GP).
+ push r14
+ push fp ; Store frame pointer.
+ mov fp,sp ; Update frame pointer.
+ sub sp,sp,<imm> ; Create space for local vars on the stack.
+
+ Depending on compiler options lots of things may change:
+
+ 1) BLINK is not saved in leaf functions.
+ 2) Frame pointer is not saved and updated if -fomit-frame-pointer is used.
+ 3) 16-bit versions of those instructions may be used.
+ 4) Instead of a sequence of several push'es, compiler may instead prefer to
+ do one subtract on stack pointer and then store registers using normal
+ store, that doesn't update SP. Like this:
+
+
+ sub sp,sp,8 ; Create space for calee-saved registers.
+ st r13,[sp,4] ; Store callee saved registers (up to R26/GP).
+ st r14,[sp,0]
+
+ 5) ENTER_S instruction can encode most of prologue sequence in one
+ instruction (except for those subtracts for variadic arguments and local
+ variables).
+ 6) GCC may use "millicode" functions from libgcc to store callee-saved
+ registers with minimal code-size requirements. This function currently
+ doesn't support this.
+
+ ENTRYPOINT is a function entry point where prologue starts.
+
+ LIMIT_PC is a maximum possible end address of prologue (meaning address
+ of first instruction after the prologue). It might also point to the middle
+ of prologue if execution has been stopped by the breakpoint at this address
+ - in this case debugger should analyze prologue only up to this address,
+ because further instructions haven't been executed yet.
+
+ Returns address of the first instruction after the prologue. */
+
+static CORE_ADDR
+arc_analyze_prologue (struct gdbarch *gdbarch, const CORE_ADDR entrypoint,
+ const CORE_ADDR limit_pc, struct arc_frame_cache *cache)
+{
+ if (arc_debug)
+ debug_printf ("arc: analyze_prologue (entrypoint=%s, limit_pc=%s)\n",
+ paddress (gdbarch, entrypoint),
+ paddress (gdbarch, limit_pc));
+
+ /* Prologue values. Only core registers can be stored. */
+ pv_t regs[ARC_LAST_CORE_REGNUM + 1];
+ for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+ regs[i] = pv_register (i, 0);
+ pv_area stack (ARC_SP_REGNUM, gdbarch_addr_bit (gdbarch));
+
+ CORE_ADDR current_prologue_end = entrypoint;
+
+ /* Look at each instruction in the prologue. */
+ while (current_prologue_end < limit_pc)
+ {
+ struct arc_instruction insn;
+ struct disassemble_info di = arc_disassemble_info (gdbarch);
+ arc_insn_decode (current_prologue_end, &di, arc_delayed_print_insn,
+ &insn);
+
+ if (arc_debug >= 2)
+ arc_insn_dump (insn);
+
+ /* 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))
{
- read_memory (adr, regbuf, REGISTER_RAW_SIZE (regnum));
- write_register_bytes (REGISTER_BYTE (regnum), regbuf,
- REGISTER_RAW_SIZE (regnum));
+ /* Found an instruction that is not in the prologue. */
+ if (arc_debug)
+ debug_printf ("arc: End of prologue reached at address %s\n",
+ paddress (gdbarch, insn.address));
+ break;
}
+
+ current_prologue_end = arc_insn_get_linear_next_pc (insn);
}
- write_register (FP_REGNUM, read_memory_integer (fp, 4));
- write_register (PC_REGNUM, read_memory_integer (fp + 4, 4));
- write_register (SP_REGNUM, fp + 8);
- flush_cached_frames ();
-}
-\f
-/* Simulate single-step. */
-
-typedef enum
-{
- NORMAL4, /* a normal 4 byte insn */
- NORMAL8, /* a normal 8 byte insn */
- BRANCH4, /* a 4 byte branch insn, including ones without delay slots */
- BRANCH8, /* an 8 byte branch insn, including ones with delay slots */
-} insn_type;
-
-/* Return the type of INSN and store in TARGET the destination address of a
- branch if this is one. */
-/* ??? Need to verify all cases are properly handled. */
-
-static insn_type
-get_insn_type (insn, pc, target)
- unsigned long insn;
- CORE_ADDR pc, *target;
-{
- unsigned long limm;
-
- switch (insn >> 27)
- {
- case 0 : case 1 : case 2 : /* load/store insns */
- if (LIMM_P (X_A (insn))
- || LIMM_P (X_B (insn))
- || LIMM_P (X_C (insn)))
- return NORMAL8;
- return NORMAL4;
- case 4 : case 5 : case 6 : /* branch insns */
- *target = pc + 4 + X_L (insn);
- /* ??? It isn't clear that this is always the right answer.
- The problem occurs when the next insn is an 8 byte insn. If the
- branch is conditional there's no worry as there shouldn't be an 8
- byte insn following. The programmer may be cheating if s/he knows
- the branch will never be taken, but we don't deal with that.
- Note that the programmer is also allowed to play games by putting
- an insn with long immediate data in the delay slot and then duplicate
- the long immediate data at the branch target. Ugh! */
- if (X_N (insn) == 0)
- return BRANCH4;
- return BRANCH8;
- case 7 : /* jump insns */
- if (LIMM_P (X_B (insn)))
+
+ if (cache != NULL)
+ {
+ /* Figure out if it is a frame pointer or just a stack pointer. */
+ if (pv_is_register (regs[ARC_FP_REGNUM], ARC_SP_REGNUM))
{
- limm = read_memory_integer (pc + 4, 4);
- *target = ARC_PC_TO_REAL_ADDRESS (limm);
- return BRANCH8;
+ cache->frame_base_reg = ARC_FP_REGNUM;
+ cache->frame_base_offset = -regs[ARC_FP_REGNUM].k;
}
- if (SHIMM_P (X_B (insn)))
- *target = ARC_PC_TO_REAL_ADDRESS (X_D (insn));
else
- *target = ARC_PC_TO_REAL_ADDRESS (read_register (X_B (insn)));
- if (X_Q (insn) == 0 && X_N (insn) == 0)
- return BRANCH4;
- return BRANCH8;
- default : /* arithmetic insns, etc. */
- if (LIMM_P (X_A (insn))
- || LIMM_P (X_B (insn))
- || LIMM_P (X_C (insn)))
- return NORMAL8;
- return NORMAL4;
+ {
+ cache->frame_base_reg = ARC_SP_REGNUM;
+ cache->frame_base_offset = -regs[ARC_SP_REGNUM].k;
+ }
+
+ /* Assign offset from old SP to all saved registers. */
+ for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+ {
+ CORE_ADDR offset;
+ if (stack.find_reg (gdbarch, i, &offset))
+ cache->saved_regs[i].addr = offset;
+ }
}
+
+ return current_prologue_end;
}
-/* single_step() is called just before we want to resume the inferior, if we
- want to single-step it but there is no hardware or kernel single-step
- support. We find all the possible targets of the coming instruction and
- breakpoint them.
+/* Estimated maximum prologue length in bytes. This should include:
+ 1) Store instruction for each callee-saved register (R25 - R13 + 1)
+ 2) Two instructions for FP
+ 3) One for BLINK
+ 4) Three substract instructions for SP (for variadic args, for
+ callee saved regs and for local vars) and assuming that those SUB use
+ long-immediate (hence double length).
+ 5) Stores of arguments registers are considered part of prologue too
+ (R7 - R1 + 1).
+ This is quite an extreme case, because even with -O0 GCC will collapse first
+ two SUBs into one and long immediate values are quite unlikely to appear in
+ this case, but still better to overshoot a bit - prologue analysis will
+ anyway stop at the first instruction that doesn't fit prologue, so this
+ limit will be rarely reached. */
+
+const static int MAX_PROLOGUE_LENGTH
+ = 4 * (ARC_R25_REGNUM - ARC_R13_REGNUM + 1 + 2 + 1 + 6
+ + ARC_LAST_ARG_REGNUM - ARC_FIRST_ARG_REGNUM + 1);
+
+/* Implement the "skip_prologue" gdbarch method.
+
+ Skip the prologue for the function at PC. This is done by checking from
+ the line information read from the DWARF, if possible; otherwise, we scan
+ the function prologue to find its end. */
+
+static CORE_ADDR
+arc_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+ if (arc_debug)
+ debug_printf ("arc: skip_prologue\n");
- single_step is also called just after the inferior stops. If we had
- set up a simulated single-step, we undo our damage. */
+ CORE_ADDR func_addr;
+ const char *func_name;
-void
-arc_software_single_step (ignore, insert_breakpoints_p)
- enum target_signal ignore; /* sig but we don't need it */
- int insert_breakpoints_p;
+ /* See what the symbol table says. */
+ if (find_pc_partial_function (pc, &func_name, &func_addr, NULL))
+ {
+ /* Found a function. */
+ CORE_ADDR postprologue_pc
+ = skip_prologue_using_sal (gdbarch, func_addr);
+
+ if (postprologue_pc != 0)
+ return std::max (pc, postprologue_pc);
+ }
+
+ /* No prologue info in symbol table, have to analyze prologue. */
+
+ /* Find an upper limit on the function prologue using the debug
+ information. If there is no debug information about prologue end, then
+ skip_prologue_using_sal will return 0. */
+ CORE_ADDR limit_pc = skip_prologue_using_sal (gdbarch, pc);
+
+ /* If there is no debug information at all, it is required to give some
+ semi-arbitrary hard limit on amount of bytes to scan during prologue
+ analysis. */
+ if (limit_pc == 0)
+ limit_pc = pc + MAX_PROLOGUE_LENGTH;
+
+ /* Find the address of the first instruction after the prologue by scanning
+ through it - no other information is needed, so pass NULL as a cache. */
+ return arc_analyze_prologue (gdbarch, pc, limit_pc, NULL);
+}
+
+/* Implement the "print_insn" gdbarch method.
+
+ arc_get_disassembler () may return different functions depending on bfd
+ type, so it is not possible to pass print_insn directly to
+ set_gdbarch_print_insn (). Instead this wrapper function is used. It also
+ may be used by other functions to get disassemble_info for address. It is
+ important to note, that those print_insn from opcodes always print
+ instruction to the stream specified in the INFO. If this is not desired,
+ then either `print_insn` function in INFO should be set to some function
+ that will not print, or `stream` should be different from standard
+ gdb_stdlog. */
+
+int
+arc_delayed_print_insn (bfd_vma addr, struct disassemble_info *info)
+{
+ /* Standard BFD "machine number" field allows libocodes 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.
+
+ ARC supports both big- and little-endian. However, instructions for
+ little-endian processors are encoded in the middle-endian: half-words are
+ in big-endian, while bytes inside the half-words are in little-endian; data
+ is represented in the "normal" little-endian. Big-endian processors treat
+ data and code identically.
+
+ Assuming the number 0x01020304, it will be presented this way:
+
+ Address : N N+1 N+2 N+3
+ little-endian : 0x04 0x03 0x02 0x01
+ big-endian : 0x01 0x02 0x03 0x04
+ ARC middle-endian : 0x02 0x01 0x04 0x03
+ */
+
+static const gdb_byte arc_brk_s_be[] = { 0x7f, 0xff };
+static const gdb_byte arc_brk_s_le[] = { 0xff, 0x7f };
+static const gdb_byte arc_brk_be[] = { 0x25, 0x6f, 0x00, 0x3f };
+static const gdb_byte arc_brk_le[] = { 0x6f, 0x25, 0x3f, 0x00 };
+
+/* For ARC ELF, breakpoint uses the 16-bit BRK_S instruction, which is 0x7fff
+ (little endian) or 0xff7f (big endian). We used to insert BRK_S even
+ instead of 32-bit instructions, which works mostly ok, unless breakpoint is
+ inserted into delay slot instruction. In this case if branch is taken
+ BLINK value will be set to address of instruction after delay slot, however
+ if we replaced 32-bit instruction in delay slot with 16-bit long BRK_S,
+ then BLINK value will have an invalid value - it will point to the address
+ after the BRK_S (which was there at the moment of branch execution) while
+ it should point to the address after the 32-bit long instruction. To avoid
+ such issues this function disassembles instruction at target location and
+ evaluates it value.
+
+ ARC 600 supports only 16-bit BRK_S.
+
+ NB: Baremetal GDB uses BRK[_S], while user-space GDB uses TRAP_S. BRK[_S]
+ is much better because it doesn't commit unlike TRAP_S, so it can be set in
+ delay slots; however it cannot be used in user-mode, hence usage of TRAP_S
+ in GDB for user-space. */
+
+/* Implement the "breakpoint_kind_from_pc" gdbarch method. */
+
+static int
+arc_breakpoint_kind_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr)
{
- static CORE_ADDR next_pc, target;
- static int brktrg_p;
- typedef char binsn_quantum[BREAKPOINT_MAX];
- static binsn_quantum break_mem[2];
+ size_t length_with_limm = gdb_insn_length (gdbarch, *pcptr);
+
+ /* Replace 16-bit instruction with BRK_S, replace 32-bit instructions with
+ BRK. LIMM is part of instruction length, so it can be either 4 or 8
+ bytes for 32-bit instructions. */
+ if ((length_with_limm == 4 || length_with_limm == 8)
+ && !arc_mach_is_arc600 (gdbarch))
+ return sizeof (arc_brk_le);
+ else
+ return sizeof (arc_brk_s_le);
+}
+
+/* Implement the "sw_breakpoint_from_kind" gdbarch method. */
- if (insert_breakpoints_p)
+static const gdb_byte *
+arc_sw_breakpoint_from_kind (struct gdbarch *gdbarch, int kind, int *size)
+{
+ *size = kind;
+
+ if (kind == sizeof (arc_brk_le))
+ {
+ return ((gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
+ ? arc_brk_be
+ : arc_brk_le);
+ }
+ else
{
- insn_type type;
- CORE_ADDR pc;
- unsigned long insn;
+ return ((gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
+ ? arc_brk_s_be
+ : arc_brk_s_le);
+ }
+}
- pc = read_register (PC_REGNUM);
- insn = read_memory_integer (pc, 4);
- type = get_insn_type (insn, pc, &target);
+/* Implement the "frame_align" gdbarch method. */
- /* Always set a breakpoint for the insn after the branch. */
- next_pc = pc + ((type == NORMAL8 || type == BRANCH8) ? 8 : 4);
- target_insert_breakpoint (next_pc, break_mem[0]);
+static CORE_ADDR
+arc_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp)
+{
+ return align_down (sp, 4);
+}
- brktrg_p = 0;
+/* Dump the frame info. Used for internal debugging only. */
- if ((type == BRANCH4 || type == BRANCH8)
- /* Watch out for branches to the following location.
- We just stored a breakpoint there and another call to
- target_insert_breakpoint will think the real insn is the
- breakpoint we just stored there. */
- && target != next_pc)
- {
- brktrg_p = 1;
- target_insert_breakpoint (target, break_mem[1]);
- }
+static void
+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);
+ debug_printf ("arc: prev_sp = %s\n", paddress (gdbarch, cache->prev_sp));
+ debug_printf ("arc: frame_base_reg = %i\n", cache->frame_base_reg);
+ debug_printf ("arc: frame_base_offset = %s\n",
+ plongest (cache->frame_base_offset));
+
+ for (int i = 0; i <= ARC_BLINK_REGNUM; i++)
+ {
+ if (trad_frame_addr_p (cache->saved_regs, i))
+ debug_printf ("arc: saved register %s at %s %s\n",
+ gdbarch_register_name (gdbarch, i),
+ (addresses_known) ? "address" : "offset",
+ paddress (gdbarch, cache->saved_regs[i].addr));
+ }
+}
+
+/* Frame unwinder for normal frames. */
+
+static struct arc_frame_cache *
+arc_make_frame_cache (struct frame_info *this_frame)
+{
+ if (arc_debug)
+ debug_printf ("arc: frame_cache\n");
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
+
+ CORE_ADDR block_addr = get_frame_address_in_block (this_frame);
+ CORE_ADDR entrypoint, prologue_end;
+ if (find_pc_partial_function (block_addr, NULL, &entrypoint, &prologue_end))
+ {
+ struct symtab_and_line sal = find_pc_line (entrypoint, 0);
+ CORE_ADDR prev_pc = get_frame_pc (this_frame);
+ if (sal.line == 0)
+ /* No line info so use current PC. */
+ prologue_end = prev_pc;
+ else if (sal.end < prologue_end)
+ /* The next line begins after the function end. */
+ prologue_end = sal.end;
+
+ prologue_end = std::min (prologue_end, prev_pc);
}
else
{
- /* Remove breakpoints. */
- target_remove_breakpoint (next_pc, break_mem[0]);
+ /* If find_pc_partial_function returned nothing then there is no symbol
+ information at all for this PC. Currently it is assumed in this case
+ that current PC is entrypoint to function and try to construct the
+ frame from that. This is, probably, suboptimal, for example ARM
+ assumes in this case that program is inside the normal frame (with
+ frame pointer). ARC, perhaps, should try to do the same. */
+ entrypoint = get_frame_register_unsigned (this_frame,
+ gdbarch_pc_regnum (gdbarch));
+ prologue_end = entrypoint + MAX_PROLOGUE_LENGTH;
+ }
- if (brktrg_p)
- target_remove_breakpoint (target, break_mem[1]);
+ /* Allocate new frame cache instance and space for saved register info.
+ FRAME_OBSTACK_ZALLOC will initialize fields to zeroes. */
+ struct arc_frame_cache *cache
+ = FRAME_OBSTACK_ZALLOC (struct arc_frame_cache);
+ cache->saved_regs = trad_frame_alloc_saved_regs (this_frame);
- /* Fix the pc. */
- stop_pc -= DECR_PC_AFTER_BREAK;
- write_pc (stop_pc);
+ arc_analyze_prologue (gdbarch, entrypoint, prologue_end, cache);
+
+ if (arc_debug)
+ arc_print_frame_cache (gdbarch, "after prologue", cache, false);
+
+ CORE_ADDR unwound_fb = get_frame_register_unsigned (this_frame,
+ cache->frame_base_reg);
+ if (unwound_fb == 0)
+ return cache;
+ cache->prev_sp = unwound_fb + cache->frame_base_offset;
+
+ for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+ {
+ if (trad_frame_addr_p (cache->saved_regs, i))
+ cache->saved_regs[i].addr += cache->prev_sp;
}
+
+ if (arc_debug)
+ arc_print_frame_cache (gdbarch, "after previous SP found", cache, true);
+
+ return cache;
}
-\f
-#ifdef GET_LONGJMP_TARGET
-/* Figure out where the longjmp will land. Slurp the args out of the stack.
- We expect the first arg to be a pointer to the jmp_buf structure from which
- we extract the pc (JB_PC) that we will land at. The pc is copied into PC.
- This routine returns true on success. */
-int
-get_longjmp_target(pc)
- CORE_ADDR *pc;
+/* Implement the "this_id" frame_unwind method. */
+
+static void
+arc_frame_this_id (struct frame_info *this_frame, void **this_cache,
+ struct frame_id *this_id)
{
- char buf[TARGET_PTR_BIT / TARGET_CHAR_BIT];
- CORE_ADDR sp, jb_addr;
+ if (arc_debug)
+ debug_printf ("arc: frame_this_id\n");
- sp = read_register (SP_REGNUM);
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
- if (target_read_memory (sp + SP_ARG0, /* Offset of first arg on stack */
- buf,
- TARGET_PTR_BIT / TARGET_CHAR_BIT))
- return 0;
+ if (*this_cache == NULL)
+ *this_cache = arc_make_frame_cache (this_frame);
+ struct arc_frame_cache *cache = (struct arc_frame_cache *) (*this_cache);
- jb_addr = extract_address (buf, TARGET_PTR_BIT / TARGET_CHAR_BIT);
+ CORE_ADDR stack_addr = cache->prev_sp;
- if (target_read_memory (jb_addr + JB_PC * JB_ELEMENT_SIZE, buf,
- TARGET_PTR_BIT / TARGET_CHAR_BIT))
- return 0;
+ /* There are 4 possible situation which decide how frame_id->code_addr is
+ evaluated:
- *pc = extract_address (buf, TARGET_PTR_BIT / TARGET_CHAR_BIT);
+ 1) Function is compiled with option -g. Then frame_id will be created
+ in dwarf_* function and not in this function. NB: even if target
+ binary is compiled with -g, some std functions like __start and _init
+ are not, so they still will follow one of the following choices.
- return 1;
+ 2) Function is compiled without -g and binary hasn't been stripped in
+ any way. In this case GDB still has enough information to evaluate
+ frame code_addr properly. This case is covered by call to
+ get_frame_func ().
+
+ 3) Binary has been striped with option -g (strip debug symbols). In
+ this case there is still enough symbols for get_frame_func () to work
+ properly, so this case is also covered by it.
+
+ 4) Binary has been striped with option -s (strip all symbols). In this
+ case GDB cannot get function start address properly, so we return current
+ PC value instead.
+ */
+ CORE_ADDR code_addr = get_frame_func (this_frame);
+ if (code_addr == 0)
+ code_addr = get_frame_register_unsigned (this_frame,
+ gdbarch_pc_regnum (gdbarch));
+
+ *this_id = frame_id_build (stack_addr, code_addr);
}
-#endif /* GET_LONGJMP_TARGET */
-\f
-/* Disassemble one instruction. */
-static int
-arc_print_insn (vma, info)
- bfd_vma vma;
- disassemble_info *info;
+/* Implement the "prev_register" frame_unwind method. */
+
+static struct value *
+arc_frame_prev_register (struct frame_info *this_frame,
+ void **this_cache, int regnum)
{
- static int current_mach;
- static int current_endian;
- static disassembler_ftype current_disasm;
+ if (*this_cache == NULL)
+ *this_cache = arc_make_frame_cache (this_frame);
+ struct arc_frame_cache *cache = (struct arc_frame_cache *) (*this_cache);
- if (current_disasm == NULL
- || arc_bfd_mach_type != current_mach
- || TARGET_BYTE_ORDER != current_endian)
- {
- current_mach = arc_bfd_mach_type;
- current_endian = TARGET_BYTE_ORDER;
- current_disasm = arc_get_disassembler (current_mach,
- current_endian == BIG_ENDIAN);
- }
+ struct gdbarch *gdbarch = get_frame_arch (this_frame);
- return (*current_disasm) (vma, info);
+ /* If we are asked to unwind the PC, then we need to return BLINK instead:
+ the saved value of PC points into this frame's function's prologue, not
+ the next frame's function's resume location. */
+ if (regnum == gdbarch_pc_regnum (gdbarch))
+ regnum = ARC_BLINK_REGNUM;
+
+ /* SP is a special case - we should return prev_sp, because
+ trad_frame_get_prev_register will return _current_ SP value.
+ Alternatively we could have stored cache->prev_sp in the cache->saved
+ regs, but here we follow the lead of AArch64, ARM and Xtensa and will
+ leave that logic in this function, instead of prologue analyzers. That I
+ think is a bit more clear as `saved_regs` should contain saved regs, not
+ computable.
+
+ Because value has been computed, "got_constant" should be used, so that
+ returned value will be a "not_lval" - immutable. */
+
+ if (regnum == gdbarch_sp_regnum (gdbarch))
+ return frame_unwind_got_constant (this_frame, regnum, cache->prev_sp);
+
+ return trad_frame_get_prev_register (this_frame, cache->saved_regs, regnum);
}
-\f
-/* Command to set cpu type. */
-void
-arc_set_cpu_type_command (args, from_tty)
- char *args;
- int from_tty;
+/* Implement the "init_reg" dwarf2_frame method. */
+
+static void
+arc_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum,
+ struct dwarf2_frame_state_reg *reg,
+ struct frame_info *info)
{
- int i;
+ if (regnum == gdbarch_pc_regnum (gdbarch))
+ /* The return address column. */
+ reg->how = DWARF2_FRAME_REG_RA;
+ else if (regnum == gdbarch_sp_regnum (gdbarch))
+ /* The call frame address. */
+ reg->how = DWARF2_FRAME_REG_CFA;
+}
+
+/* Structure defining the ARC ordinary frame unwind functions. Since we are
+ the fallback unwinder, we use the default frame sniffer, which always
+ accepts the frame. */
+
+static const struct frame_unwind arc_frame_unwind = {
+ NORMAL_FRAME,
+ default_frame_unwind_stop_reason,
+ arc_frame_this_id,
+ arc_frame_prev_register,
+ NULL,
+ default_frame_sniffer,
+ NULL,
+ NULL
+};
+
+
+static const struct frame_base arc_normal_base = {
+ &arc_frame_unwind,
+ arc_frame_base_address,
+ arc_frame_base_address,
+ arc_frame_base_address
+};
- if (tmp_arc_cpu_type == NULL || *tmp_arc_cpu_type == '\0')
+/* Initialize target description for the ARC.
+
+ Returns TRUE if input tdesc was valid and in this case it will assign TDESC
+ and TDESC_DATA output parameters. */
+
+static int
+arc_tdesc_init (struct gdbarch_info info, const struct target_desc **tdesc,
+ struct tdesc_arch_data **tdesc_data)
+{
+ if (arc_debug)
+ debug_printf ("arc: Target description initialization.\n");
+
+ const struct target_desc *tdesc_loc = info.target_desc;
+
+ /* Depending on whether this is ARCompact or ARCv2 we will assign
+ different default registers sets (which will differ in exactly two core
+ registers). GDB will also refuse to accept register feature from invalid
+ ISA - v2 features can be used only with v2 ARChitecture. We read
+ bfd_arch_info, which looks like to be a safe bet here, as it looks like it
+ is always initialized even when we don't pass any elf file to GDB at all
+ (it uses default arch in this case). Also GDB will call this function
+ multiple times, and if XML target description file contains architecture
+ specifications, then GDB will set this architecture to info.bfd_arch_info,
+ overriding value from ELF file if they are different. That means that,
+ where matters, this value is always our best guess on what CPU we are
+ debugging. It has been noted that architecture specified in tdesc file
+ has higher precedence over ELF and even "set architecture" - that is,
+ using "set architecture" command will have no effect when tdesc has "arch"
+ 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;
+ const char *const *core_regs;
+ const char *core_feature_name;
+
+ /* If target doesn't provide a description - use default one. */
+ 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");
+ }
+ else
+ {
+ tdesc_loc = tdesc_arc_arcompact;
+ if (arc_debug)
+ debug_printf ("arc: Using default register set for ARCompact.\n");
+ }
+ }
+ else
+ {
+ if (arc_debug)
+ debug_printf ("arc: Using provided register set.\n");
+ }
+ gdb_assert (tdesc_loc != NULL);
+
+ /* Now we can search for base registers. Core registers can be either full
+ or reduced. Summary:
+
+ - core.v2 + aux-minimal
+ - core-reduced.v2 + aux-minimal
+ - core.arcompact + aux-minimal
+
+ NB: It is entirely feasible to have ARCompact with reduced core regs, but
+ we ignore that because GCC doesn't support that and at the same time
+ ARCompact is considered obsolete, so there is not much reason to support
+ that. */
+ const struct tdesc_feature *feature
+ = tdesc_find_feature (tdesc_loc, core_v2_feature_name);
+ if (feature != NULL)
{
- printf_unfiltered ("The known ARC cpu types are as follows:\n");
- for (i = 0; arc_cpu_type_table[i].name != NULL; ++i)
- printf_unfiltered ("%s\n", arc_cpu_type_table[i].name);
+ /* Confirm that register and architecture match, to prevent accidents in
+ some situations. This code will trigger an error if:
- /* Restore the value. */
- tmp_arc_cpu_type = strsave (arc_cpu_type);
+ 1. XML tdesc doesn't specify arch explicitly, registers are for arch
+ X, but ELF specifies arch Y.
- return;
+ 2. XML tdesc specifies arch X, but contains registers for arch Y.
+
+ It will not protect from case where XML or ELF specify arch X,
+ registers are for the same arch X, but the real target is arch Y. To
+ detect this case we need to check IDENTITY register. */
+ if (!is_arcv2)
+ {
+ arc_print (_("Error: ARC v2 target description supplied for "
+ "non-ARCv2 target.\n"));
+ return FALSE;
+ }
+
+ is_reduced_rf = FALSE;
+ core_feature_name = core_v2_feature_name;
+ core_regs = core_v2_register_names;
+ }
+ else
+ {
+ feature = tdesc_find_feature (tdesc_loc, core_reduced_v2_feature_name);
+ if (feature != NULL)
+ {
+ if (!is_arcv2)
+ {
+ arc_print (_("Error: ARC v2 target description supplied for "
+ "non-ARCv2 target.\n"));
+ return FALSE;
+ }
+
+ is_reduced_rf = TRUE;
+ core_feature_name = core_reduced_v2_feature_name;
+ core_regs = core_v2_register_names;
+ }
+ else
+ {
+ feature = tdesc_find_feature (tdesc_loc,
+ core_arcompact_feature_name);
+ if (feature != NULL)
+ {
+ if (is_arcv2)
+ {
+ arc_print (_("Error: ARCompact target description supplied "
+ "for non-ARCompact target.\n"));
+ return FALSE;
+ }
+
+ is_reduced_rf = FALSE;
+ core_feature_name = core_arcompact_feature_name;
+ core_regs = core_arcompact_register_names;
+ }
+ else
+ {
+ arc_print (_("Error: Couldn't find core register feature in "
+ "supplied target description."));
+ return FALSE;
+ }
+ }
}
-
- if (!arc_set_cpu_type (tmp_arc_cpu_type))
+
+ struct tdesc_arch_data *tdesc_data_loc = tdesc_data_alloc ();
+
+ gdb_assert (feature != NULL);
+ int valid_p = 1;
+
+ for (int i = 0; i <= ARC_LAST_CORE_REGNUM; i++)
+ {
+ /* If rf16, then skip extra registers. */
+ if (is_reduced_rf && ((i >= ARC_R4_REGNUM && i <= ARC_R9_REGNUM)
+ || (i >= ARC_R16_REGNUM && i <= ARC_R25_REGNUM)))
+ continue;
+
+ valid_p = tdesc_numbered_register (feature, tdesc_data_loc, i,
+ core_regs[i]);
+
+ /* - Ignore errors in extension registers - they are optional.
+ - Ignore missing ILINK because it doesn't make sense for Linux.
+ - Ignore missing ILINK2 when architecture is ARCompact, because it
+ doesn't make sense for Linux targets.
+
+ In theory those optional registers should be in separate features, but
+ that would create numerous but tiny features, which looks like an
+ overengineering of a rather simple task. */
+ if (!valid_p && (i <= ARC_SP_REGNUM || i == ARC_BLINK_REGNUM
+ || i == ARC_LP_COUNT_REGNUM || i == ARC_PCL_REGNUM
+ || (i == ARC_R30_REGNUM && is_arcv2)))
+ {
+ 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;
+ }
+ }
+
+ /* Mandatory AUX registeres 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;
+ }
+
+ for (int i = ARC_FIRST_AUX_REGNUM; i <= ARC_LAST_AUX_REGNUM; i++)
{
- error ("Unknown cpu type `%s'.", tmp_arc_cpu_type);
- /* Restore its value. */
- tmp_arc_cpu_type = strsave (arc_cpu_type);
+ const char *name = aux_minimal_register_names[i - ARC_FIRST_AUX_REGNUM];
+ valid_p = tdesc_numbered_register (feature, tdesc_data_loc, i, name);
+ if (!valid_p)
+ {
+ arc_print (_("Error: Cannot find required register `%s' "
+ "in feature `%s'.\n"),
+ name, tdesc_feature_name (feature));
+ tdesc_data_cleanup (tdesc_data_loc);
+ return FALSE;
+ }
}
+
+ *tdesc = tdesc_loc;
+ *tdesc_data = tdesc_data_loc;
+
+ return TRUE;
}
-static void
-arc_show_cpu_type_command (args, from_tty)
- char *args;
- int from_tty;
+/* Implement the type_align gdbarch function. */
+
+static ULONGEST
+arc_type_align (struct gdbarch *gdbarch, struct type *type)
{
+ switch (TYPE_CODE (type))
+ {
+ 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;
+ }
}
-/* Modify the actual cpu type.
- Result is a boolean indicating success. */
+/* Implement the "init" gdbarch method. */
-int
-arc_set_cpu_type (str)
- char *str;
+static struct gdbarch *
+arc_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
{
- int i, j;
+ const struct target_desc *tdesc;
+ struct tdesc_arch_data *tdesc_data;
+
+ if (arc_debug)
+ debug_printf ("arc: Architecture initialization.\n");
+
+ if (!arc_tdesc_init (info, &tdesc, &tdesc_data))
+ return NULL;
+
+ /* Allocate the ARC-private target-dependent information structure, and the
+ GDB target-independent information structure. */
+ struct gdbarch_tdep *tdep = XCNEW (struct gdbarch_tdep);
+ tdep->jb_pc = -1; /* No longjmp support by default. */
+ struct gdbarch *gdbarch = gdbarch_alloc (&info, tdep);
+
+ /* Data types. */
+ set_gdbarch_short_bit (gdbarch, 16);
+ set_gdbarch_int_bit (gdbarch, 32);
+ set_gdbarch_long_bit (gdbarch, 32);
+ set_gdbarch_long_long_bit (gdbarch, 64);
+ 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_double_format (gdbarch, floatformats_ieee_double);
+ set_gdbarch_ptr_bit (gdbarch, 32);
+ set_gdbarch_addr_bit (gdbarch, 32);
+ set_gdbarch_char_signed (gdbarch, 0);
+
+ set_gdbarch_write_pc (gdbarch, arc_write_pc);
+
+ set_gdbarch_virtual_frame_pointer (gdbarch, arc_virtual_frame_pointer);
+
+ /* tdesc_use_registers expects gdbarch_num_regs to return number of registers
+ parsed by gdbarch_init, and then it will add all of the remaining
+ registers and will increase number of registers. */
+ set_gdbarch_num_regs (gdbarch, ARC_LAST_REGNUM + 1);
+ set_gdbarch_num_pseudo_regs (gdbarch, 0);
+ set_gdbarch_sp_regnum (gdbarch, ARC_SP_REGNUM);
+ set_gdbarch_pc_regnum (gdbarch, ARC_PC_REGNUM);
+ set_gdbarch_ps_regnum (gdbarch, ARC_STATUS32_REGNUM);
+ set_gdbarch_fp0_regnum (gdbarch, -1); /* No FPU registers. */
+
+ set_gdbarch_push_dummy_call (gdbarch, arc_push_dummy_call);
+ set_gdbarch_push_dummy_code (gdbarch, arc_push_dummy_code);
+
+ set_gdbarch_cannot_fetch_register (gdbarch, arc_cannot_fetch_register);
+ set_gdbarch_cannot_store_register (gdbarch, arc_cannot_store_register);
+
+ set_gdbarch_believe_pcc_promotion (gdbarch, 1);
+
+ set_gdbarch_return_value (gdbarch, arc_return_value);
+
+ set_gdbarch_skip_prologue (gdbarch, arc_skip_prologue);
+ set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+
+ set_gdbarch_breakpoint_kind_from_pc (gdbarch, arc_breakpoint_kind_from_pc);
+ set_gdbarch_sw_breakpoint_from_kind (gdbarch, arc_sw_breakpoint_from_kind);
+
+ /* On ARC 600 BRK_S instruction advances PC, unlike other ARC cores. */
+ if (!arc_mach_is_arc600 (gdbarch))
+ set_gdbarch_decr_pc_after_break (gdbarch, 0);
+ else
+ set_gdbarch_decr_pc_after_break (gdbarch, 2);
- if (str == NULL)
- return 0;
+ set_gdbarch_frame_align (gdbarch, arc_frame_align);
+
+ set_gdbarch_print_insn (gdbarch, arc_delayed_print_insn);
+
+ set_gdbarch_cannot_step_breakpoint (gdbarch, 1);
- for (i = 0; arc_cpu_type_table[i].name != NULL; ++i)
+ /* "nonsteppable" watchpoint means that watchpoint triggers before
+ instruction is committed, therefore it is required to remove watchpoint
+ to step though instruction that triggers it. ARC watchpoints trigger
+ only after instruction is committed, thus there is no need to remove
+ them. In fact on ARC watchpoint for memory writes may trigger with more
+ significant delay, like one or two instructions, depending on type of
+ memory where write is performed (CCM or external) and next instruction
+ after the memory write. */
+ set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 0);
+
+ /* This doesn't include possible long-immediate value. */
+ set_gdbarch_max_insn_length (gdbarch, 4);
+
+ /* Frame unwinders and sniffers. */
+ dwarf2_frame_set_init_reg (gdbarch, arc_dwarf2_frame_init_reg);
+ dwarf2_append_unwinders (gdbarch);
+ frame_unwind_append_unwinder (gdbarch, &arc_frame_unwind);
+ frame_base_set_default (gdbarch, &arc_normal_base);
+
+ /* Setup stuff specific to a particular environment (baremetal or Linux).
+ It can override functions set earlier. */
+ gdbarch_init_osabi (info, gdbarch);
+
+ 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)
{
- if (strcasecmp (str, arc_cpu_type_table[i].name) == 0)
+ const struct bfd_arch_info *tdesc_arch
+ = tdesc_architecture (info.target_desc);
+ if (tdesc_arch != NULL)
{
- arc_cpu_type = str;
- arc_bfd_mach_type = arc_cpu_type_table[i].value;
- return 1;
+ 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);
}
}
- return 0;
+ tdesc_use_registers (gdbarch, tdesc, tdesc_data);
+
+ return gdbarch;
+}
+
+/* Implement the "dump_tdep" gdbarch method. */
+
+static void
+arc_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ fprintf_unfiltered (file, "arc_dump_tdep: jb_pc = %i\n", tdep->jb_pc);
}
-\f
+
+/* Wrapper for "maintenance print arc" list of commands. */
+
+static void
+maintenance_print_arc_command (const 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 (const char *args, int from_tty)
+{
+ struct value *val;
+ if (args != NULL && strlen (args) > 0)
+ val = evaluate_expression (parse_expression (args).get ());
+ else
+ val = access_value_history (0);
+ record_latest_value (val);
+
+ CORE_ADDR address = value_as_address (val);
+ struct arc_instruction insn;
+ struct disassemble_info di = arc_disassemble_info (target_gdbarch ());
+ arc_insn_decode (address, &di, arc_delayed_print_insn, &insn);
+ arc_insn_dump (insn);
+}
+
void
-_initialize_arc_tdep ()
-{
- struct cmd_list_element *c;
-
- c = add_set_cmd ("cpu", class_support, var_string_noescape,
- (char *) &tmp_arc_cpu_type,
- "Set the type of ARC cpu in use.\n\
-This command has two purposes. In a multi-cpu system it lets one\n\
-change the cpu being debugged. It also gives one access to\n\
-cpu-type-specific registers and recognize cpu-type-specific instructions.\
-",
- &setlist);
- c->function.cfunc = arc_set_cpu_type_command;
- c = add_show_from_set (c, &showlist);
- c->function.cfunc = arc_show_cpu_type_command;
-
- /* We have to use strsave here because the `set' command frees it before
- setting a new value. */
- tmp_arc_cpu_type = strsave (DEFAULT_ARC_CPU_TYPE);
- arc_set_cpu_type (tmp_arc_cpu_type);
-
- c = add_set_cmd ("displaypipeline", class_support, var_zinteger,
- (char *) &display_pipeline_p,
- "Set pipeline display (simulator only).\n\
-When enabled, the state of the pipeline after each cycle is displayed.",
- &setlist);
- c = add_show_from_set (c, &showlist);
-
- c = add_set_cmd ("debugpipeline", class_support, var_zinteger,
- (char *) &debug_pipeline_p,
- "Set pipeline debug display (simulator only).\n\
-When enabled, debugging information about the pipeline is displayed.",
- &setlist);
- c = add_show_from_set (c, &showlist);
-
- c = add_set_cmd ("cputimer", class_support, var_zinteger,
- (char *) &cpu_timer,
- "Set maximum cycle count (simulator only).\n\
-Control will return to gdb if the timer expires.\n\
-A negative value disables the timer.",
- &setlist);
- c = add_show_from_set (c, &showlist);
-
- tm_print_insn = arc_print_insn;
+_initialize_arc_tdep (void)
+{
+ 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_cmd ("arc-instruction", class_maintenance,
+ dump_arc_instruction_command,
+ _("Dump arc_instruction structure for specified address."),
+ &maintenance_print_arc_list);
+
+ /* Debug internals for ARC GDB. */
+ add_setshow_zinteger_cmd ("arc", class_maintenance,
+ &arc_debug,
+ _("Set ARC specific debugging."),
+ _("Show ARC specific debugging."),
+ _("Non-zero enables ARC specific debugging."),
+ NULL, NULL, &setdebuglist, &showdebuglist);
}