MIPS: Add support for microMIPS Linux signal trampolines

The necessity for this change has been revealed in the course of
investigation related to proposed changes in the treatment of the ISA
bit encoded in function symbols on the MIPS target.  This change adds
support for Linux signal trampolines encoded with the microMIPS
instruction set.  Such trampolines are used by the Linux kernel if
compiled as a microMIPS binary (even if the binary run/debugged itself
contains no microMIPS code at all).

To see if we need to check whether the execution mode selected matches
the given trampoline I have checked what the bit patterns of all the
trampoline sequences decode to in the opposite instruction set.  This
produced useless or at least unusual code in most cases, for example:

microMIPS/EB, o32 sigreturn, decoded as MIPS code:
	30401017 	andi	zero,v0,0x1017
	00008b7c 	dsll32	s1,zero,0xd

MIPS/EL, o32 sigreturn, decoded as microMIPS code:
	1017 2402 	addi	zero,s7,9218
	000c 0000 	sll	zero,t0,0x0

However in some corner cases reasonable code can mimic a trampoline, for
example:

MIPS/EB, n32 rt_sigreturn, decoded as microMIPS code:
	2402      	sll	s0,s0,1
	1843 0000 	sb	v0,0(v1)
	000c 0f3c 	jr	t0

-- here the first instruction is a 16-bit one, making things nastier
even as there are some other microMIPS instructions whose first 16-bit
halfword is 0x000c and therefore matches this whole trampoline pattern.

To overcome this problem I have decided the signal trampoline unwinder
has to ask the platform backend whether it can apply a given trampoline
pattern to the code location being concerned or not.  Anticipating the
acceptance of the ISA bit proposal I decided the handler not to merely
be a predicate, but also to be able to provide an adjusted PC if
required.  I decided that returning zero will mean that the trampoline
pattern is not applicable and any other value is the adjusted PC to use;
a handler may return the value requested if the trampoline pattern and
the PC requested as-is are both accepted.

This changes the semantics of the trampoline unwinder a bit in that the
zero PC now has a special value.  I think this should be safe as a NULL
pointer is generally supposed to be invalid.

	* tramp-frame.h (tramp_frame): Add `validate' member.
	* tramp-frame.c (tramp_frame_start): Validate trampoline before
	scanning.
	* mips-linux-tdep.c (MICROMIPS_INST_LI_V0): New macro.
	(MICROMIPS_INST_POOL32A, MICROMIPS_INST_SYSCALL): Likewise.
	(mips_linux_o32_sigframe): Initialize `validate' member.
	(mips_linux_o32_rt_sigframe): Likewise.
	(mips_linux_n32_rt_sigframe): Likewise.
	(mips_linux_n64_rt_sigframe): Likewise.
	(micromips_linux_o32_sigframe): New variable.
	(micromips_linux_o32_rt_sigframe): Likewise.
	(micromips_linux_n32_rt_sigframe): Likewise.
	(micromips_linux_n64_rt_sigframe): Likewise.
	(mips_linux_o32_sigframe_init): Handle microMIPS trampolines.
	(mips_linux_n32n64_sigframe_init): Likewise.
	(mips_linux_sigframe_validate): New function.
	(micromips_linux_sigframe_validate): Likewise.
	(mips_linux_init_abi): Install microMIPS trampoline unwinders.
This commit is contained in:
Maciej W. Rozycki 2014-12-03 19:19:41 +00:00
parent db6b071a97
commit 858339f2b7
4 changed files with 139 additions and 6 deletions

View File

@ -1,3 +1,24 @@
2014-12-03 Maciej W. Rozycki <macro@codesourcery.com>
* tramp-frame.h (tramp_frame): Add `validate' member.
* tramp-frame.c (tramp_frame_start): Validate trampoline before
scanning.
* mips-linux-tdep.c (MICROMIPS_INST_LI_V0): New macro.
(MICROMIPS_INST_POOL32A, MICROMIPS_INST_SYSCALL): Likewise.
(mips_linux_o32_sigframe): Initialize `validate' member.
(mips_linux_o32_rt_sigframe): Likewise.
(mips_linux_n32_rt_sigframe): Likewise.
(mips_linux_n64_rt_sigframe): Likewise.
(micromips_linux_o32_sigframe): New variable.
(micromips_linux_o32_rt_sigframe): Likewise.
(micromips_linux_n32_rt_sigframe): Likewise.
(micromips_linux_n64_rt_sigframe): Likewise.
(mips_linux_o32_sigframe_init): Handle microMIPS trampolines.
(mips_linux_n32n64_sigframe_init): Likewise.
(mips_linux_sigframe_validate): New function.
(micromips_linux_sigframe_validate): Likewise.
(mips_linux_init_abi): Install microMIPS trampoline unwinders.
2014-12-03 Ulrich Weigand  <uweigand@de.ibm.com>
* config/sparc/sol2.mh (NATDEPFILES): Remove core-regset.o.

View File

