From 858339f2b7aafe199c1cd07fc8d4a7c8130aa285 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Wed, 3 Dec 2014 19:19:41 +0000 Subject: [PATCH] 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. --- gdb/ChangeLog | 21 ++++++++ gdb/mips-linux-tdep.c | 113 +++++++++++++++++++++++++++++++++++++++--- gdb/tramp-frame.c | 4 ++ gdb/tramp-frame.h | 7 +++ 4 files changed, 139 insertions(+), 6 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index ebb36b78161..80eb8c9baad 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,24 @@ +2014-12-03 Maciej W. Rozycki + + * 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  * config/sparc/sol2.mh (NATDEPFILES): Remove core-regset.o. diff --git a/gdb/mips-linux-tdep.c b/gdb/mips-linux-tdep.c index bc42921f375..41602ba7369 100644 --- a/gdb/mips-linux-tdep.c +++ b/gdb/mips-linux-tdep.c @@ -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 == µmips_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 == µmips_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, µmips_linux_o32_sigframe); + tramp_frame_prepend_unwinder (gdbarch, + µmips_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, + µmips_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, + µmips_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; diff --git a/gdb/tramp-frame.c b/gdb/tramp-frame.c index 0fd6ddccc2f..a0e3eb5ebef 100644 --- a/gdb/tramp-frame.c +++ b/gdb/tramp-frame.c @@ -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++) diff --git a/gdb/tramp-frame.h b/gdb/tramp-frame.h index eeaf7b55147..d659c0b7ee5 100644 --- a/gdb/tramp-frame.h +++ b/gdb/tramp-frame.h @@ -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, -- 2.30.2