diff options
author | Anton Kolesov <Anton.Kolesov@synopsys.com> | 2016-12-22 21:52:16 +0300 |
---|---|---|
committer | Shahab Vahedi <shahab@synopsys.com> | 2020-12-22 12:05:19 +0100 |
commit | d4af727286e3a9f177ba11677fbd3a012d36558a (patch) | |
tree | 4b4295e77472494ddacf8f2605310ffc43a47cef /gdb | |
parent | b4e3cd0440109d0a5552d3313ccbd35c8103335b (diff) | |
download | fsf-binutils-gdb-d4af727286e3a9f177ba11677fbd3a012d36558a.zip fsf-binutils-gdb-d4af727286e3a9f177ba11677fbd3a012d36558a.tar.gz fsf-binutils-gdb-d4af727286e3a9f177ba11677fbd3a012d36558a.tar.bz2 |
arc: Add support for signal frames for Linux targets
Implement functions needed to unwind signal frames on ARC Linux targets.
gdb/ChangeLog
* arc-linux-tdep.c (arc_linux_sc_reg_offsets): New static variable.
(arc_linux_is_sigtramp): New function.
(arc_linux_sigcontext_addr): Likewise.
(arc_linux_init_osabi): Use them.
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ChangeLog | 7 | ||||
-rw-r--r-- | gdb/arc-linux-tdep.c | 181 |
2 files changed, 188 insertions, 0 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index ba574d7..05c82f6 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,12 @@ 2020-12-22 Anton Kolesov <anton.kolesov@synopsys.com> + * arc-linux-tdep.c (arc_linux_sc_reg_offsets): New static variable. + (arc_linux_is_sigtramp): New function. + (arc_linux_sigcontext_addr): Likewise. + (arc_linux_init_osabi): Use them. + +2020-12-22 Anton Kolesov <anton.kolesov@synopsys.com> + * arc-tdep.c (arc_make_sigtramp_frame_cache): New function. (arc_sigtramp_frame_this_id): Likewise. (arc_sigtramp_frame_prev_register): Likewise. diff --git a/gdb/arc-linux-tdep.c b/gdb/arc-linux-tdep.c index 2bdeaaf..c86bd60 100644 --- a/gdb/arc-linux-tdep.c +++ b/gdb/arc-linux-tdep.c @@ -33,6 +33,60 @@ #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. @@ -87,6 +141,127 @@ static const int arc_linux_core_reg_offsets[] = { 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 (struct frame_info *this_frame) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + CORE_ADDR pc = get_frame_pc (this_frame); + + if (arc_debug) + { + debug_printf ("arc-linux: arc_linux_is_sigtramp, pc=%s\n", + 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 (struct frame_info *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 @@ -430,6 +605,12 @@ arc_linux_init_osabi (struct gdbarch_info info, struct gdbarch *gdbarch) if (arc_debug) debug_printf ("arc-linux: GNU/Linux OS/ABI initialization.\n"); + /* 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): |