@ -827,6 +827,14 @@ static void mips_linux_n32n64_sigframe_init (const struct tramp_frame *self,
struct trad_frame_cache *this_cache,
CORE_ADDR func);
static int mips_linux_sigframe_validate (const struct tramp_frame *self,
struct frame_info *this_frame,
CORE_ADDR *pc);
static int micromips_linux_sigframe_validate (const struct tramp_frame *self,
struct frame_info *this_frame,
CORE_ADDR *pc);
#define MIPS_NR_LINUX 4000
#define MIPS_NR_N64_LINUX 5000
#define MIPS_NR_N32_LINUX 6000
@ -842,6 +850,10 @@ static void mips_linux_n32n64_sigframe_init (const struct tramp_frame *self,
#define MIPS_INST_LI_V0_N32_RT_SIGRETURN 0x24020000 + MIPS_NR_N32_rt_sigreturn
#define MIPS_INST_SYSCALL 0x0000000c
#define MICROMIPS_INST_LI_V0 0x3040
#define MICROMIPS_INST_POOL32A 0x0000
#define MICROMIPS_INST_SYSCALL 0x8b7c
static const struct tramp_frame mips_linux_o32_sigframe = {
SIGTRAMP_FRAME,
4,
@ -850,7 +862,8 @@ static const struct tramp_frame mips_linux_o32_sigframe = {
{ MIPS_INST_SYSCALL, -1 },
{ TRAMP_SENTINEL_INSN, -1 }
},
mips_linux_o32_sigframe_init
mips_linux_o32_sigframe_init,
mips_linux_sigframe_validate
};
static const struct tramp_frame mips_linux_o32_rt_sigframe = {
@ -860,7 +873,8 @@ static const struct tramp_frame mips_linux_o32_rt_sigframe = {
{ MIPS_INST_LI_V0_RT_SIGRETURN, -1 },
{ MIPS_INST_SYSCALL, -1 },
{ TRAMP_SENTINEL_INSN, -1 } },
mips_linux_o32_sigframe_init
mips_linux_o32_sigframe_init,
mips_linux_sigframe_validate
};
static const struct tramp_frame mips_linux_n32_rt_sigframe = {
@ -871,7 +885,8 @@ static const struct tramp_frame mips_linux_n32_rt_sigframe = {
{ MIPS_INST_SYSCALL, -1 },
{ TRAMP_SENTINEL_INSN, -1 }
},
mips_linux_n32n64_sigframe_init
mips_linux_n32n64_sigframe_init,
mips_linux_sigframe_validate
};
static const struct tramp_frame mips_linux_n64_rt_sigframe = {
@ -882,7 +897,64 @@ static const struct tramp_frame mips_linux_n64_rt_sigframe = {
{ MIPS_INST_SYSCALL, -1 },
{ TRAMP_SENTINEL_INSN, -1 }
},
mips_linux_n32n64_sigframe_init
mips_linux_n32n64_sigframe_init,
mips_linux_sigframe_validate
};
static const struct tramp_frame micromips_linux_o32_sigframe = {
SIGTRAMP_FRAME,
2,
{
{ MICROMIPS_INST_LI_V0, -1 },
{ MIPS_NR_sigreturn, -1 },
{ MICROMIPS_INST_POOL32A, -1 },
{ MICROMIPS_INST_SYSCALL, -1 },
{ TRAMP_SENTINEL_INSN, -1 }
},
mips_linux_o32_sigframe_init,
micromips_linux_sigframe_validate
};
static const struct tramp_frame micromips_linux_o32_rt_sigframe = {
SIGTRAMP_FRAME,
2,
{
{ MICROMIPS_INST_LI_V0, -1 },
{ MIPS_NR_rt_sigreturn, -1 },
{ MICROMIPS_INST_POOL32A, -1 },
{ MICROMIPS_INST_SYSCALL, -1 },
{ TRAMP_SENTINEL_INSN, -1 }
},
mips_linux_o32_sigframe_init,
micromips_linux_sigframe_validate
};
static const struct tramp_frame micromips_linux_n32_rt_sigframe = {
SIGTRAMP_FRAME,
2,
{
{ MICROMIPS_INST_LI_V0, -1 },
{ MIPS_NR_N32_rt_sigreturn, -1 },
{ MICROMIPS_INST_POOL32A, -1 },
{ MICROMIPS_INST_SYSCALL, -1 },
{ TRAMP_SENTINEL_INSN, -1 }
},
mips_linux_n32n64_sigframe_init,
micromips_linux_sigframe_validate
};
static const struct tramp_frame micromips_linux_n64_rt_sigframe = {
SIGTRAMP_FRAME,
2,
{
{ MICROMIPS_INST_LI_V0, -1 },
{ MIPS_NR_N64_rt_sigreturn, -1 },
{ MICROMIPS_INST_POOL32A, -1 },
{ MICROMIPS_INST_SYSCALL, -1 },
{ TRAMP_SENTINEL_INSN, -1 }
},
mips_linux_n32n64_sigframe_init,
micromips_linux_sigframe_validate
};
/* *INDENT-OFF* */
@ -1002,7 +1074,8 @@ mips_linux_o32_sigframe_init (const struct tramp_frame *self,
const struct mips_regnum *regs = mips_regnum (gdbarch);
CORE_ADDR regs_base;
if (self == &mips_linux_o32_sigframe)
if (self == &mips_linux_o32_sigframe
|| self == &micromips_linux_o32_sigframe)
sigcontext_base = frame_sp + SIGFRAME_SIGCONTEXT_OFFSET;
else
sigcontext_base = frame_sp + RTSIGFRAME_SIGCONTEXT_OFFSET;
@ -1203,7 +1276,8 @@ mips_linux_n32n64_sigframe_init (const struct tramp_frame *self,
CORE_ADDR sigcontext_base;
const struct mips_regnum *regs = mips_regnum (gdbarch);
if (self == &mips_linux_n32_rt_sigframe)
if (self == &mips_linux_n32_rt_sigframe
|| self == &micromips_linux_n32_rt_sigframe)
sigcontext_base = frame_sp + N32_SIGFRAME_SIGCONTEXT_OFFSET;
else
sigcontext_base = frame_sp + N64_SIGFRAME_SIGCONTEXT_OFFSET;
@ -1273,6 +1347,26 @@ mips_linux_n32n64_sigframe_init (const struct tramp_frame *self,
trad_frame_set_id (this_cache, frame_id_build (frame_sp, func));
}
/* Implement struct tramp_frame's "validate" method for standard MIPS code. */
static int
mips_linux_sigframe_validate (const struct tramp_frame *self,
struct frame_info *this_frame,
CORE_ADDR *pc)
{
return mips_pc_is_mips (*pc);
}
/* Implement struct tramp_frame's "validate" method for microMIPS code. */
static int
micromips_linux_sigframe_validate (const struct tramp_frame *self,
struct frame_info *this_frame,
CORE_ADDR *pc)
{
return mips_pc_is_micromips (get_frame_arch (this_frame), *pc);
}
/* Implement the "write_pc" gdbarch method. */
static void
@ -1556,6 +1650,9 @@ mips_linux_init_abi (struct gdbarch_info info,
mips_linux_get_longjmp_target);
set_solib_svr4_fetch_link_map_offsets
(gdbarch, svr4_ilp32_fetch_link_map_offsets);
tramp_frame_prepend_unwinder (gdbarch, &micromips_linux_o32_sigframe);
tramp_frame_prepend_unwinder (gdbarch,
&micromips_linux_o32_rt_sigframe);
tramp_frame_prepend_unwinder (gdbarch, &mips_linux_o32_sigframe);
tramp_frame_prepend_unwinder (gdbarch, &mips_linux_o32_rt_sigframe);
set_xml_syscall_file_name (gdbarch, "syscalls/mips-o32-linux.xml");
@ -1571,6 +1668,8 @@ mips_linux_init_abi (struct gdbarch_info info,
except that the quiet/signalling NaN bit is reversed (GDB
does not distinguish between quiet and signalling NaNs). */
set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
tramp_frame_prepend_unwinder (gdbarch,
&micromips_linux_n32_rt_sigframe);
tramp_frame_prepend_unwinder (gdbarch, &mips_linux_n32_rt_sigframe);
set_xml_syscall_file_name (gdbarch, "syscalls/mips-n32-linux.xml");
break;
@ -1585,6 +1684,8 @@ mips_linux_init_abi (struct gdbarch_info info,
except that the quiet/signalling NaN bit is reversed (GDB
does not distinguish between quiet and signalling NaNs). */
set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
tramp_frame_prepend_unwinder (gdbarch,
&micromips_linux_n64_rt_sigframe);
tramp_frame_prepend_unwinder (gdbarch, &mips_linux_n64_rt_sigframe);
set_xml_syscall_file_name (gdbarch, "syscalls/mips-n64-linux.xml");
break;

View File

@ -86,6 +86,10 @@ tramp_frame_start (const struct tramp_frame *tramp,
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
int ti;
/* Check if we can use this trampoline. */
if (tramp->validate && !tramp->validate (tramp, this_frame, &pc))
return 0;
/* Search through the trampoline for one that matches the
instruction sequence around PC. */
for (ti = 0; tramp->insn[ti].bytes != TRAMP_SENTINEL_INSN; ti++)

View File

@ -69,6 +69,13 @@ struct tramp_frame
struct frame_info *this_frame,
struct trad_frame_cache *this_cache,
CORE_ADDR func);
/* Return non-zero if the tramp-frame is valid for the PC requested.
Adjust the PC to point to the address to check the instruction
sequence against if required. If this is NULL, then the tramp-frame
is valid for any PC. */
int (*validate) (const struct tramp_frame *self,
struct frame_info *this_frame,
CORE_ADDR *pc);
};
void tramp_frame_prepend_unwinder (struct gdbarch *gdbarch,