X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Farc-tdep.c;h=e7a7d53f6f79656464764f658bcaf86e3fbf124a;hb=7913a64cab3930fe524f0e38f85cfca11bd52dcb;hp=fc20d8b2938b8388578f0f12584d202cbea34bff;hpb=fba45db2faf619e71856ee38ec63949c0ef6903e;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/arc-tdep.c b/gdb/arc-tdep.c index fc20d8b293..e7a7d53f6f 100644 --- a/gdb/arc-tdep.c +++ b/gdb/arc-tdep.c @@ -1,11 +1,13 @@ -/* ARC target-dependent stuff. - Copyright (C) 1995, 1997 Free Software Foundation, Inc. +/* Target dependent code for ARC arhitecture, for GDB. + + Copyright 2005-2016 Free Software Foundation, Inc. + Contributed by Synopsys Inc. This file is part of GDB. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -14,716 +16,1302 @@ 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, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + along with this program. If not, see . */ +/* 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 "trad-frame.h" + +/* ARC header files. */ +#include "opcode/arc.h" +#include "arc-tdep.h" -/* Local functions */ +/* Standard headers. */ +#include -static int arc_set_cpu_type (char *str); +/* 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; +/* The frame unwind cache for the ARC. Current structure is a stub, because + it should be filled in during the prologue analysis. */ -/* Table of cpu names. */ -struct - { - char *name; - int value; - } -arc_cpu_type_table[] = +struct arc_frame_cache { - { - "base", bfd_mach_arc_base - } - , - { - NULL, 0 - } + /* 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; + + /* Store addresses for registers saved in prologue. */ + 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)) - -/* Codestream stuff. */ -static void codestream_read (unsigned int *, int); -static void codestream_seek (CORE_ADDR); -static unsigned int codestream_fill (int); - -#define CODESTREAM_BUFSIZ 16 -static CORE_ADDR codestream_next_addr; -static CORE_ADDR codestream_addr; -/* FIXME assumes sizeof (int) == 32? */ -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 (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 -> convert to host byte ordering. */ - { - int i; - for (i = 0; i < CODESTREAM_BUFSIZ; i++) - codestream_buf[i] = - extract_unsigned_integer (&codestream_buf[i], - sizeof (codestream_buf[i])); - } - - if (peek_flag) - return codestream_peek (); - else - return codestream_get (); -} +/* Global debug flag. */ + +int arc_debug; + +/* 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", "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", "pcl", +}; + +/* 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 -codestream_seek (CORE_ADDR place) +arc_write_pc (struct regcache *regcache, CORE_ADDR new_pc) { - codestream_next_addr = place / CODESTREAM_BUFSIZ; - codestream_next_addr *= CODESTREAM_BUFSIZ; - codestream_cnt = 0; - codestream_fill (1); - while (codestream_tell () != place) - codestream_get (); + struct gdbarch *gdbarch = get_regcache_arch (regcache); + + if (arc_debug) + debug_printf ("arc: Writing PC, new value=%s\n", + paddress (gdbarch, new_pc)); + + 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) + { + 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); + } } -/* This function is currently unused but leave in for now. */ +/* 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 -codestream_read (unsigned int *buf, int count) +arc_virtual_frame_pointer (struct gdbarch *gdbarch, CORE_ADDR pc, + int *reg_ptr, LONGEST *offset_ptr) { - unsigned int *p; - int i; - p = buf; - for (i = 0; i < count; i++) - *p++ = codestream_get (); + *reg_ptr = gdbarch_sp_regnum (gdbarch); + *offset_ptr = 0; } - -/* Set up prologue scanning and return the first insn. */ -static unsigned int -setup_prologue_scan (CORE_ADDR pc) -{ - unsigned int insn; +/* Implement the "dummy_id" gdbarch method. - codestream_seek (pc); - insn = codestream_get (); + Tear down a dummy frame created by arc_push_dummy_call (). This data has + to be constructed manually from the data in our hand. The stack pointer + and program counter can be obtained from the frame info. */ - return insn; +static struct frame_id +arc_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame) +{ + return frame_id_build (get_frame_sp (this_frame), + get_frame_pc (this_frame)); } -/* - * 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. - */ - -static long -arc_get_frame_setup (CORE_ADDR pc) +/* 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, int struct_return, + CORE_ADDR struct_addr) { - 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; + if (arc_debug) + debug_printf ("arc: push_dummy_call (nargs = %d)\n", nargs); - insn = setup_prologue_scan (pc); + int arg_reg = ARC_FIRST_ARG_REGNUM; - /* The authority for what appears here is the home-grown ABI. - The most recent version is 1.2. */ + /* Push the return address. */ + regcache_cooked_write_unsigned (regcache, ARC_BLINK_REGNUM, bp_addr); - /* 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)) + /* 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 (struct_return) { - maybe_stdarg_decr = X_D (insn); - insn = codestream_get (); - } + /* Pass the return address in the first argument register. */ + regcache_cooked_write_unsigned (regcache, arg_reg, struct_addr); - if ((insn & BUILD_INSN (-1, 0, -1, -1, -1)) /* st blink,[sp,4] */ - == BUILD_INSN (2, 0, SP_REGNUM, BLINK_REGNUM, 4)) - { - insn = codestream_get (); - /* Frame may not be necessary, even though blink is saved. - At least this is something we recognize. */ - frame_size = 0; + if (arc_debug) + debug_printf ("arc: struct return address %s passed in R%d", + print_core_address (gdbarch, struct_addr), arg_reg); + + arg_reg++; } - if ((insn & BUILD_INSN (-1, 0, -1, -1, -1)) /* st fp,[sp] */ - == BUILD_INSN (2, 0, SP_REGNUM, FP_REGNUM, 0)) + if (nargs > 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; - - /* 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)) + 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++) { - 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; + unsigned int len = TYPE_LENGTH (value_type (args[i])); + unsigned int space = align_up (len, 4); - codestream_get (); + total_space += space; - /* 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 (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 (regcache, 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. + + 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. + + 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. - 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. */ + 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. */ -CORE_ADDR -arc_skip_prologue (CORE_ADDR pc, int frameless_p) +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; +} - if ((frame_size = arc_get_frame_setup (pc)) < 0) - return (pc); +/* Implement the "cannot_fetch_register" gdbarch method. */ - if (frameless_p) - return frame_size == 0 ? pc : codestream_tell (); +static int +arc_cannot_fetch_register (struct gdbarch *gdbarch, int regnum) +{ + /* Assume that register is readable if it is unknown. */ + return FALSE; +} - /* Skip over register saves. */ - for (i = 0; i < 8; i++) +/* Implement the "cannot_store_register" gdbarch method. */ + +static int +arc_cannot_store_register (struct gdbarch *gdbarch, int regnum) +{ + /* Assume that register is writable if it is unknown. */ + 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_PCL_REGNUM: + return TRUE; + default: + return FALSE; } - - return codestream_tell (); } -/* Return the return address for a frame. - This is used to implement FRAME_SAVED_PC. - This is taken from frameless_look_for_prologue. */ +/* 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. -CORE_ADDR -arc_frame_saved_pc (struct frame_info *frame) + 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) { - CORE_ADDR func_start; - unsigned int insn; + unsigned int len = TYPE_LENGTH (type); + + if (arc_debug) + debug_printf ("arc: extract_return_value\n"); + + if (len <= ARC_REGISTER_SIZE) + { + ULONGEST val; + + /* 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); - func_start = get_pc_function_start (frame->pc) + FUNCTION_START_OFFSET; - if (func_start == 0) + if (arc_debug) + debug_printf ("arc: returning 0x%s\n", phex (val, ARC_REGISTER_SIZE)); + } + else if (len <= ARC_REGISTER_SIZE * 2) { - /* Best guess. */ - return ARC_PC_TO_REAL_ADDRESS (read_memory_integer (FRAME_FP (frame) + 4, 4)); + 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); +} + - /* The authority for what appears here is the home-grown ABI. - The most recent version is 1.2. */ +/* Store the return value of a function into the registers/memory used to + return it, according to the convention used by the ABI. - insn = setup_prologue_scan (func_start); + TODO: This implementation ignores the case of "complex double", where + according to ABI, value is returned in the R0-R3 registers. - /* 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 (); + TYPE is a returned value's type. VALBUF is a buffer with the value to + return. */ - /* 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. */ +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 ((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)); + 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 - return ARC_PC_TO_REAL_ADDRESS (read_memory_integer (FRAME_FP (frame) + 4, 4)); + error (_("arc_store_return_value: type length too large.")); } -/* - * 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). - */ +/* Implement the "get_longjmp_target" gdbarch method. */ -void -frame_find_saved_regs (struct frame_info *fip, struct frame_saved_regs *fsrp) +static int +arc_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc) { - long locals; - unsigned int insn; - CORE_ADDR dummy_bottom; - CORE_ADDR adr; - int i, regnum, offset; + if (arc_debug) + debug_printf ("arc: get_longjmp_target\n"); - memset (fsrp, 0, sizeof *fsrp); + 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 frame is the end of a dummy, compute where the beginning would be. */ - dummy_bottom = fip->frame - 4 - REGISTER_BYTES - CALL_DUMMY_LENGTH; + if (target_read_memory (jb_addr + pc_offset, buf, ARC_REGISTER_SIZE)) + return 0; /* Failed to read from memory. */ - /* Check if the PC is in the stack, in a dummy frame. */ - if (dummy_bottom <= fip->pc && fip->pc <= fip->frame) - { - /* all regs were saved by push_call_dummy () */ - adr = fip->frame; - for (i = 0; i < NUM_REGS; i++) - { - adr -= REGISTER_RAW_SIZE (i); - fsrp->regs[i] = adr; - } - return; - } + *pc = extract_unsigned_integer (buf, ARC_REGISTER_SIZE, + gdbarch_byte_order (gdbarch)); + return 1; +} - locals = arc_get_frame_setup (get_pc_function_start (fip->pc)); +/* Implement the "return_value" gdbarch method. */ - if (locals >= 0) +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) { - /* Set `adr' to the value of `sp'. */ - adr = fip->frame - locals; - for (i = 0; i < 8; i++) - { - 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; - } + /* 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) + { + /* 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); } - fsrp->regs[PC_REGNUM] = fip->frame + 4; - fsrp->regs[FP_REGNUM] = fip->frame; + return (is_struct_return + ? RETURN_VALUE_STRUCT_CONVENTION + : RETURN_VALUE_REGISTER_CONVENTION); } -void -arc_push_dummy_frame (void) +/* Return the base address of the frame. For ARC, the base address is the + frame pointer. */ + +static CORE_ADDR +arc_frame_base_address (struct frame_info *this_frame, void **prologue_cache) { - CORE_ADDR sp = read_register (SP_REGNUM); - int regnum; - char regbuf[MAX_REGISTER_RAW_SIZE]; - - 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++) - { - read_register_gen (regnum, regbuf); - sp = push_bytes (sp, regbuf, REGISTER_RAW_SIZE (regnum)); - } - sp += (2 * REGISTER_SIZE); - write_register (SP_REGNUM, sp); + return (CORE_ADDR) get_frame_register_unsigned (this_frame, ARC_FP_REGNUM); } -void -arc_pop_frame (void) +/* 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) { - 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++) + if (arc_debug) + debug_printf ("arc: skip_prologue\n"); + + CORE_ADDR func_addr; + const char *func_name; + + /* See what the symbol table says. */ + if (find_pc_partial_function (pc, &func_name, &func_addr, NULL)) { - CORE_ADDR adr; - adr = fsr.regs[regnum]; - if (adr) - { - read_memory (adr, regbuf, REGISTER_RAW_SIZE (regnum)); - write_register_bytes (REGISTER_BYTE (regnum), regbuf, - REGISTER_RAW_SIZE (regnum)); - } + /* Found a function. */ + CORE_ADDR postprologue_pc + = skip_prologue_using_sal (gdbarch, func_addr); + + if (postprologue_pc != 0) + return std::max (pc, postprologue_pc); } - 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 (); + + /* No prologue info in symbol table, have to analyze prologue. */ + + /* Find an upper limit on the function prologue using the debug + information. If the debug information could not be used to provide that + bound, then pass 0 and arc_scan_prologue will estimate value itself. */ + CORE_ADDR limit_pc = skip_prologue_using_sal (gdbarch, pc); + /* We don't have a proper analyze_prologue function yet, but its result + should be returned here. Currently GDB will just stop at the first + instruction of function if debug information doesn't have prologue info; + and if there is a debug info about prologue - this code path will not be + taken at all. */ + return (limit_pc == 0 ? pc : limit_pc); +} + +/* 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. */ + +static int +arc_delayed_print_insn (bfd_vma addr, struct disassemble_info *info) +{ + int (*print_insn) (bfd_vma, struct disassemble_info *); + /* exec_bfd may be null, if GDB is run without a target BFD file. Opcodes + will handle NULL value gracefully. */ + print_insn = arc_get_disassembler (exec_bfd); + gdb_assert (print_insn != NULL); + return print_insn (addr, info); } - -/* Simulate single-step. */ -typedef enum +/* 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) { - 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 */ + 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); } -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. */ +/* Implement the "sw_breakpoint_from_kind" gdbarch method. */ -static insn_type -get_insn_type (unsigned long insn, CORE_ADDR pc, CORE_ADDR *target) +static const gdb_byte * +arc_sw_breakpoint_from_kind (struct gdbarch *gdbarch, int kind, int *size) { - unsigned long limm; + *size = kind; - switch (insn >> 27) + if (kind == sizeof (arc_brk_le)) { - 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))) - { - limm = read_memory_integer (pc + 4, 4); - *target = ARC_PC_TO_REAL_ADDRESS (limm); - return BRANCH8; - } - 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; + return ((gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) + ? arc_brk_be + : arc_brk_le); + } + else + { + return ((gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) + ? arc_brk_s_be + : arc_brk_s_le); } } -/* 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. +/* Implement the "unwind_pc" gdbarch method. */ - single_step is also called just after the inferior stops. If we had - set up a simulated single-step, we undo our damage. */ +static CORE_ADDR +arc_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) +{ + int pc_regnum = gdbarch_pc_regnum (gdbarch); + CORE_ADDR pc = frame_unwind_register_unsigned (next_frame, pc_regnum); -void -arc_software_single_step (enum target_signal ignore, /* sig but we don't need it */ - int insert_breakpoints_p) + if (arc_debug) + debug_printf ("arc: unwind PC: %s\n", paddress (gdbarch, pc)); + + return pc; +} + +/* Implement the "unwind_sp" gdbarch method. */ + +static CORE_ADDR +arc_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame) { - static CORE_ADDR next_pc, target; - static int brktrg_p; - typedef char binsn_quantum[BREAKPOINT_MAX]; - static binsn_quantum break_mem[2]; + int sp_regnum = gdbarch_sp_regnum (gdbarch); + CORE_ADDR sp = frame_unwind_register_unsigned (next_frame, sp_regnum); - if (insert_breakpoints_p) - { - insn_type type; - CORE_ADDR pc; - unsigned long insn; - - pc = read_register (PC_REGNUM); - insn = read_memory_integer (pc, 4); - type = get_insn_type (insn, pc, &target); - - /* 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]); - - brktrg_p = 0; - - 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]); - } + if (arc_debug) + debug_printf ("arc: unwind SP: %s\n", paddress (gdbarch, sp)); + + return sp; +} + +/* Implement the "frame_align" gdbarch method. */ +static CORE_ADDR +arc_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp) +{ + return align_down (sp, 4); +} + +/* 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 prev_pc = get_frame_pc (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); + 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]); + entrypoint = get_frame_register_unsigned (this_frame, + gdbarch_pc_regnum (gdbarch)); + prologue_end = 0; + } - 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); - } + /* Should call analyze_prologue here, when it will be implemented. */ + + return cache; } - -#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 (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; -} -#endif /* GET_LONGJMP_TARGET */ - -/* Disassemble one instruction. */ + 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 (). -static int -arc_print_insn (bfd_vma vma, disassemble_info *info) -{ - static int current_mach; - static int current_endian; - static disassembler_ftype current_disasm; + 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. - 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); - } + 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)); - return (*current_disasm) (vma, info); + *this_id = frame_id_build (stack_addr, code_addr); } - -/* Command to set cpu type. */ -void -arc_set_cpu_type_command (char *args, int from_tty) +/* Implement the "prev_register" frame_unwind method. */ + +static struct value * +arc_frame_prev_register (struct frame_info *this_frame, + void **this_cache, int regnum) { - int i; + if (*this_cache == NULL) + *this_cache = arc_make_frame_cache (this_frame); + struct arc_frame_cache *cache = (struct arc_frame_cache *) (*this_cache); - if (tmp_arc_cpu_type == NULL || *tmp_arc_cpu_type == '\0') - { - 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); + struct gdbarch *gdbarch = get_frame_arch (this_frame); - /* Restore the value. */ - tmp_arc_cpu_type = strsave (arc_cpu_type); + /* 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; - return; - } + /* 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. - if (!arc_set_cpu_type (tmp_arc_cpu_type)) - { - error ("Unknown cpu type `%s'.", tmp_arc_cpu_type); - /* Restore its value. */ - tmp_arc_cpu_type = strsave (arc_cpu_type); - } + 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); } +/* Implement the "init_reg" dwarf2_frame method. */ + static void -arc_show_cpu_type_command (char *args, int from_tty) +arc_dwarf2_frame_init_reg (struct gdbarch *gdbarch, int regnum, + struct dwarf2_frame_state_reg *reg, + struct frame_info *info) { + 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; } -/* Modify the actual cpu type. - Result is a boolean indicating success. */ +/* 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 +}; + +/* 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_set_cpu_type (char *str) +arc_tdesc_init (struct gdbarch_info info, const struct target_desc **tdesc, + struct tdesc_arch_data **tdesc_data) { - int i, j; + 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) + { + /* Confirm that register and architecture match, to prevent accidents in + some situations. This code will trigger an error if: - if (str == NULL) - return 0; + 1. XML tdesc doesn't specify arch explicitly, registers are for arch + X, but ELF specifies arch Y. - for (i = 0; arc_cpu_type_table[i].name != NULL; ++i) + 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 { - if (strcasecmp (str, arc_cpu_type_table[i].name) == 0) + feature = tdesc_find_feature (tdesc_loc, core_reduced_v2_feature_name); + if (feature != NULL) { - arc_cpu_type = str; - arc_bfd_mach_type = arc_cpu_type_table[i].value; - return 1; + 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; + } } } - return 0; + 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++) + { + 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; +} + +/* Implement the "init" gdbarch method. */ + +static struct gdbarch * +arc_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) +{ + 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_long_long_align_bit (gdbarch, 32); + 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_dummy_id (gdbarch, arc_dummy_id); + set_gdbarch_push_dummy_call (gdbarch, arc_push_dummy_call); + set_gdbarch_push_dummy_code (gdbarch, arc_push_dummy_code); + + 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); + + set_gdbarch_unwind_pc (gdbarch, arc_unwind_pc); + set_gdbarch_unwind_sp (gdbarch, arc_unwind_sp); + + set_gdbarch_frame_align (gdbarch, arc_frame_align); + + set_gdbarch_print_insn (gdbarch, arc_delayed_print_insn); + + set_gdbarch_cannot_step_breakpoint (gdbarch, 1); + + /* "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); + + 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); +} + +/* Suppress warning from -Wmissing-prototypes. */ +extern initialize_file_ftype _initialize_arc_tdep; + void _initialize_arc_tdep (void) { - 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; + 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. */ + + /* 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); }