| /* Target dependent code for ARC processor family, for GDB, the GNU debugger. |
| |
| Copyright 2005, 2008, 2009 Free Software Foundation, Inc. |
| |
| 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 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, 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 "regcache.h" |
| #include "inferior.h" |
| #include "reggroups.h" |
| #include "solib-svr4.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 "opcodes/arcompact-dis.h" |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* local data */ |
| /* -------------------------------------------------------------------------- */ |
| |
| #define STATUS32_L 0x00000100 |
| |
| |
| /* 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 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] = |
| { |
| "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" |
| }; |
| |
| |
| /* 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; |
| } |
| } |
| |
| |
| /* Check for a zero-overhead loop. */ |
| { |
| 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; |
| } |
| } |
| |
| return two_targets; |
| } |
| |
| |
| /* 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) |
| { |
| 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]); |
| } |
| } |
| |
| |
| /* 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) |
| { |
| 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. |
| |
| 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) |
| { |
| 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; |
| } |
| |
| /* Let the caller sort it out! */ |
| return -1; |
| } |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* local functions called from gdb */ |
| /* -------------------------------------------------------------------------- */ |
| |
| /* 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) |
| { |
| unsigned int* contents = (unsigned int *) buf; |
| unsigned int status32, ret; |
| int orig_r8; |
| |
| regcache_cooked_read (regcache, ARC_ORIG_R8_REGNUM, (gdb_byte*) &orig_r8); |
| |
| if (gdb_regno == ARC_ILINK1_REGNUM || |
| gdb_regno == ARC_ILINK2_REGNUM || |
| gdb_regno == ARC_ERET_REGNUM) |
| { |
| regcache_cooked_read (regcache, ARC_RET_REGNUM, (gdb_byte*) &ret); |
| |
| 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); |
| } |
| |
| |
| 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__); |
| } |
| |
| |
| /* 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) |
| { |
| /* 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. |
| |
| 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 ); |
| } |
| } |
| |
| |
| /* Return the name of the given register. */ |
| |
| static const char* |
| arc_linux_register_name (struct gdbarch *gdbarch, int gdb_regno) |
| { |
| gdb_assert(ELEMENTS_IN_ARRAY(register_names) == (unsigned int) (ARC_NR_REGS + ARC_NR_PSEUDO_REGS)); |
| |
| /* 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 register_names[gdb_regno]; |
| } |
| |
| |
| /* Determine whether the given register is read-only. */ |
| |
| static int |
| arc_linux_cannot_store_register (struct gdbarch *gdbarch, int gdb_regno) |
| { |
| 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 |
| arc_linux_write_pc (struct regcache *regcache, CORE_ADDR pc) |
| { |
| 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 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. */ |
| |
| 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, |
| gdb sets a breakpoint at the return address, and continues. */ |
| |
| /* Lookup_minimal_symbol didn't work, for some reason. */ |
| struct symbol *resolver = |
| lookup_symbol_global ("_dl_linux_resolver", 0, 0, VAR_DOMAIN, 0); |
| |
| 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)))); |
| |
| 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 |
| have only ARCompact. */ |
| |
| static void |
| arc_linux_supply_gregset (const struct regset *regset, |
| struct regcache *regcache, |
| int regnum, |
| const void *gregs, |
| size_t 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 const struct regset * |
| arc_linux_regset_from_core_section (struct gdbarch *core_arch, |
| const char *sect_name, |
| size_t sect_size) |
| { |
| 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; |
| } |
| |
| |
| /* Initialize for this ABI. */ |
| |
| static void |
| arc_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) |
| { |
| 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); |
| } |
| |
| |
| /* -------------------------------------------------------------------------- */ |
| /* externally visible functions */ |
| /* -------------------------------------------------------------------------- */ |
| |
| /* Initialize the module. This function is called from the gdb core on start-up. */ |
| |
| 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); |
| } |
| |
| |
| /* This function is required simply to avoid an undefined symbol at linkage. */ |
| |
| void |
| arc_check_pc_defined (struct gdbarch *gdbarch) |
| { |
| } |
| |
| /******************************************************************************/ |