binutils-gdb/gdb/tramp-frame.c
Maciej W. Rozycki 858339f2b7 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.
2014-12-03 20:57:06 +00:00

173 lines
5.1 KiB
C

/* Signal trampoline unwinder, for GDB the GNU Debugger.
Copyright (C) 2004-2014 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/>. */
#include "defs.h"
#include "tramp-frame.h"
#include "frame-unwind.h"
#include "gdbcore.h"
#include "symtab.h"
#include "objfiles.h"
#include "target.h"
#include "trad-frame.h"
#include "frame-base.h"
struct frame_data
{
const struct tramp_frame *tramp_frame;
};
struct tramp_frame_cache
{
CORE_ADDR func;
const struct tramp_frame *tramp_frame;
struct trad_frame_cache *trad_cache;
};
static struct trad_frame_cache *
tramp_frame_cache (struct frame_info *this_frame,
void **this_cache)
{
struct tramp_frame_cache *tramp_cache = (*this_cache);
if (tramp_cache->trad_cache == NULL)
{
tramp_cache->trad_cache = trad_frame_cache_zalloc (this_frame);
tramp_cache->tramp_frame->init (tramp_cache->tramp_frame,
this_frame,
tramp_cache->trad_cache,
tramp_cache->func);
}
return tramp_cache->trad_cache;
}
static void
tramp_frame_this_id (struct frame_info *this_frame,
void **this_cache,
struct frame_id *this_id)
{
struct trad_frame_cache *trad_cache
= tramp_frame_cache (this_frame, this_cache);
trad_frame_get_id (trad_cache, this_id);
}
static struct value *
tramp_frame_prev_register (struct frame_info *this_frame,
void **this_cache,
int prev_regnum)
{
struct trad_frame_cache *trad_cache
= tramp_frame_cache (this_frame, this_cache);
return trad_frame_get_register (trad_cache, this_frame, prev_regnum);
}
static CORE_ADDR
tramp_frame_start (const struct tramp_frame *tramp,
struct frame_info *this_frame, CORE_ADDR pc)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
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++)
{
CORE_ADDR func = pc - tramp->insn_size * ti;
int i;
for (i = 0; 1; i++)
{
gdb_byte buf[sizeof (tramp->insn[0])];
ULONGEST insn;
if (tramp->insn[i].bytes == TRAMP_SENTINEL_INSN)
return func;
if (!safe_frame_unwind_memory (this_frame,
func + i * tramp->insn_size,
buf, tramp->insn_size))
break;
insn = extract_unsigned_integer (buf, tramp->insn_size, byte_order);
if (tramp->insn[i].bytes != (insn & tramp->insn[i].mask))
break;
}
}
/* Trampoline doesn't match. */
return 0;
}
static int
tramp_frame_sniffer (const struct frame_unwind *self,
struct frame_info *this_frame,
void **this_cache)
{
const struct tramp_frame *tramp = self->unwind_data->tramp_frame;
CORE_ADDR pc = get_frame_pc (this_frame);
CORE_ADDR func;
struct tramp_frame_cache *tramp_cache;
/* tausq/2004-12-12: We used to assume if pc has a name or is in a valid
section, then this is not a trampoline. However, this assumption is
false on HPUX which has a signal trampoline that has a name; it can
also be false when using an alternative signal stack. */
func = tramp_frame_start (tramp, this_frame, pc);
if (func == 0)
return 0;
tramp_cache = FRAME_OBSTACK_ZALLOC (struct tramp_frame_cache);
tramp_cache->func = func;
tramp_cache->tramp_frame = tramp;
(*this_cache) = tramp_cache;
return 1;
}
void
tramp_frame_prepend_unwinder (struct gdbarch *gdbarch,
const struct tramp_frame *tramp_frame)
{
struct frame_data *data;
struct frame_unwind *unwinder;
int i;
/* Check that the instruction sequence contains a sentinel. */
for (i = 0; i < ARRAY_SIZE (tramp_frame->insn); i++)
{
if (tramp_frame->insn[i].bytes == TRAMP_SENTINEL_INSN)
break;
}
gdb_assert (i < ARRAY_SIZE (tramp_frame->insn));
gdb_assert (tramp_frame->insn_size <= sizeof (tramp_frame->insn[0].bytes));
data = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_data);
unwinder = GDBARCH_OBSTACK_ZALLOC (gdbarch, struct frame_unwind);
data->tramp_frame = tramp_frame;
unwinder->type = tramp_frame->frame_type;
unwinder->unwind_data = data;
unwinder->sniffer = tramp_frame_sniffer;
unwinder->stop_reason = default_frame_unwind_stop_reason;
unwinder->this_id = tramp_frame_this_id;
unwinder->prev_register = tramp_frame_prev_register;
frame_unwind_prepend_unwinder (gdbarch, unwinder);
}