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.
This commit is contained in:
Anton Kolesov 2016-12-22 21:52:16 +03:00 committed by Shahab Vahedi
parent b4e3cd0440
commit d4af727286
2 changed files with 188 additions and 0 deletions

View File

@ -1,3 +1,10 @@
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.

View File

@ -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):