|  | /* Target dependent code for GNU/Linux ARC. | 
|  |  | 
|  | Copyright 2020-2023 Free Software Foundation, 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 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/>.  */ | 
|  |  | 
|  | /* GDB header files.  */ | 
|  | #include "defs.h" | 
|  | #include "linux-tdep.h" | 
|  | #include "objfiles.h" | 
|  | #include "opcode/arc.h" | 
|  | #include "osabi.h" | 
|  | #include "solib-svr4.h" | 
|  | #include "disasm.h" | 
|  |  | 
|  | /* ARC header files.  */ | 
|  | #include "opcodes/arc-dis.h" | 
|  | #include "arc-linux-tdep.h" | 
|  | #include "arc-tdep.h" | 
|  | #include "arch/arc.h" | 
|  |  | 
|  | /* Print an "arc-linux" debug statement.  */ | 
|  |  | 
|  | #define arc_linux_debug_printf(fmt, ...) \ | 
|  | debug_prefixed_printf_cond (arc_debug, "arc-linux", fmt, ##__VA_ARGS__) | 
|  |  | 
|  | #define REGOFF(offset) (offset * ARC_REGISTER_SIZE) | 
|  |  | 
|  | /* arc_linux_sc_reg_offsets[i] is the offset of register i in the `struct | 
|  | sigcontext'.  Array index is an internal GDB register number, as defined in | 
|  | arc-tdep.h:arc_regnum. | 
|  |  | 
|  | From <include/uapi/asm/sigcontext.h> and <include/uapi/asm/ptrace.h>. | 
|  |  | 
|  | The layout of this struct is tightly bound to "arc_regnum" enum | 
|  | in arc-tdep.h.  Any change of order in there, must be reflected | 
|  | here as well.  */ | 
|  | static const int arc_linux_sc_reg_offsets[] = { | 
|  | /* R0 - R12.  */ | 
|  | REGOFF (22), REGOFF (21), REGOFF (20), REGOFF (19), | 
|  | REGOFF (18), REGOFF (17), REGOFF (16), REGOFF (15), | 
|  | REGOFF (14), REGOFF (13), REGOFF (12), REGOFF (11), | 
|  | REGOFF (10), | 
|  |  | 
|  | /* R13 - R25.  */ | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, | 
|  |  | 
|  | REGOFF (9),			/* R26 (GP) */ | 
|  | REGOFF (8),			/* FP */ | 
|  | REGOFF (23),			/* SP */ | 
|  | ARC_OFFSET_NO_REGISTER,	/* ILINK */ | 
|  | ARC_OFFSET_NO_REGISTER,	/* R30 */ | 
|  | REGOFF (7),			/* BLINK */ | 
|  |  | 
|  | /* R32 - R59.  */ | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, | 
|  |  | 
|  | REGOFF (4),			/* LP_COUNT */ | 
|  | ARC_OFFSET_NO_REGISTER,	/* RESERVED */ | 
|  | ARC_OFFSET_NO_REGISTER,	/* LIMM */ | 
|  | ARC_OFFSET_NO_REGISTER,	/* PCL */ | 
|  |  | 
|  | REGOFF (6),			/* PC  */ | 
|  | REGOFF (5),			/* STATUS32 */ | 
|  | REGOFF (2),			/* LP_START */ | 
|  | REGOFF (3),			/* LP_END */ | 
|  | REGOFF (1),			/* BTA */ | 
|  | }; | 
|  |  | 
|  | /* arc_linux_core_reg_offsets[i] is the offset in the .reg section of GDB | 
|  | regnum i.  Array index is an internal GDB register number, as defined in | 
|  | arc-tdep.h:arc_regnum. | 
|  |  | 
|  | From include/uapi/asm/ptrace.h in the ARC Linux sources.  */ | 
|  |  | 
|  | /* The layout of this struct is tightly bound to "arc_regnum" enum | 
|  | in arc-tdep.h.  Any change of order in there, must be reflected | 
|  | here as well.  */ | 
|  | static const int arc_linux_core_reg_offsets[] = { | 
|  | /* R0 - R12.  */ | 
|  | REGOFF (22), REGOFF (21), REGOFF (20), REGOFF (19), | 
|  | REGOFF (18), REGOFF (17), REGOFF (16), REGOFF (15), | 
|  | REGOFF (14), REGOFF (13), REGOFF (12), REGOFF (11), | 
|  | REGOFF (10), | 
|  |  | 
|  | /* R13 - R25.  */ | 
|  | REGOFF (37), REGOFF (36), REGOFF (35), REGOFF (34), | 
|  | REGOFF (33), REGOFF (32), REGOFF (31), REGOFF (30), | 
|  | REGOFF (29), REGOFF (28), REGOFF (27), REGOFF (26), | 
|  | REGOFF (25), | 
|  |  | 
|  | REGOFF (9),			/* R26 (GP) */ | 
|  | REGOFF (8),			/* FP */ | 
|  | REGOFF (23),			/* SP */ | 
|  | ARC_OFFSET_NO_REGISTER,	/* ILINK */ | 
|  | ARC_OFFSET_NO_REGISTER,	/* R30 */ | 
|  | REGOFF (7),			/* BLINK */ | 
|  |  | 
|  | /* R32 - R59.  */ | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, ARC_OFFSET_NO_REGISTER, | 
|  | ARC_OFFSET_NO_REGISTER, | 
|  |  | 
|  | REGOFF (4),			/* LP_COUNT */ | 
|  | ARC_OFFSET_NO_REGISTER,	/* RESERVED */ | 
|  | ARC_OFFSET_NO_REGISTER,	/* LIMM */ | 
|  | ARC_OFFSET_NO_REGISTER,	/* PCL */ | 
|  |  | 
|  | REGOFF (39),			/* PC  */ | 
|  | REGOFF (5),			/* STATUS32 */ | 
|  | REGOFF (2),			/* LP_START */ | 
|  | REGOFF (3),			/* LP_END */ | 
|  | REGOFF (1),			/* BTA */ | 
|  | REGOFF (6)			/* ERET */ | 
|  | }; | 
|  |  | 
|  | /* Is THIS_FRAME a sigtramp function - the function that returns from | 
|  | signal handler into normal execution flow? This is the case if the PC is | 
|  | either at the start of, or in the middle of the two instructions: | 
|  |  | 
|  | mov r8, __NR_rt_sigreturn ; __NR_rt_sigreturn == 139 | 
|  | trap_s 0 ; `swi' for ARC700 | 
|  |  | 
|  | On ARC uClibc Linux this function is called __default_rt_sa_restorer. | 
|  |  | 
|  | Returns TRUE if this is a sigtramp frame.  */ | 
|  |  | 
|  | static bool | 
|  | arc_linux_is_sigtramp (frame_info_ptr this_frame) | 
|  | { | 
|  | struct gdbarch *gdbarch = get_frame_arch (this_frame); | 
|  | CORE_ADDR pc = get_frame_pc (this_frame); | 
|  |  | 
|  | arc_linux_debug_printf ("pc=%s", paddress(gdbarch, pc)); | 
|  |  | 
|  | static const gdb_byte insns_be_hs[] = { | 
|  | 0x20, 0x8a, 0x12, 0xc2,	/* mov  r8,nr_rt_sigreturn */ | 
|  | 0x78, 0x1e			/* trap_s 0 */ | 
|  | }; | 
|  | static const gdb_byte insns_be_700[] = { | 
|  | 0x20, 0x8a, 0x12, 0xc2,	/* mov  r8,nr_rt_sigreturn */ | 
|  | 0x22, 0x6f, 0x00, 0x3f	/* swi */ | 
|  | }; | 
|  |  | 
|  | gdb_byte arc_sigtramp_insns[sizeof (insns_be_700)]; | 
|  | size_t insns_sz; | 
|  | if (arc_mach_is_arcv2 (gdbarch)) | 
|  | { | 
|  | insns_sz = sizeof (insns_be_hs); | 
|  | memcpy (arc_sigtramp_insns, insns_be_hs, insns_sz); | 
|  | } | 
|  | else | 
|  | { | 
|  | insns_sz = sizeof (insns_be_700); | 
|  | memcpy (arc_sigtramp_insns, insns_be_700, insns_sz); | 
|  | } | 
|  | if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_LITTLE) | 
|  | { | 
|  | /* On little endian targets, ARC code section is in what is called | 
|  | "middle endian", where half-words are in the big-endian order, | 
|  | only bytes inside the halfwords are in the little endian order. | 
|  | As a result it is very easy to convert big endian instruction to | 
|  | little endian, since it is needed to swap bytes in the halfwords, | 
|  | so there is no need to have information on whether that is a | 
|  | 4-byte instruction or 2-byte.  */ | 
|  | gdb_assert ((insns_sz % 2) == 0); | 
|  | for (int i = 0; i < insns_sz; i += 2) | 
|  | std::swap (arc_sigtramp_insns[i], arc_sigtramp_insns[i+1]); | 
|  | } | 
|  |  | 
|  | gdb_byte buf[insns_sz]; | 
|  |  | 
|  | /* Read the memory at the PC.  Since we are stopped, any breakpoint must | 
|  | have been removed.  */ | 
|  | if (!safe_frame_unwind_memory (this_frame, pc, {buf, insns_sz})) | 
|  | { | 
|  | /* Failed to unwind frame.  */ | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | /* Is that code the sigtramp instruction sequence?  */ | 
|  | if (memcmp (buf, arc_sigtramp_insns, insns_sz) == 0) | 
|  | return TRUE; | 
|  |  | 
|  | /* No - look one instruction earlier in the code...  */ | 
|  | if (!safe_frame_unwind_memory (this_frame, pc - 4, {buf, insns_sz})) | 
|  | { | 
|  | /* Failed to unwind frame.  */ | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | return (memcmp (buf, arc_sigtramp_insns, insns_sz) == 0); | 
|  | } | 
|  |  | 
|  | /* Get sigcontext structure of sigtramp frame - it contains saved | 
|  | registers of interrupted frame. | 
|  |  | 
|  | Stack pointer points to the rt_sigframe structure, and sigcontext can | 
|  | be found as in: | 
|  |  | 
|  | struct rt_sigframe { | 
|  | struct siginfo info; | 
|  | struct ucontext uc; | 
|  | ... | 
|  | }; | 
|  |  | 
|  | struct ucontext { | 
|  | unsigned long uc_flags; | 
|  | struct ucontext *uc_link; | 
|  | stack_t uc_stack; | 
|  | struct sigcontext uc_mcontext; | 
|  | sigset_t uc_sigmask; | 
|  | }; | 
|  |  | 
|  | sizeof (struct siginfo) == 0x80 | 
|  | offsetof (struct ucontext, uc_mcontext) == 0x14 | 
|  |  | 
|  | GDB cannot include linux headers and use offsetof () because those are | 
|  | target headers and GDB might be built for a different run host.  There | 
|  | doesn't seem to be an established mechanism to figure out those offsets | 
|  | via gdbserver, so the only way is to hardcode values in the GDB, | 
|  | meaning that GDB will be broken if values will change.  That seems to | 
|  | be a very unlikely scenario and other arches (aarch64, alpha, amd64, | 
|  | etc) in GDB hardcode values.  */ | 
|  |  | 
|  | static CORE_ADDR | 
|  | arc_linux_sigcontext_addr (frame_info_ptr this_frame) | 
|  | { | 
|  | const int ucontext_offset = 0x80; | 
|  | const int sigcontext_offset = 0x14; | 
|  | return get_frame_sp (this_frame) + ucontext_offset + sigcontext_offset; | 
|  | } | 
|  |  | 
|  | /* Implement the "cannot_fetch_register" gdbarch method.  */ | 
|  |  | 
|  | static int | 
|  | arc_linux_cannot_fetch_register (struct gdbarch *gdbarch, int regnum) | 
|  | { | 
|  | /* Assume that register is readable if it is unknown.  */ | 
|  | switch (regnum) | 
|  | { | 
|  | case ARC_ILINK_REGNUM: | 
|  | case ARC_RESERVED_REGNUM: | 
|  | case ARC_LIMM_REGNUM: | 
|  | return true; | 
|  | case ARC_R30_REGNUM: | 
|  | case ARC_R58_REGNUM: | 
|  | case ARC_R59_REGNUM: | 
|  | return !arc_mach_is_arcv2 (gdbarch); | 
|  | } | 
|  | return (regnum > ARC_BLINK_REGNUM) && (regnum < ARC_LP_COUNT_REGNUM); | 
|  | } | 
|  |  | 
|  | /* Implement the "cannot_store_register" gdbarch method.  */ | 
|  |  | 
|  | static int | 
|  | arc_linux_cannot_store_register (struct gdbarch *gdbarch, int regnum) | 
|  | { | 
|  | /* Assume that register is writable if it is unknown.  */ | 
|  | switch (regnum) | 
|  | { | 
|  | case ARC_ILINK_REGNUM: | 
|  | case ARC_RESERVED_REGNUM: | 
|  | case ARC_LIMM_REGNUM: | 
|  | case ARC_PCL_REGNUM: | 
|  | return true; | 
|  | case ARC_R30_REGNUM: | 
|  | case ARC_R58_REGNUM: | 
|  | case ARC_R59_REGNUM: | 
|  | return !arc_mach_is_arcv2 (gdbarch); | 
|  | } | 
|  | return (regnum > ARC_BLINK_REGNUM) && (regnum < ARC_LP_COUNT_REGNUM); | 
|  | } | 
|  |  | 
|  | /* For ARC Linux, breakpoints use the 16-bit TRAP_S 1 instruction, which | 
|  | is 0x3e78 (little endian) or 0x783e (big endian).  */ | 
|  |  | 
|  | static const gdb_byte arc_linux_trap_s_be[] = { 0x78, 0x3e }; | 
|  | static const gdb_byte arc_linux_trap_s_le[] = { 0x3e, 0x78 }; | 
|  | static const int trap_size = 2;   /* Number of bytes to insert "trap".  */ | 
|  |  | 
|  | /* Implement the "breakpoint_kind_from_pc" gdbarch method.  */ | 
|  |  | 
|  | static int | 
|  | arc_linux_breakpoint_kind_from_pc (struct gdbarch *gdbarch, CORE_ADDR *pcptr) | 
|  | { | 
|  | return trap_size; | 
|  | } | 
|  |  | 
|  | /* Implement the "sw_breakpoint_from_kind" gdbarch method.  */ | 
|  |  | 
|  | static const gdb_byte * | 
|  | arc_linux_sw_breakpoint_from_kind (struct gdbarch *gdbarch, | 
|  | int kind, int *size) | 
|  | { | 
|  | gdb_assert (kind == trap_size); | 
|  | *size = kind; | 
|  | return ((gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG) | 
|  | ? arc_linux_trap_s_be | 
|  | : arc_linux_trap_s_le); | 
|  | } | 
|  |  | 
|  | /* Check for an atomic sequence of instructions beginning with an | 
|  | LLOCK instruction and ending with a SCOND instruction. | 
|  |  | 
|  | These patterns are hand coded in libc's (glibc and uclibc). Take | 
|  | a look at [1] for instance: | 
|  |  | 
|  | main+14: llock   r2,[r0] | 
|  | main+18: brne.nt r2,0,main+30 | 
|  | main+22: scond   r3,[r0] | 
|  | main+26: bne     main+14 | 
|  | main+30: mov_s   r0,0 | 
|  |  | 
|  | If such a sequence is found, attempt to step over it. | 
|  | A breakpoint is placed at the end of the sequence. | 
|  |  | 
|  | This function expects the INSN to be a "llock(d)" instruction. | 
|  |  | 
|  | [1] | 
|  | https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git/tree/libc/ \ | 
|  | sysdeps/linux/arc/bits/atomic.h#n46 | 
|  | */ | 
|  |  | 
|  | static std::vector<CORE_ADDR> | 
|  | handle_atomic_sequence (arc_instruction insn, disassemble_info *di) | 
|  | { | 
|  | const int atomic_seq_len = 24;    /* Instruction sequence length.  */ | 
|  | std::vector<CORE_ADDR> next_pcs; | 
|  |  | 
|  | /* Sanity check.  */ | 
|  | gdb_assert (insn.insn_class == LLOCK); | 
|  |  | 
|  | /* Data size we are dealing with: LLOCK vs. LLOCKD  */ | 
|  | arc_ldst_data_size llock_data_size_mode = insn.data_size_mode; | 
|  | /* Indicator if any conditional branch is found in the sequence.  */ | 
|  | bool found_bc = false; | 
|  | /* Becomes true if "LLOCK(D) .. SCOND(D)" sequence is found.  */ | 
|  | bool is_pattern_valid = false; | 
|  |  | 
|  | for (int insn_count = 0; insn_count < atomic_seq_len; ++insn_count) | 
|  | { | 
|  | arc_insn_decode (arc_insn_get_linear_next_pc (insn), | 
|  | di, arc_delayed_print_insn, &insn); | 
|  |  | 
|  | if (insn.insn_class == BRCC) | 
|  | { | 
|  | /* If more than one conditional branch is found, this is not | 
|  | the pattern we are interested in.  */ | 
|  | if (found_bc) | 
|  | break; | 
|  | found_bc = true; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | /* This is almost a happy ending.  */ | 
|  | if (insn.insn_class == SCOND) | 
|  | { | 
|  | /* SCOND should match the LLOCK's data size.  */ | 
|  | if (insn.data_size_mode == llock_data_size_mode) | 
|  | is_pattern_valid = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (is_pattern_valid) | 
|  | { | 
|  | /* Get next instruction after scond(d).  There is no limm.  */ | 
|  | next_pcs.push_back (insn.address + insn.length); | 
|  | } | 
|  |  | 
|  | return next_pcs; | 
|  | } | 
|  |  | 
|  | /* Implement the "software_single_step" gdbarch method.  */ | 
|  |  | 
|  | static std::vector<CORE_ADDR> | 
|  | arc_linux_software_single_step (struct regcache *regcache) | 
|  | { | 
|  | struct gdbarch *gdbarch = regcache->arch (); | 
|  | arc_gdbarch_tdep *tdep = gdbarch_tdep<arc_gdbarch_tdep> (gdbarch); | 
|  | struct gdb_non_printing_memory_disassembler dis (gdbarch); | 
|  |  | 
|  | /* Read current instruction.  */ | 
|  | struct arc_instruction curr_insn; | 
|  | arc_insn_decode (regcache_read_pc (regcache), dis.disasm_info (), | 
|  | arc_delayed_print_insn, &curr_insn); | 
|  |  | 
|  | if (curr_insn.insn_class == LLOCK) | 
|  | return handle_atomic_sequence (curr_insn, dis.disasm_info ()); | 
|  |  | 
|  | CORE_ADDR next_pc = arc_insn_get_linear_next_pc (curr_insn); | 
|  | std::vector<CORE_ADDR> next_pcs; | 
|  |  | 
|  | /* For instructions with delay slots, the fall thru is not the | 
|  | instruction immediately after the current instruction, but the one | 
|  | after that.  */ | 
|  | if (curr_insn.has_delay_slot) | 
|  | { | 
|  | struct arc_instruction next_insn; | 
|  | arc_insn_decode (next_pc, dis.disasm_info (), arc_delayed_print_insn, | 
|  | &next_insn); | 
|  | next_pcs.push_back (arc_insn_get_linear_next_pc (next_insn)); | 
|  | } | 
|  | else | 
|  | next_pcs.push_back (next_pc); | 
|  |  | 
|  | ULONGEST status32; | 
|  | regcache_cooked_read_unsigned (regcache, gdbarch_ps_regnum (gdbarch), | 
|  | &status32); | 
|  |  | 
|  | if (curr_insn.is_control_flow) | 
|  | { | 
|  | CORE_ADDR branch_pc = arc_insn_get_branch_target (curr_insn); | 
|  | if (branch_pc != next_pc) | 
|  | next_pcs.push_back (branch_pc); | 
|  | } | 
|  | /* Is current instruction the last in a loop body?  */ | 
|  | else if (tdep->has_hw_loops) | 
|  | { | 
|  | /* If STATUS32.L is 1, then ZD-loops are disabled.  */ | 
|  | if ((status32 & ARC_STATUS32_L_MASK) == 0) | 
|  | { | 
|  | ULONGEST lp_end, lp_start, lp_count; | 
|  | regcache_cooked_read_unsigned (regcache, ARC_LP_START_REGNUM, | 
|  | &lp_start); | 
|  | regcache_cooked_read_unsigned (regcache, ARC_LP_END_REGNUM, &lp_end); | 
|  | regcache_cooked_read_unsigned (regcache, ARC_LP_COUNT_REGNUM, | 
|  | &lp_count); | 
|  |  | 
|  | arc_linux_debug_printf ("lp_start = %s, lp_end = %s, " | 
|  | "lp_count = %s, next_pc = %s", | 
|  | paddress (gdbarch, lp_start), | 
|  | paddress (gdbarch, lp_end), | 
|  | pulongest (lp_count), | 
|  | paddress (gdbarch, next_pc)); | 
|  |  | 
|  | if (next_pc == lp_end && lp_count > 1) | 
|  | { | 
|  | /* The instruction is in effect a jump back to the start of | 
|  | the loop.  */ | 
|  | next_pcs.push_back (lp_start); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Is this a delay slot?  Then next PC is in BTA register.  */ | 
|  | if ((status32 & ARC_STATUS32_DE_MASK) != 0) | 
|  | { | 
|  | ULONGEST bta; | 
|  | regcache_cooked_read_unsigned (regcache, ARC_BTA_REGNUM, &bta); | 
|  | next_pcs.push_back (bta); | 
|  | } | 
|  |  | 
|  | return next_pcs; | 
|  | } | 
|  |  | 
|  | /* Implement the "skip_solib_resolver" gdbarch method. | 
|  |  | 
|  | See glibc_skip_solib_resolver for details.  */ | 
|  |  | 
|  | 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.  */ | 
|  | struct bound_minimal_symbol resolver | 
|  | = lookup_minimal_symbol ("_dl_linux_resolver", NULL, NULL); | 
|  |  | 
|  | if (arc_debug) | 
|  | { | 
|  | if (resolver.minsym != nullptr) | 
|  | { | 
|  | CORE_ADDR res_addr = resolver.value_address (); | 
|  | arc_linux_debug_printf ("pc = %s, resolver at %s", | 
|  | print_core_address (gdbarch, pc), | 
|  | print_core_address (gdbarch, res_addr)); | 
|  | } | 
|  | else | 
|  | arc_linux_debug_printf ("pc = %s, no resolver found", | 
|  | print_core_address (gdbarch, pc)); | 
|  | } | 
|  |  | 
|  | if (resolver.minsym != nullptr && resolver.value_address () == pc) | 
|  | { | 
|  | /* Find the return address.  */ | 
|  | return frame_unwind_caller_pc (get_current_frame ()); | 
|  | } | 
|  | else | 
|  | { | 
|  | /* No breakpoint required.  */ | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Populate REGCACHE with register REGNUM from BUF.  */ | 
|  |  | 
|  | static void | 
|  | supply_register (struct regcache *regcache, int regnum, const gdb_byte *buf) | 
|  | { | 
|  | /* Skip non-existing registers.  */ | 
|  | if ((arc_linux_core_reg_offsets[regnum] == ARC_OFFSET_NO_REGISTER)) | 
|  | return; | 
|  |  | 
|  | regcache->raw_supply (regnum, buf + arc_linux_core_reg_offsets[regnum]); | 
|  | } | 
|  |  | 
|  | void | 
|  | arc_linux_supply_gregset (const struct regset *regset, | 
|  | struct regcache *regcache, | 
|  | int regnum, const void *gregs, size_t size) | 
|  | { | 
|  | gdb_static_assert (ARC_LAST_REGNUM | 
|  | < ARRAY_SIZE (arc_linux_core_reg_offsets)); | 
|  |  | 
|  | const bfd_byte *buf = (const bfd_byte *) gregs; | 
|  |  | 
|  | /* REGNUM == -1 means writing all the registers.  */ | 
|  | if (regnum == -1) | 
|  | for (int reg = 0; reg <= ARC_LAST_REGNUM; reg++) | 
|  | supply_register (regcache, reg, buf); | 
|  | else if (regnum <= ARC_LAST_REGNUM) | 
|  | supply_register (regcache, regnum, buf); | 
|  | else | 
|  | gdb_assert_not_reached ("Invalid regnum in arc_linux_supply_gregset."); | 
|  | } | 
|  |  | 
|  | void | 
|  | arc_linux_supply_v2_regset (const struct regset *regset, | 
|  | struct regcache *regcache, int regnum, | 
|  | const void *v2_regs, size_t size) | 
|  | { | 
|  | const bfd_byte *buf = (const bfd_byte *) v2_regs; | 
|  |  | 
|  | /* user_regs_arcv2 is defined in linux arch/arc/include/uapi/asm/ptrace.h.  */ | 
|  | if (regnum == -1 || regnum == ARC_R30_REGNUM) | 
|  | regcache->raw_supply (ARC_R30_REGNUM, buf); | 
|  | if (regnum == -1 || regnum == ARC_R58_REGNUM) | 
|  | regcache->raw_supply (ARC_R58_REGNUM, buf + REGOFF (1)); | 
|  | if (regnum == -1 || regnum == ARC_R59_REGNUM) | 
|  | regcache->raw_supply (ARC_R59_REGNUM, buf + REGOFF (2)); | 
|  | } | 
|  |  | 
|  | /* Populate BUF with register REGNUM from the REGCACHE.  */ | 
|  |  | 
|  | static void | 
|  | collect_register (const struct regcache *regcache, struct gdbarch *gdbarch, | 
|  | int regnum, gdb_byte *buf) | 
|  | { | 
|  | int offset; | 
|  |  | 
|  | /* Skip non-existing registers.  */ | 
|  | if (arc_linux_core_reg_offsets[regnum] == ARC_OFFSET_NO_REGISTER) | 
|  | return; | 
|  |  | 
|  | /* The address where the execution has stopped is in pseudo-register | 
|  | STOP_PC.  However, when kernel code is returning from the exception, | 
|  | it uses the value from ERET register.  Since, TRAP_S (the breakpoint | 
|  | instruction) commits, the ERET points to the next instruction.  In | 
|  | other words: ERET != STOP_PC.  To jump back from the kernel code to | 
|  | the correct address, ERET must be overwritten by GDB's STOP_PC.  Else, | 
|  | the program will continue at the address after the current instruction. | 
|  | */ | 
|  | if (regnum == gdbarch_pc_regnum (gdbarch)) | 
|  | offset = arc_linux_core_reg_offsets[ARC_ERET_REGNUM]; | 
|  | else | 
|  | offset = arc_linux_core_reg_offsets[regnum]; | 
|  | regcache->raw_collect (regnum, buf + offset); | 
|  | } | 
|  |  | 
|  | void | 
|  | arc_linux_collect_gregset (const struct regset *regset, | 
|  | const struct regcache *regcache, | 
|  | int regnum, void *gregs, size_t size) | 
|  | { | 
|  | gdb_static_assert (ARC_LAST_REGNUM | 
|  | < ARRAY_SIZE (arc_linux_core_reg_offsets)); | 
|  |  | 
|  | gdb_byte *buf = (gdb_byte *) gregs; | 
|  | struct gdbarch *gdbarch = regcache->arch (); | 
|  |  | 
|  | /* REGNUM == -1 means writing all the registers.  */ | 
|  | if (regnum == -1) | 
|  | for (int reg = 0; reg <= ARC_LAST_REGNUM; reg++) | 
|  | collect_register (regcache, gdbarch, reg, buf); | 
|  | else if (regnum <= ARC_LAST_REGNUM) | 
|  | collect_register (regcache, gdbarch, regnum, buf); | 
|  | else | 
|  | gdb_assert_not_reached ("Invalid regnum in arc_linux_collect_gregset."); | 
|  | } | 
|  |  | 
|  | void | 
|  | arc_linux_collect_v2_regset (const struct regset *regset, | 
|  | const struct regcache *regcache, int regnum, | 
|  | void *v2_regs, size_t size) | 
|  | { | 
|  | bfd_byte *buf = (bfd_byte *) v2_regs; | 
|  |  | 
|  | if (regnum == -1 || regnum == ARC_R30_REGNUM) | 
|  | regcache->raw_collect (ARC_R30_REGNUM, buf); | 
|  | if (regnum == -1 || regnum == ARC_R58_REGNUM) | 
|  | regcache->raw_collect (ARC_R58_REGNUM, buf + REGOFF (1)); | 
|  | if (regnum == -1 || regnum == ARC_R59_REGNUM) | 
|  | regcache->raw_collect (ARC_R59_REGNUM, buf + REGOFF (2)); | 
|  | } | 
|  |  | 
|  | /* Linux regset definitions.  */ | 
|  |  | 
|  | static const struct regset arc_linux_gregset = { | 
|  | arc_linux_core_reg_offsets, | 
|  | arc_linux_supply_gregset, | 
|  | arc_linux_collect_gregset, | 
|  | }; | 
|  |  | 
|  | static const struct regset arc_linux_v2_regset = { | 
|  | NULL, | 
|  | arc_linux_supply_v2_regset, | 
|  | arc_linux_collect_v2_regset, | 
|  | }; | 
|  |  | 
|  | /* Implement the `iterate_over_regset_sections` gdbarch method.  */ | 
|  |  | 
|  | static void | 
|  | arc_linux_iterate_over_regset_sections (struct gdbarch *gdbarch, | 
|  | iterate_over_regset_sections_cb *cb, | 
|  | void *cb_data, | 
|  | const struct regcache *regcache) | 
|  | { | 
|  | /* There are 40 registers in Linux user_regs_struct, although some of | 
|  | them are now just a mere paddings, kept to maintain binary | 
|  | compatibility with older tools.  */ | 
|  | const int sizeof_gregset = 40 * ARC_REGISTER_SIZE; | 
|  |  | 
|  | cb (".reg", sizeof_gregset, sizeof_gregset, &arc_linux_gregset, NULL, | 
|  | cb_data); | 
|  | cb (".reg-arc-v2", ARC_LINUX_SIZEOF_V2_REGSET, ARC_LINUX_SIZEOF_V2_REGSET, | 
|  | &arc_linux_v2_regset, NULL, cb_data); | 
|  | } | 
|  |  | 
|  | /* Implement the `core_read_description` gdbarch method.  */ | 
|  |  | 
|  | static const struct target_desc * | 
|  | arc_linux_core_read_description (struct gdbarch *gdbarch, | 
|  | struct target_ops *target, | 
|  | bfd *abfd) | 
|  | { | 
|  | arc_arch_features features | 
|  | = arc_arch_features_create (abfd, | 
|  | gdbarch_bfd_arch_info (gdbarch)->mach); | 
|  | return arc_lookup_target_description (features); | 
|  | } | 
|  |  | 
|  | /* Initialization specific to Linux environment.  */ | 
|  |  | 
|  | static void | 
|  | arc_linux_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch) | 
|  | { | 
|  | arc_gdbarch_tdep *tdep = gdbarch_tdep<arc_gdbarch_tdep> (gdbarch); | 
|  |  | 
|  | arc_linux_debug_printf ("GNU/Linux OS/ABI initialization."); | 
|  |  | 
|  | /* Fill in target-dependent info in ARC-private structure.  */ | 
|  | tdep->is_sigtramp = arc_linux_is_sigtramp; | 
|  | tdep->sigcontext_addr = arc_linux_sigcontext_addr; | 
|  | tdep->sc_reg_offset = arc_linux_sc_reg_offsets; | 
|  | tdep->sc_num_regs = ARRAY_SIZE (arc_linux_sc_reg_offsets); | 
|  |  | 
|  | /* If we are using Linux, we have in uClibc | 
|  | (libc/sysdeps/linux/arc/bits/setjmp.h): | 
|  |  | 
|  | typedef int __jmp_buf[13+1+1+1];    //r13-r25, fp, sp, blink | 
|  |  | 
|  | Where "blink" is a stored PC of a caller function. | 
|  | */ | 
|  | tdep->jb_pc = 15; | 
|  |  | 
|  | linux_init_abi (info, gdbarch, 0); | 
|  |  | 
|  | /* Set up target dependent GDB architecture entries.  */ | 
|  | set_gdbarch_cannot_fetch_register (gdbarch, arc_linux_cannot_fetch_register); | 
|  | set_gdbarch_cannot_store_register (gdbarch, arc_linux_cannot_store_register); | 
|  | set_gdbarch_breakpoint_kind_from_pc (gdbarch, | 
|  | arc_linux_breakpoint_kind_from_pc); | 
|  | set_gdbarch_sw_breakpoint_from_kind (gdbarch, | 
|  | arc_linux_sw_breakpoint_from_kind); | 
|  | set_gdbarch_fetch_tls_load_module_address (gdbarch, | 
|  | svr4_fetch_objfile_link_map); | 
|  | set_gdbarch_software_single_step (gdbarch, arc_linux_software_single_step); | 
|  | set_gdbarch_skip_trampoline_code (gdbarch, find_solib_trampoline_target); | 
|  | set_gdbarch_skip_solib_resolver (gdbarch, arc_linux_skip_solib_resolver); | 
|  | set_gdbarch_iterate_over_regset_sections | 
|  | (gdbarch, arc_linux_iterate_over_regset_sections); | 
|  | set_gdbarch_core_read_description (gdbarch, arc_linux_core_read_description); | 
|  |  | 
|  | /* GNU/Linux uses SVR4-style shared libraries, with 32-bit ints, longs | 
|  | and pointers (ILP32).  */ | 
|  | set_solib_svr4_fetch_link_map_offsets (gdbarch, | 
|  | linux_ilp32_fetch_link_map_offsets); | 
|  | } | 
|  |  | 
|  | /* Suppress warning from -Wmissing-prototypes.  */ | 
|  | extern initialize_file_ftype _initialize_arc_linux_tdep; | 
|  |  | 
|  | void | 
|  | _initialize_arc_linux_tdep () | 
|  | { | 
|  | gdbarch_register_osabi (bfd_arch_arc, 0, GDB_OSABI_LINUX, | 
|  | arc_linux_init_osabi); | 
|  | } |