diff options
Diffstat (limited to 'gdb/arc-linux-tdep.c')
-rw-r--r-- | gdb/arc-linux-tdep.c | 1143 |
1 files changed, 759 insertions, 384 deletions
diff --git a/gdb/arc-linux-tdep.c b/gdb/arc-linux-tdep.c index 14d34a6..479d850 100644 --- a/gdb/arc-linux-tdep.c +++ b/gdb/arc-linux-tdep.c @@ -1,506 +1,881 @@ -/* Target dependent code for ARC700, for GDB, the GNU debugger. +/* Target dependent code for ARC processor family, for GDB, the GNU debugger. - Copyright 2005 Free Software Foundation, Inc. + Copyright 2005, 2008, 2009 Free Software Foundation, Inc. - Authors: - Soam Vasani <soam.vasani@codito.com> - Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Contributed by Codito Technologies Pvt. Ltd. (www.codito.com) + + Authors: + Soam Vasani <soam.vasani@codito.com> + Ramana Radhakrishnan <ramana.radhakrishnan@codito.com> + Richard Stuckey <richard.stuckey@arc.com> 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, 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, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -*/ - -#include <string.h> + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/******************************************************************************/ +/* */ +/* Outline: */ +/* This module provides support for the ARC processor family's target */ +/* dependencies which are specific to the arc-linux-uclibc configuration */ +/* of the ARC gdb. */ +/* */ +/* Functionality: */ +/* This module provides a number of operations, including: */ +/* */ +/* 1) a function which returns the name of a register, given its number */ +/* */ +/* 2) a function which determines whether a given register belongs to a */ +/* particular group (e.g. the group of registers which should be saved */ +/* and restored across a function call) */ +/* */ +/* 3) a function which prints out registers */ +/* */ +/* */ +/* Usage: */ +/* The module exports a function _initialize_arc_linux_tdep: the call to */ +/* this function is generated by the gdb build mechanism, so this function*/ +/* should not be explicitly called. */ +/* */ +/* Some of the operations provided by this module are registered with gdb */ +/* during initialization; gdb then calls them via function pointers, */ +/* rather than by name (this allows gdb to handle multiple target */ +/* architectures): */ +/* */ +/* set_gdbarch_XXX (gdbarch, <function>); */ +/* */ +/* */ +/* Register Numbering Scheme: */ +/* The N target processor registers are assigned gdb numbers which form a */ +/* contiguous range starting at 0. The scheme used is: */ +/* */ +/* 0 .. 26 : core registers R0 .. R26 */ +/* 27 : BTA (Branch Target Address) auxiliary register */ +/* 28 : LP_START auxiliary register */ +/* 29 : LP_END auxiliary register */ +/* 30 : LP_COUNT core register (R60) */ +/* 31 : STATUS32 auxiliary register */ +/* 32 : BLINK (Branch Link) core register (R31) */ +/* 33 : FP (Frame Pointer) core register (R27) */ +/* 34 : SP (Stack Pointer) core register (R28) */ +/* 35 : EFA (Exception Fault Address) auxiliary register */ +/* 36 : RET (Exception Return Address) auxiliary register */ +/* 37 : ORIG_R8 */ +/* 38 : STOP_PC */ +/* */ +/* N.B. 1) core registers R61 and R62 are not included in the scheme, as */ +/* R61 is reserved, and R62 is not a real register; */ +/* */ +/* 2) core registers R29 (ILINK1), R30 (ILINK2) and R63 (PCL) are */ +/* not included; */ +/* */ +/* 3) extension core registers R32 .. R59 are not included; */ +/* */ +/* 4) most auxiliary registers (including all Build Configuration */ +/* Registers) are not included. */ +/* */ +/******************************************************************************/ + +/* gdb header files */ #include "defs.h" #include "osabi.h" -#include "frame.h" #include "regcache.h" -#include "gdb_assert.h" #include "inferior.h" #include "reggroups.h" #include "solib-svr4.h" -#include "symtab.h" -#include "objfiles.h" #include "block.h" +#include "regset.h" +#include "dis-asm.h" +#include "opcode/arc.h" +#include "gdb_assert.h" +/* ARC header files */ +#include "config/arc/tm-linux.h" +#include "arc-linux-tdep.h" +#include "arc-support.h" #include "arc-tdep.h" -#include "regset.h" +#include "opcodes/arcompact-dis.h" -//#define ARC_DEBUG 1 -/* Default Breakpoint instructions used for - ARC700 Linux -*/ -unsigned int arc700_linux_breakpoint_size = 2; -unsigned char arc700_linux_breakpoint_insn[2] = { 0x3e,0x78 } ; +/* -------------------------------------------------------------------------- */ +/* local data */ +/* -------------------------------------------------------------------------- */ + +#define STATUS32_L 0x00000100 -static const char * -arc_linux_register_name (int regno) -{ - static char linux_names[][10] = {"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", "r26", - - "bta", - "lp_start", "lp_end", "lp_count", - "status32", "blink", - "fp", "sp", "efa", - /* linux-only registers */ - "ret", "orig_r8", "pc", - - /* pseudo-regs */ - "ilink1", "ilink2", "eret", - "status_l1", "status_l2", "erstatus" }; - - gdb_assert(ARRAY_SIZE (linux_names) == NUM_REGS + NUM_PSEUDO_REGS); - gdb_assert(regno >=0 && regno < NUM_REGS + NUM_PSEUDO_REGS); - - return linux_names[regno]; -} -/* - * The kernel stores only one of (ilink1,ilink2,eret). This is stored in - * the ret "register". ilink1 is stored when the kernel has been entered - * because of a level 1 interrupt, etc. - * - * Same story for (status_l1, status_l2, erstatus). - * - * This disambiguity has been fixed by adding orig_r8 to pt_regs. - * It will take the following values - - * 1. if an exception of any kind occurs then orig_r8 >= 0 - * 2. Int level 1 : -1 - * 3. Int level 2 : -2 - * - * Registers whose value we don't know are given the value zero. +/* Default breakpoint instruction used for ARC700 Linux. */ +static const unsigned char le_breakpoint_instruction[] = { 0x3e, 0x78 }; +static const unsigned char be_breakpoint_instruction[] = { 0x78, 0x3e }; + + +/* This array holds the object code of two instructions: + mov r8,nr_sigreturn + swi */ -static void -arc_linux_pseudo_register_read (struct gdbarch *gdbarch, struct regcache *regcache, - int regno, void *buf) +static const gdb_byte arc_sigtramp_insns[] = { 0x8a, 0x20, 0xc1, 0x1d, + 0x6f, 0x22, 0x3f, 0x00 }; + +#define SIGTRAMP_INSNS_LENGTH sizeof(arc_sigtramp_insns) + + +/* N.B. the array size is specified in the declaration so that the compiler + will warn of "excess elements in array initializer" if there is a + mismatch (but not of too few elements, unfortunately!). */ +static const char *register_names[ARC_NR_REGS + ARC_NR_PSEUDO_REGS] = { - int status32, ret, orig_r8; - regcache_cooked_read (current_regcache, ARC_ORIG_R8_REGNUM, &orig_r8); + "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", "r26", + + "bta", + "lp_start", + "lp_end", + "lp_count", + "status32", + "blink", + "fp", + "sp", + "efa", + + /* Linux-only registers. */ + "ret", + "orig_r8", + "pc", // stop pc + + /* Pseudo-regs. */ + "ilink1", + "ilink2", + "eret", + "status_l1", + "status_l2", + "erstatus" +}; - if(regno == ARC_ILINK1_REGNUM || - regno == ARC_ILINK2_REGNUM || - regno == ARC_ERET_REGNUM) - { - regcache_cooked_read (current_regcache, ARC_RET_REGNUM, &ret); - if(regno == ARC_ILINK1_REGNUM) - *((unsigned int *)buf) = ((orig_r8 == -1) ? ret : 0); - else if(regno == ARC_ILINK2_REGNUM) - *((unsigned int *)buf) = ((orig_r8 == -2) ? ret : 0); - else if(regno == ARC_ERET_REGNUM) - *((unsigned int *)buf) = ((orig_r8 >= 0) ? ret : 0); +/* Mapping between the general-purpose registers in `struct sigcontext' format + and GDB's register cache layout. + + arc_linux_sc_reg_offset[i] is the sigcontext offset of GDB regnum `i'. */ + +/* From <asm/sigcontext.h>. */ +static const int arc_linux_sc_reg_offset[ARC_NR_REGS] = +{ + 23 * BYTES_IN_REGISTER, /* r0 */ + 22 * BYTES_IN_REGISTER, /* r1 */ + 21 * BYTES_IN_REGISTER, /* r2 */ + 20 * BYTES_IN_REGISTER, /* r3 */ + 19 * BYTES_IN_REGISTER, /* r4 */ + 18 * BYTES_IN_REGISTER, /* r5 */ + 17 * BYTES_IN_REGISTER, /* r6 */ + 16 * BYTES_IN_REGISTER, /* r7 */ + 15 * BYTES_IN_REGISTER, /* r8 */ + 14 * BYTES_IN_REGISTER, /* r9 */ + 13 * BYTES_IN_REGISTER, /* r10 */ + 12 * BYTES_IN_REGISTER, /* r11 */ + 11 * BYTES_IN_REGISTER, /* r12 */ + REGISTER_NOT_PRESENT, /* r13 */ + REGISTER_NOT_PRESENT, /* r14 */ + REGISTER_NOT_PRESENT, /* r15 */ + REGISTER_NOT_PRESENT, /* r16 */ + REGISTER_NOT_PRESENT, /* r17 */ + REGISTER_NOT_PRESENT, /* r18 */ + REGISTER_NOT_PRESENT, /* r19 */ + REGISTER_NOT_PRESENT, /* r20 */ + REGISTER_NOT_PRESENT, /* r21 */ + REGISTER_NOT_PRESENT, /* r22 */ + REGISTER_NOT_PRESENT, /* r23 */ + REGISTER_NOT_PRESENT, /* r24 */ + REGISTER_NOT_PRESENT, /* r25 */ + 10 * BYTES_IN_REGISTER, /* r26 */ + 2 * BYTES_IN_REGISTER, /* bta */ + 3 * BYTES_IN_REGISTER, /* lp_start */ + 4 * BYTES_IN_REGISTER, /* lp_end */ + 5 * BYTES_IN_REGISTER, /* lp_count */ + 6 * BYTES_IN_REGISTER, /* status32 */ + 8 * BYTES_IN_REGISTER, /* blink */ + 9 * BYTES_IN_REGISTER, /* fp */ + 1 * BYTES_IN_REGISTER, /* sp */ + REGISTER_NOT_PRESENT, /* efa */ + 7 * BYTES_IN_REGISTER, /* ret */ + REGISTER_NOT_PRESENT, /* orig_r8 */ + REGISTER_NOT_PRESENT /* stop_pc */ +}; + + +/* arcompact_linux_core_reg_offsets[i] is the offset in the .reg section of GDB regnum i. + From include/asm-arc/user.h in the ARC Linux sources. */ + +static const int arcompact_linux_core_reg_offsets[ARC_NR_REGS] = +{ + 22 * BYTES_IN_REGISTER, /* r0 */ + 21 * BYTES_IN_REGISTER, /* r1 */ + 20 * BYTES_IN_REGISTER, /* r2 */ + 19 * BYTES_IN_REGISTER, /* r3 */ + 18 * BYTES_IN_REGISTER, /* r4 */ + 17 * BYTES_IN_REGISTER, /* r5 */ + 16 * BYTES_IN_REGISTER, /* r6 */ + 15 * BYTES_IN_REGISTER, /* r7 */ + 14 * BYTES_IN_REGISTER, /* r8 */ + 13 * BYTES_IN_REGISTER, /* r9 */ + 12 * BYTES_IN_REGISTER, /* r10 */ + 11 * BYTES_IN_REGISTER, /* r11 */ + 10 * BYTES_IN_REGISTER, /* r12 */ + 39 * BYTES_IN_REGISTER, /* r13 */ + 38 * BYTES_IN_REGISTER, /* r14 */ + 37 * BYTES_IN_REGISTER, /* r15 */ + 36 * BYTES_IN_REGISTER, /* r16 */ + 35 * BYTES_IN_REGISTER, /* r17 */ + 34 * BYTES_IN_REGISTER, /* r18 */ + 33 * BYTES_IN_REGISTER, /* r19 */ + 32 * BYTES_IN_REGISTER, /* r20 */ + 31 * BYTES_IN_REGISTER, /* r21 */ + 30 * BYTES_IN_REGISTER, /* r22 */ + 29 * BYTES_IN_REGISTER, /* r23 */ + 28 * BYTES_IN_REGISTER, /* r24 */ + 27 * BYTES_IN_REGISTER, /* r25 */ + 9 * BYTES_IN_REGISTER, /* r26 */ + 1 * BYTES_IN_REGISTER, /* bta */ + 2 * BYTES_IN_REGISTER, /* lp_start */ + 3 * BYTES_IN_REGISTER, /* lp_end */ + 4 * BYTES_IN_REGISTER, /* lp_count */ + 5 * BYTES_IN_REGISTER, /* status32 */ + 7 * BYTES_IN_REGISTER, /* blink */ + 8 * BYTES_IN_REGISTER, /* fp */ + 25 * BYTES_IN_REGISTER, /* sp */ + REGISTER_NOT_PRESENT, /* efa */ + 6 * BYTES_IN_REGISTER, /* ret */ + 24 * BYTES_IN_REGISTER, /* orig_r8 */ + 40 * BYTES_IN_REGISTER, /* stop_pc */ +}; + + +/* -------------------------------------------------------------------------- */ +/* forward declarations */ +/* -------------------------------------------------------------------------- */ + +static int arc_linux_binutils_reg_to_regnum (struct gdbarch *gdbarch, int reg); + + +/* -------------------------------------------------------------------------- */ +/* local macros */ +/* -------------------------------------------------------------------------- */ + +#define PRINT(regnum) \ + default_print_registers_info (gdbarch, file, frame, regnum, all) + + +/* -------------------------------------------------------------------------- */ +/* local functions */ +/* -------------------------------------------------------------------------- */ + +/* Returns TRUE if the instruction at PC is a branch (of any kind). + *fall_thru is set to the address of the next insn. + *target is set to the branch target. */ + +static Boolean +next_pc (CORE_ADDR pc, CORE_ADDR *fall_thru, CORE_ADDR *target) +{ + struct regcache *regcache = get_current_regcache(); + struct disassemble_info di; + struct arcDisState instr; + Boolean two_targets = FALSE; + + arc_initialize_disassembler(&di); + + /* So what is the instruction at the given PC? */ + instr = arcAnalyzeInstr(pc, &di); + + /* By default, the next instruction is the one immediately after the one at PC. */ + *fall_thru = pc + instr.instructionLen; + DEBUG("--- next_pc(%x) = %x, isBranch = %d, tcnt = %d [%x], flow = %s (%d), " + "reg for indirect jump = %d, nullifyMode = %s\n", + (unsigned int) pc, (unsigned int) *fall_thru, instr.isBranch, instr.tcnt, instr.targets[0], + (instr.flow == direct_jump || instr.flow == direct_call) ? "direct" : "indirect", + instr.flow, + instr.register_for_indirect_jump, + ((instr.nullifyMode == (char) BR_exec_always) ? "delay slot" : "no delay")); + + /* OK, it's a branch. */ + if ((Boolean) instr.isBranch) + { + two_targets = TRUE; + + /* If it's a direct jump or call, the destination address is encoded in + the instruction, so we got it by disassembling the instruction; + otherwise, it's an indirect jump to the address held in the register + named in the instruction, so we must read that register. */ + if (instr.flow == direct_jump || instr.flow == direct_call) + *target = (CORE_ADDR) instr.targets[0]; + else + regcache_cooked_read(regcache, + arc_linux_binutils_reg_to_regnum(current_gdbarch, + instr.register_for_indirect_jump), + (gdb_byte*) target); + + /* For instructions with delay slots, the fall thru is not the instruction + immediately after the branch instruction, but the one after that. */ + if (instr.nullifyMode == (char) BR_exec_always) + { + struct arcDisState instr_d = arcAnalyzeInstr(*fall_thru, &di); + + *fall_thru += instr_d.instructionLen; + } } - else if(regno == ARC_STATUS32_L1_REGNUM || - regno == ARC_STATUS32_L2_REGNUM || - regno == ARC_ERSTATUS_REGNUM) + + + /* Check for a zero-overhead loop. */ { - regcache_cooked_read (current_regcache, ARC_STATUS32_REGNUM, &status32); - - if(regno == ARC_STATUS32_L1_REGNUM) - *((unsigned int *)buf) = ((orig_r8 == -1) ? status32 : 0); - else if(regno == ARC_STATUS32_L2_REGNUM) - *((unsigned int *)buf) = ((orig_r8 == -2) ? status32 : 0); - else if(regno == ARC_ERSTATUS_REGNUM) - *((unsigned int *)buf) = ((orig_r8 >= 0) ? status32 : 0); + unsigned int lp_end, lp_start, lp_count, status32; + + regcache_cooked_read(regcache, ARC_LP_START_REGNUM, (gdb_byte*) &lp_start); + regcache_cooked_read(regcache, ARC_LP_END_REGNUM, (gdb_byte*) &lp_end); + regcache_cooked_read(regcache, ARC_LP_COUNT_REGNUM, (gdb_byte*) &lp_count); + regcache_cooked_read(regcache, ARC_STATUS32_REGNUM, (gdb_byte*) &status32); + + if (!(status32 & STATUS32_L) && *fall_thru == lp_end && lp_count > 1) + { + /* The instruction is in effect a jump back to the start of the loop. */ + two_targets = TRUE; + *target = lp_start; + } } - else - internal_error(__FILE__, __LINE__, "arc_pseudo_register_read: bad register number (%d)", regno); + + return two_targets; } + +/* Extract the register values found in the ABI GREGSET, storing their values in + regcache. */ + static void -arc_linux_pseudo_register_write (struct gdbarch *gdbarch, struct regcache *regcache, - int regno, const void *buf) +arcompact_linux_supply_gregset (struct regcache *regcache, + int regnum, + const void *gregs, + size_t size) { - /* none of our pseudo-regs are writable */ - internal_error(__FILE__, __LINE__, "arc_pseudo_register_write: bad register number"); + const bfd_byte *buf = gregs; + unsigned int reg; + + for (reg = 0; reg < ELEMENTS_IN_ARRAY(arcompact_linux_core_reg_offsets); reg++) + { + if (arcompact_linux_core_reg_offsets[reg] != REGISTER_NOT_PRESENT) + regcache_raw_supply (regcache, + (int) reg, + buf + arcompact_linux_core_reg_offsets[reg]); + } } -/* - * print registers in the correct order. - * - * Why not have the regnums in the right order in the first place ? - * Because some of the registers have to be pseudo-registers because of - * the way the kernel is written, and because gdb assumes that - * pseudo-registers have regnums greater than real register regnums. - */ -static void -arc_linux_print_registers_info (struct gdbarch *gdbarch, struct ui_file *file, - struct frame_info *frame, int regnum, int all) + +/* Return whether the frame preceding next_frame corresponds to a GNU/Linux + sigtramp routine. */ + +static Boolean +is_linux_sigtramp (struct frame_info *next_frame) +{ + /* Find the PC for that previous frame. */ + CORE_ADDR pc = frame_pc_unwind (next_frame); + gdb_byte buf[SIGTRAMP_INSNS_LENGTH]; + + /* Read the memory at that PC (this gives us the code without any s/w + breakpoints that may have been set in it). */ + if (!safe_frame_unwind_memory (next_frame, pc, buf, (int) SIGTRAMP_INSNS_LENGTH)) + /* Failed to unwind frame. */ + return FALSE; + + /* Is that code the sigtramp instruction sequence? */ + if (memcmp(buf, arc_sigtramp_insns, SIGTRAMP_INSNS_LENGTH) == 0) + return TRUE; + + /* No - look one instruction earlier in the code. */ + if (!safe_frame_unwind_memory (next_frame, pc - 4, buf, (int) SIGTRAMP_INSNS_LENGTH)) + /* Failed to unwind frame. */ + return FALSE; + + if (memcmp(buf, arc_sigtramp_insns, SIGTRAMP_INSNS_LENGTH) == 0) + return TRUE; + + return FALSE; +} + + +/* Assuming next_frame is a frame following a GNU/Linux sigtramp + routine, return the address of the associated sigcontext structure. */ + +static CORE_ADDR +linux_sigcontext_addr (struct frame_info *next_frame) { - int i; + gdb_byte buf[4]; + + frame_unwind_register (next_frame, ARC_SP_REGNUM, buf); + + return (CORE_ADDR) extract_unsigned_integer (buf, 4); +} + + +/* Determine whether the given register is a member of the given group. - if (regnum >= 0) + Returns 0, 1, or -1: + 0 means the register is not in the group. + 1 means the register is in the group. + -1 means the tdep has nothing to say about this register and group. */ + +static int +register_reggroup_p (int regnum, struct reggroup *group) +{ + if (system_reggroup) { - default_print_registers_info (gdbarch, file, frame, regnum, all); - return; + if (regnum == ARC_ORIG_R8_REGNUM || + regnum == ARC_EFA_REGNUM || + regnum == ARC_ERET_REGNUM || + regnum == ARC_ERSTATUS_REGNUM) + return 1; + } + else if (group == general_reggroup) + { + if (regnum == ARC_RET_REGNUM) + return 0; + + return (regnum == ARC_STATUS32_REGNUM) ? 0 : 1; } - /* print all registers */ - - /* r0..r26 */ - for (i=0; i <= 26; ++i) - default_print_registers_info (gdbarch, file, frame, i, all); - - default_print_registers_info (gdbarch, file, frame, ARC_FP_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_SP_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_ILINK1_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_ILINK2_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_BLINK_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_LP_COUNT_REGNUM, all); - - /* now the aux registers */ - - default_print_registers_info (gdbarch, file, frame, ARC_BTA_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_LP_START_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_LP_END_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_EFA_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_ERET_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_STATUS32_L1_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_STATUS32_L2_REGNUM, all); - default_print_registers_info (gdbarch, file, frame, ARC_ERSTATUS_REGNUM, all); - - /* show the pc */ - default_print_registers_info (gdbarch, file, frame, ARC_STOP_PC_REGNUM, all); + /* Let the caller sort it out! */ + return -1; } -/* - * mov r8,nr_sigreturn - * swi - */ -static char arc_sigtramp_insn[] = { 0x8a, 0x20, 0xc1, 0x1d, 0x6f, 0x22, 0x3f, 0x00 }; +/* -------------------------------------------------------------------------- */ +/* local functions called from gdb */ +/* -------------------------------------------------------------------------- */ -/* Return whether the frame preceding NEXT_FRAME corresponds to a - GNU/Linux sigtramp routine. */ -static int -arc_linux_sigtramp_p (struct frame_info *next_frame) +/* The Linux kernel stores only one of (ilink1, ilink2, eret). This is stored + in the ret "register". ilink1 is stored when the kernel has been entered + because of a level 1 interrupt, etc. + + Same story for (status_l1, status_l2, erstatus). + + This disambiguity has been fixed by adding orig_r8 to pt_regs. + + FIXME: what is pt_regs???? + + It will take the following values - + 1. if an exception of any kind occurs then orig_r8 >= 0 + 2. Interrupt level 1 : orig == -1 + 3. Interrupt level 2 : orig == -2 + + Registers whose value we don't know are given the value zero. + + The only pseudo-registers are: + + ARC_ILINK1_REGNUM + ARC_ILINK2_REGNUM + ARC_ERET_REGNUM + ARC_STATUS32_L1_REGNUM + ARC_STATUS32_L2_REGNUM + ARC_ERSTATUS_REGNUM +*/ + +static void +arc_linux_pseudo_register_read (struct gdbarch *gdbarch, + struct regcache *regcache, + int gdb_regno, + gdb_byte *buf) { - CORE_ADDR pc = frame_pc_unwind (next_frame); - unsigned char buf[8]; + unsigned int* contents = (unsigned int *) buf; + unsigned int status32, ret; + int orig_r8; - if (!safe_frame_unwind_memory (next_frame, pc, buf, 8)) - return 0; + regcache_cooked_read (regcache, ARC_ORIG_R8_REGNUM, (gdb_byte*) &orig_r8); - if (memcmp(buf, arc_sigtramp_insn, 8) == 0) - return 1; - else + if (gdb_regno == ARC_ILINK1_REGNUM || + gdb_regno == ARC_ILINK2_REGNUM || + gdb_regno == ARC_ERET_REGNUM) { - pc -= 4; - - if (!safe_frame_unwind_memory (next_frame, pc, buf, 8)) - return 0; + regcache_cooked_read (regcache, ARC_RET_REGNUM, (gdb_byte*) &ret); - if (memcmp(buf, arc_sigtramp_insn, 8) == 0) - return 1; + if (gdb_regno == ARC_ILINK1_REGNUM) + *contents = ((orig_r8 == -1) ? ret : 0); + else if (gdb_regno == ARC_ILINK2_REGNUM) + *contents = ((orig_r8 == -2) ? ret : 0); + else // (gdb_regno == ARC_ERET_REGNUM) + *contents = ((orig_r8 >= 0) ? ret : 0); + + } + else if (gdb_regno == ARC_STATUS32_L1_REGNUM || + gdb_regno == ARC_STATUS32_L2_REGNUM || + gdb_regno == ARC_ERSTATUS_REGNUM) + { + regcache_cooked_read (regcache, ARC_STATUS32_REGNUM, (gdb_byte*) &status32); + + if (gdb_regno == ARC_STATUS32_L1_REGNUM) + *contents = ((orig_r8 == -1) ? status32 : 0); + else if (gdb_regno == ARC_STATUS32_L2_REGNUM) + *contents = ((orig_r8 == -2) ? status32 : 0); + else // (gdb_regno == ARC_ERSTATUS_REGNUM) + *contents = ((orig_r8 >= 0) ? status32 : 0); } + else + internal_error(__FILE__, __LINE__, _("%s: bad pseudo register number (%d)"), __FUNCTION__, gdb_regno); +} + - return 0; +static void +arc_linux_pseudo_register_write (struct gdbarch *gdbarch, + struct regcache *regcache, + int gdb_regno, + const gdb_byte *buf) +{ + /* None of our pseudo-regs are writable. */ + internal_error(__FILE__, __LINE__, _("%s: pseudo-registers are unwritable"), __FUNCTION__); } -/* Assuming NEXT_FRAME is a frame following a GNU/Linux sigtramp - routine, return the address of the associated sigcontext structure. */ -static CORE_ADDR -arc_linux_sigcontext_addr (struct frame_info *next_frame) + +/* Mapping from binutils/gcc register number to GDB register number ("regnum"). + N.B. registers such as ARC_FP_REGNUM, ARC_SP_REGNUM, etc., actually have + different GDB register numbers in the arc-elf32 and arc-linux-uclibc + configurations of the ARC gdb. */ + +static int +arc_linux_binutils_reg_to_regnum (struct gdbarch *gdbarch, int reg) { - char buf[4]; - CORE_ADDR sp; - - frame_unwind_register (next_frame, ARC_SP_REGNUM, buf); - sp = extract_unsigned_integer (buf, 4); + /* From gcc/config/arc/arc.h header file. */ + + if (reg >= 0 && reg <= 26) + return reg; + else if (reg == ARC_ABI_FRAME_POINTER) /* fp */ + return ARC_FP_REGNUM; + else if (reg == ARC_ABI_STACK_POINTER) /* sp */ + return ARC_SP_REGNUM; + else if (reg == 29) /* ilink1 */ + return ARC_ILINK1_REGNUM; + else if (reg == 30) /* ilink2 */ + return ARC_ILINK2_REGNUM; + else if (reg == 31) /* blink */ + return ARC_BLINK_REGNUM; + else if (IS_EXTENSION_CORE_REGISTER(reg)) /* reserved */ + ; + else if (reg == 60) /* lp_count */ + return ARC_LP_COUNT_REGNUM; +#if 0 + else if (reg == 61) /* reserved */ + ; + else if (reg == 62) /* no such register */ + ; + else if (reg == 63) /* PCL */ + ; +#endif + + warning(_("unmapped register #%d encountered"), reg); + return -1; +} + + +/* Print the contents of one, some or all registers. - return sp; + Print registers in the correct order. + Why not have the regnums in the right order in the first place? + Because some of the registers have to be pseudo-registers because of + the way the kernel is written, and because gdb assumes that + pseudo-registers have regnums greater than real register regnums. */ + +static void +arc_linux_print_registers_info (struct gdbarch *gdbarch, + struct ui_file *file, + struct frame_info *frame, + int regnum, + int all) +{ + if (regnum >= 0) + PRINT (regnum); + else /* If regnum < 0, print all registers. */ + { + int i; + + /* R0 .. R26 */ + for (i = 0; i <= 26; i++) PRINT (i); + + PRINT (ARC_FP_REGNUM ); + PRINT (ARC_SP_REGNUM ); + PRINT (ARC_ILINK1_REGNUM ); + PRINT (ARC_ILINK2_REGNUM ); + PRINT (ARC_BLINK_REGNUM ); + PRINT (ARC_LP_COUNT_REGNUM ); + + /* Now the auxiliary registers. */ + + PRINT (ARC_BTA_REGNUM ); + PRINT (ARC_LP_START_REGNUM ); + PRINT (ARC_LP_END_REGNUM ); + PRINT (ARC_EFA_REGNUM ); + PRINT (ARC_ERET_REGNUM ); + PRINT (ARC_STATUS32_L1_REGNUM); + PRINT (ARC_STATUS32_L2_REGNUM); + PRINT (ARC_ERSTATUS_REGNUM ); + + /* Show the PC. */ + PRINT (ARC_STOP_PC_REGNUM ); + } } -int -arc_linux_register_reggroup_p (int regnum, struct reggroup *group) + +/* Return the name of the given register. */ + +static const char* +arc_linux_register_name (struct gdbarch *gdbarch, int gdb_regno) { - if(regnum == ARC_ORIG_R8_REGNUM && group == system_reggroup) - return 1; + gdb_assert(ELEMENTS_IN_ARRAY(register_names) == (unsigned int) (ARC_NR_REGS + ARC_NR_PSEUDO_REGS)); - if(regnum == ARC_RET_REGNUM && group == general_reggroup) - return 0; + /* Oh, for a proper language with array bounds checking, like Ada... */ + gdb_assert(0 <= gdb_regno && gdb_regno < (int) ELEMENTS_IN_ARRAY(register_names)); - return -1; + return register_names[gdb_regno]; } -/* Mapping between the general-purpose registers in `struct - sigcontext' format and GDB's register cache layout. - arc_linux_sc_reg_offset[i] is the sigcontext offset of GDB regnum `i'. */ -/* From <asm/sigcontext.h>. */ -static int arc_linux_sc_reg_offset[] = +/* Determine whether the given register is read-only. */ + +static int +arc_linux_cannot_store_register (struct gdbarch *gdbarch, int gdb_regno) { - 23 * 4, /* r0 */ - 22 * 4, /* r1 */ - 21 * 4, /* r2 */ - 20 * 4, /* r3 */ - 19 * 4, /* r4 */ - 18 * 4, /* r5 */ - 17 * 4, /* r6 */ - 16 * 4, /* r7 */ - 15 * 4, /* r8 */ - 14 * 4, /* r9 */ - 13 * 4, /* r10 */ - 12 * 4, /* r11 */ - 11 * 4, /* r12 */ - -1, /* r13 */ - -1, /* r14 */ - -1, /* r15 */ - -1, /* r16 */ - -1, /* r17 */ - -1, /* r18 */ - -1, /* r19 */ - -1, /* r20 */ - -1, /* r21 */ - -1, /* r22 */ - -1, /* r23 */ - -1, /* r24 */ - -1, /* r25 */ - 10 * 4, /* r26 */ - 2 * 4, /* bta */ - 3 * 4, /* lp_start */ - 4 * 4, /* lp_end */ - 5 * 4, /* lp_count */ - 6 * 4, /* status32 */ - 8 * 4, /* blink */ - 9 * 4, /* fp */ - 1 * 4, /* sp */ - -1, /* efa */ - 7 * 4, /* ret */ - -1, /* orig_r8 */ - -1, /* stop_pc */ -}; + if (gdb_regno == ARC_EFA_REGNUM || + gdb_regno == ARC_ERET_REGNUM || + gdb_regno == ARC_STATUS32_L1_REGNUM || + gdb_regno == ARC_STATUS32_L2_REGNUM || + gdb_regno == ARC_ERSTATUS_REGNUM || + gdb_regno == ARC_ILINK1_REGNUM || + gdb_regno == ARC_ILINK2_REGNUM) + { + /* No warning should be printed. arc_cannot_store_register being + called does not imply that someone is actually writing to regnum. */ + + /* warning(_("writing to read-only register: %s"), gdbarch_register_name(gdbarch, gdb_regno)); */ + return 1; + } + + return 0; +} + + +/* This function is called just before we resume executing the inferior, if we + want to single-step it. We find the target(s) of the instruction about to + be executed and and place breakpoints there. */ + +static int +arc_linux_software_single_step (struct frame_info *frame) +{ + CORE_ADDR fall_thru, branch_target; + CORE_ADDR pc = get_frame_pc(frame); + Boolean two_breakpoints = next_pc(pc, &fall_thru, &branch_target); + + insert_single_step_breakpoint (fall_thru); + + if (two_breakpoints) + { + if (pc != branch_target) + insert_single_step_breakpoint (branch_target); + } + + /* Always returns true for now. */ + return 1; +} /* Set the program counter for process PTID to PC. */ static void -arc700_linux_write_pc (CORE_ADDR pc, ptid_t ptid) +arc_linux_write_pc (struct regcache *regcache, CORE_ADDR pc) { - ULONGEST val; - write_register_pid (PC_REGNUM, pc, ptid); - - /* We must be careful with modifying the program counter. If we - just interrupted a system call, the kernel might try to restart - it when we resume the inferior. On restarting the system call, - the kernel will try backing up the program counter even though it - no longer points at the system call. This typically results in a - SIGSEGV or SIGILL. We can prevent this by writing `-1' in the - "orig_r8" pseudo-register. - - Note that "orig_r8" is saved when setting up a dummy call frame. - This means that it is properly restored when that frame is - popped, and that the interrupted system call will be restarted - when we resume the inferior on return from a function call from - within GDB. In all other cases the system call will not be - restarted. */ - write_register_pid (ARC_ORIG_R8_REGNUM, -3, ptid); + regcache_cooked_write_unsigned (regcache, ARC_PC_REGNUM, pc); + + /* We must be careful with modifying the program counter. If we + just interrupted a system call, the kernel might try to restart + it when we resume the inferior. On restarting the system call, + the kernel will try backing up the program counter even though it + no longer points at the system call. This typically results in a + SIGSEGV or SIGILL. We can prevent this by writing `-1' in the + "orig_r8" pseudo-register. + + Note that "orig_r8" is saved when setting up a dummy call frame. + This means that it is properly restored when that frame is + popped, and that the interrupted system call will be restarted + when we resume the inferior on return from a function call from + within GDB. In all other cases the system call will not be + restarted. */ + + // FIXME: why -3 and not -1? -3 does not appear to be a defined valued for + // orig_r8 (i.e. -2, -1 or >= 0) - perhaps it means "none of these"? + regcache_cooked_write_signed (regcache, ARC_ORIG_R8_REGNUM, -3); } /* See the comments for SKIP_SOLIB_RESOLVER at the top of infrun.c. - This is called on every single step thru the PLT and runtime resolver. + This is called on every single step through the PLT and runtime resolver. This function: - 1) decides whether a PLT has sent us into the linker to resolve - a function reference, and - 2) if so, tells us where to set a temporary breakpoint that will - trigger when the dynamic linker is done. */ + 1) decides whether a PLT has sent us into the linker to resolve + a function reference, and + 2) if so, tells us where to set a temporary breakpoint that will + trigger when the dynamic linker is done. */ -CORE_ADDR +static CORE_ADDR arc_linux_skip_solib_resolver (struct gdbarch *gdbarch, CORE_ADDR pc) { - /* For uClibc 0.9.26. - - An unresolved PLT entry points to "__dl_linux_resolve", which calls - "__dl_linux_resolver" to do the resolving and then eventually jumps to - the function. - - So we look for the symbol `_dl_linux_resolver', and if we are there, - we set a breakpoint at the return address, and continue. */ - - /* lookup_minimal_symbol didn't work, for some reason. */ - struct symbol *resolver - = lookup_symbol_global ("_dl_linux_resolver", 0, VAR_DOMAIN, 0); - -#ifdef ARC_DEBUG - printf("--- arc_linux_skip_solib_resolver: pc = %x, resolver at %x\n", pc, - resolver ? BLOCK_START (SYMBOL_BLOCK_VALUE (resolver)) : 0); -#endif - - if (resolver && (BLOCK_START (SYMBOL_BLOCK_VALUE (resolver))) == pc) - { - return frame_pc_unwind (get_current_frame ()); - } + /* For uClibc 0.9.26. - return 0; -} + An unresolved PLT entry points to "__dl_linux_resolve", which calls + "__dl_linux_resolver" to do the resolving and then eventually jumps to + the function. + So we look for the symbol `_dl_linux_resolver', and if we are there, + gdb sets a breakpoint at the return address, and continues. */ -/* arcompact_linux_core_reg_offsets[i] is the offset in the .reg section of GDB - regnum i . - - From include/asm-arc/user.h in the ARC Linux sources. */ -static int arcompact_linux_core_reg_offsets[] = { - 22 * 4, /* r0 */ - 21 * 4, /* r1 */ - 20 * 4, /* r2 */ - 19 * 4, /* r3 */ - 18 * 4, /* r4 */ - 17 * 4, /* r5 */ - 16 * 4, /* r6 */ - 15 * 4, /* r7 */ - 14 * 4, /* r8 */ - 13 * 4, /* r9 */ - 12 * 4, /* r10 */ - 11 * 4, /* r11 */ - 10 * 4, /* r12 */ - 39 * 4, /* r13 */ - 38 * 4, /* r14 */ - 37 * 4, /* r15 */ - 36 * 4, /* r16 */ - 35 * 4, /* r17 */ - 34 * 4, /* r18 */ - 33 * 4, /* r19 */ - 32 * 4, /* r20 */ - 31 * 4, /* r21 */ - 30 * 4, /* r22 */ - 29 * 4, /* r23 */ - 28 * 4, /* r24 */ - 27 * 4, /* r25 */ - 9 * 4, /* r26 */ - 1 * 4, /* bta */ - 2 * 4, /* lp_start */ - 3 * 4, /* lp_end */ - 4 * 4, /* lp_count */ - 5 * 4, /* status32 */ - 7 * 4, /* blink */ - 8 * 4, /* fp */ - 25 * 4, /* sp */ - -1, /* efa */ - 6 * 4, /* ret */ - 24 * 4, /* orig_r8 */ - 40 * 4, /* stop_pc */ -}; + /* Lookup_minimal_symbol didn't work, for some reason. */ + struct symbol *resolver = + lookup_symbol_global ("_dl_linux_resolver", 0, 0, VAR_DOMAIN, 0); -/* Extract the register values found in the ABI GREGSET, storing their - values in REGCACHE. */ -static void -arcompact_linux_supply_gregset (struct regcache *regcache, - int regnum, const void *gregs, size_t size) -{ - int regi; - int arc_num_gprs = ARRAY_SIZE (arcompact_linux_core_reg_offsets); - const bfd_byte *buf = gregs; + DEBUG((resolver == NULL) ? "--- %s : pc = %x, no resolver found" + : "--- %s : pc = %x, resolver at %x\n", + __FUNCTION__, + (unsigned int) pc, + (unsigned int) ((resolver == NULL) ? 0 : BLOCK_START (SYMBOL_BLOCK_VALUE (resolver)))); - for (regi = 0; regi < arc_num_gprs; regi++) - { - if (arcompact_linux_core_reg_offsets[regi] > 0) - regcache_raw_supply (regcache, regi, - buf + arcompact_linux_core_reg_offsets[regi]); - } + if ((resolver != NULL) && (BLOCK_START (SYMBOL_BLOCK_VALUE (resolver))) == pc) + /* Find the return address. */ + return frame_pc_unwind (get_current_frame ()); + + /* No breakpoint is required. */ + return 0; } -/* Call the right architecture variant's supply_gregset function. For now, - we only have ARCompact. */ + +/* Call the right architecture variant's supply_gregset function. For now, we + have only ARCompact. */ + static void arc_linux_supply_gregset (const struct regset *regset, - struct regcache *regcache, - int regnum, const void *gregs, size_t size) + struct regcache *regcache, + int regnum, + const void *gregs, + size_t size) { - arcompact_linux_supply_gregset (regcache, regnum, gregs, size); + arcompact_linux_supply_gregset (regcache, regnum, gregs, size); } + /* Functions for handling core files. The first element is a parameter to pass the rest of the functions. We don't need it. supply_gregset is for reading the core file. collect_regset, which we haven't defined, would be for writing the core file. */ -static struct regset arc_linux_gregset = { - NULL, arc_linux_supply_gregset -}; -/* This is called through gdbarch. */ static const struct regset * arc_linux_regset_from_core_section (struct gdbarch *core_arch, - const char *sect_name, size_t sect_size) + const char *sect_name, + size_t sect_size) { - if (strcmp (sect_name, ".reg") == 0) - return &arc_linux_gregset; + static const struct regset arc_linux_gregset = + { + NULL, // descr + arc_linux_supply_gregset, // supply_regset + NULL, // collect_regset + NULL // arch + }; + + if (strcmp (sect_name, ".reg") == 0) + return &arc_linux_gregset; - return NULL; + return NULL; } -/* Add the signal stuff to gdbarch->tdep. */ + +/* Initialize for this ABI. */ + static void arc_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { - struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); - - tdep->sigtramp_p = arc_linux_sigtramp_p; - tdep->sigcontext_addr = arc_linux_sigcontext_addr; - tdep->sc_reg_offset = arc_linux_sc_reg_offset; - tdep->sc_num_regs = ARRAY_SIZE (arc_linux_sc_reg_offset); - - tdep->arc_breakpoint_size = arc700_linux_breakpoint_size; - tdep->arc_breakpoint_insn = arc700_linux_breakpoint_insn; - - set_gdbarch_num_regs (gdbarch, ARC_NR_REGS); - set_gdbarch_num_pseudo_regs (gdbarch, ARC_NR_PSEUDO_REGS); - - set_gdbarch_pc_regnum (gdbarch, ARC_STOP_PC_REGNUM); - set_gdbarch_register_name (gdbarch, arc_linux_register_name); - - set_gdbarch_software_single_step (gdbarch, arc_software_single_step); - - set_gdbarch_write_pc (gdbarch, arc700_linux_write_pc); - - tdep->pc_regnum_in_sigcontext = ARC_RET_REGNUM; + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + + /* Fill in target-dependent info in ARC-private structure. */ + + tdep->is_sigtramp = is_linux_sigtramp; + tdep->sigcontext_addr = linux_sigcontext_addr; + tdep->sc_reg_offset = arc_linux_sc_reg_offset; + tdep->sc_num_regs = ELEMENTS_IN_ARRAY(arc_linux_sc_reg_offset); + tdep->pc_regnum_in_sigcontext = ARC_RET_REGNUM; + + tdep->le_breakpoint_instruction = le_breakpoint_instruction; + tdep->be_breakpoint_instruction = be_breakpoint_instruction; + tdep->breakpoint_size = (unsigned int) sizeof(le_breakpoint_instruction); + + tdep->register_reggroup_p = register_reggroup_p; + + tdep->lowest_pc = 0x74; // FIXME: why this? + tdep->processor_variant_info = NULL; + + /* Pass target-dependent info to gdb. */ + + /* ARC_NR_REGS and ARC_NR_PSEUDO_REGS are defined in the tm.h configuration file. */ + set_gdbarch_pc_regnum (gdbarch, ARC_STOP_PC_REGNUM); + set_gdbarch_num_regs (gdbarch, ARC_NR_REGS); + set_gdbarch_num_pseudo_regs (gdbarch, ARC_NR_PSEUDO_REGS); + set_gdbarch_print_registers_info (gdbarch, arc_linux_print_registers_info); + set_gdbarch_register_name (gdbarch, arc_linux_register_name); + set_gdbarch_cannot_store_register (gdbarch, arc_linux_cannot_store_register); + set_gdbarch_dwarf2_reg_to_regnum (gdbarch, arc_linux_binutils_reg_to_regnum); + + set_gdbarch_decr_pc_after_break (gdbarch, 0); + set_gdbarch_software_single_step (gdbarch, arc_linux_software_single_step); + set_gdbarch_write_pc (gdbarch, arc_linux_write_pc); + set_gdbarch_pseudo_register_read (gdbarch, arc_linux_pseudo_register_read); + set_gdbarch_pseudo_register_write (gdbarch, arc_linux_pseudo_register_write); + set_gdbarch_regset_from_core_section (gdbarch, arc_linux_regset_from_core_section); + set_gdbarch_skip_solib_resolver (gdbarch, arc_linux_skip_solib_resolver); + + /* GNU/Linux uses SVR4-style shared libraries. */ + set_solib_svr4_fetch_link_map_offsets (gdbarch, svr4_ilp32_fetch_link_map_offsets); +} - set_gdbarch_pseudo_register_read (gdbarch, arc_linux_pseudo_register_read); - set_gdbarch_pseudo_register_write (gdbarch, arc_linux_pseudo_register_write); +/* -------------------------------------------------------------------------- */ +/* externally visible functions */ +/* -------------------------------------------------------------------------- */ - set_gdbarch_print_registers_info (gdbarch, arc_linux_print_registers_info); +/* Initialize the module. This function is called from the gdb core on start-up. */ - tdep->register_reggroup_p = arc_linux_register_reggroup_p; - - tdep->lowest_pc = 0x74; +void +_initialize_arc_linux_tdep (void) +{ + /* Register a handler with gdb for the Linux O/S ABI variant for the ARC + processor architecture, providing an initialization function; + 'bfd_arch_arc' is an enumeration value specifically denoting the ARC + architecture. */ + gdbarch_register_osabi (bfd_arch_arc, + 0, // machine (irrelevant) + GDB_OSABI_LINUX, + arc_linux_init_abi); +} - tdep->arc_processor_variant_info = NULL; - set_gdbarch_regset_from_core_section (gdbarch, - arc_linux_regset_from_core_section); - /* GNU/Linux uses SVR4-style shared libraries. */ - set_solib_svr4_fetch_link_map_offsets - (gdbarch, svr4_ilp32_fetch_link_map_offsets); - set_gdbarch_skip_solib_resolver (gdbarch, arc_linux_skip_solib_resolver); -} +/* This function is required simply to avoid an undefined symbol at linkage. */ void -_initialize_arc_linux_tdep (void) +arc_check_pc_defined (struct gdbarch *gdbarch) { - gdbarch_register_osabi (bfd_arch_arc, 0, GDB_OSABI_LINUX, - arc_linux_init_abi); } + +/******************************************************************************/ |