Add support for backtracing through signal handlers on Linux/ARM. Also,

make prologue scanning code somewhat less naive about optimized code
on GNU/Linux/ARM.
This commit is contained in:
Kevin Buettner 2000-09-06 00:39:11 +00:00
parent a966dba9da
commit 2a451106e2
4 changed files with 232 additions and 14 deletions

View File

@ -1,3 +1,24 @@
2000-09-05 Kevin Buettner <kevinb@redhat.com>
* config/arm/tm-linux.h (arm_linux_sigcontext_register_address,
arm_linux_in_sigtramp): Declare.
(IN_SIGTRAMP, SIGCONTEXT_REGISTER_ADDRESS): Define.
* arm-tdep.c (SIGCONTEXT_REGISTER_ADDRESS): Define to be 0
if not already defined by tm.h.
(arm_scan_prologue): Don't assume that the prologue instructions
will be in a contiguous clump.
(arm_init_extra_frame_info): Add support for sigtramp frames.
(arm_pc_is_thumb, arm_pc_is_thumb_dummy): Change type of
`memaddr' from bfd_vma to CORE_ADDR.
* arm-linux-tdep.c (gdbcore.h, frame.h): Include.
(arm_pc_is_thumb): Declare.
(arm_linux_skip_solib_resolver): Fix printf() statement. [Which
shouldn't be there anyway.]
(ARM_LINUX_SIGRETURN_INSTR, ARM_LINUX_RT_SIGRETURN_INSTR): New
defines.
(arm_linux_in_sigtramp, arm_linux_sigcontext_register_address):
New functions.
2000-09-05 Kevin Buettner <kevinb@redhat.com>
* i386aix-nat.c (fetch_core_registers): Protoize.

View File

@ -23,12 +23,18 @@
#include "value.h"
#include "gdbtypes.h"
#include "floatformat.h"
#include "gdbcore.h"
#include "frame.h"
/* For arm_linux_skip_solib_resolver. */
#include "symtab.h"
#include "symfile.h"
#include "objfiles.h"
/* FIXME: Put in common header file shared between arm-tdep.c and
arm-linux-tdep.c */
int arm_pc_is_thumb (CORE_ADDR memaddr);
#ifdef GET_LONGJMP_TARGET
/* Figure out where the longjmp will land. We expect that we have
@ -425,7 +431,7 @@ arm_linux_skip_solib_resolver (CORE_ADDR pc)
/* Plug in functions for other kinds of resolvers here. */
result = skip_hurd_resolver (pc);
printf ("Result = 0x%08x\n");
printf ("Result = 0x%08lx\n", result);
if (result)
return result;
@ -433,6 +439,87 @@ arm_linux_skip_solib_resolver (CORE_ADDR pc)
return 0;
}
/* The constants below were determined by examining the following files
in the linux kernel sources:
arch/arm/kernel/signal.c
- see SWI_SYS_SIGRETURN and SWI_SYS_RT_SIGRETURN
include/asm-arm/unistd.h
- see __NR_sigreturn, __NR_rt_sigreturn, and __NR_SYSCALL_BASE */
#define ARM_LINUX_SIGRETURN_INSTR 0xef900077
#define ARM_LINUX_RT_SIGRETURN_INSTR 0xef9000ad
/* arm_linux_in_sigtramp determines if PC points at one of the
instructions which cause control to return to the Linux kernel upon
return from a signal handler. FUNC_NAME is unused. */
int
arm_linux_in_sigtramp (CORE_ADDR pc, char *func_name)
{
unsigned long inst;
inst = read_memory_integer (pc, 4);
return (inst == ARM_LINUX_SIGRETURN_INSTR
|| inst == ARM_LINUX_RT_SIGRETURN_INSTR);
}
/* arm_linux_sigcontext_register_address returns the address in the
sigcontext of register REGNO given a stack pointer value SP and
program counter value PC. The value 0 is returned if PC is not
pointing at one of the signal return instructions or if REGNO is
not saved in the sigcontext struct. */
CORE_ADDR
arm_linux_sigcontext_register_address (CORE_ADDR sp, CORE_ADDR pc, int regno)
{
unsigned long inst;
CORE_ADDR reg_addr = 0;
inst = read_memory_integer (pc, 4);
if (inst == ARM_LINUX_SIGRETURN_INSTR || inst == ARM_LINUX_RT_SIGRETURN_INSTR)
{
CORE_ADDR sigcontext_addr;
/* The sigcontext structure is at different places for the two
signal return instructions. For ARM_LINUX_SIGRETURN_INSTR,
it starts at the SP value. For ARM_LINUX_RT_SIGRETURN_INSTR,
it is at SP+8. For the latter instruction, it may also be
the case that the address of this structure may be determined
by reading the 4 bytes at SP, but I'm not convinced this is
reliable.
In any event, these magic constants (0 and 8) may be
determined by examining struct sigframe and struct
rt_sigframe in arch/arm/kernel/signal.c in the Linux kernel
sources. */
if (inst == ARM_LINUX_RT_SIGRETURN_INSTR)
sigcontext_addr = sp + 8;
else /* inst == ARM_LINUX_SIGRETURN_INSTR */
sigcontext_addr = sp + 0;
/* The layout of the sigcontext structure for ARM GNU/Linux is
in include/asm-arm/sigcontext.h in the Linux kernel sources.
There are three 4-byte fields which precede the saved r0
field. (This accounts for the 12 in the code below.) The
sixteen registers (4 bytes per field) follow in order. The
PSR value follows the sixteen registers which accounts for
the constant 19 below. */
if (0 <= regno && regno <= PC_REGNUM)
reg_addr = sigcontext_addr + 12 + (4 * regno);
else if (regno == PS_REGNUM)
reg_addr = sigcontext_addr + 19 * 4;
}
return reg_addr;
}
void
_initialize_arm_linux_tdep (void)
{

View File

@ -30,6 +30,31 @@
#include "dis-asm.h" /* For register flavors. */
#include <ctype.h> /* for isupper () */
/* Each OS has a different mechanism for accessing the various
registers stored in the sigcontext structure.
SIGCONTEXT_REGISTER_ADDRESS should be defined to the name (or
function pointer) which may be used to determine the addresses
of the various saved registers in the sigcontext structure.
For the ARM target, there are three parameters to this function.
The first is the pc value of the frame under consideration, the
second the stack pointer of this frame, and the last is the
register number to fetch.
If the tm.h file does not define this macro, then it's assumed that
no mechanism is needed and we define SIGCONTEXT_REGISTER_ADDRESS to
be 0.
When it comes time to multi-arching this code, see the identically
named machinery in ia64-tdep.c for an example of how it could be
done. It should not be necessary to modify the code below where
this macro is used. */
#ifndef SIGCONTEXT_REGISTER_ADDRESS
#define SIGCONTEXT_REGISTER_ADDRESS 0
#endif
extern void _initialize_arm_tdep (void);
/* Number of different reg name sets (options). */
@ -226,7 +251,7 @@ static int caller_is_thumb;
function. */
int
arm_pc_is_thumb (bfd_vma memaddr)
arm_pc_is_thumb (CORE_ADDR memaddr)
{
struct minimal_symbol *sym;
@ -250,7 +275,7 @@ arm_pc_is_thumb (bfd_vma memaddr)
dummy being called from a Thumb function. */
int
arm_pc_is_thumb_dummy (bfd_vma memaddr)
arm_pc_is_thumb_dummy (CORE_ADDR memaddr)
{
CORE_ADDR sp = read_sp ();
@ -725,14 +750,42 @@ arm_scan_prologue (struct frame_info *fi)
the symbol table, peek in the stack frame to find the PC. */
if (find_pc_partial_function (fi->pc, NULL, &prologue_start, &prologue_end))
{
/* Assume the prologue is everything between the first instruction
in the function and the first source line. */
struct symtab_and_line sal = find_pc_line (prologue_start, 0);
/* One way to find the end of the prologue (which works well
for unoptimized code) is to do the following:
if (sal.line == 0) /* no line info, use current PC */
prologue_end = fi->pc;
else if (sal.end < prologue_end) /* next line begins after fn end */
prologue_end = sal.end; /* (probably means no prologue) */
struct symtab_and_line sal = find_pc_line (prologue_start, 0);
if (sal.line == 0)
prologue_end = fi->pc;
else if (sal.end < prologue_end)
prologue_end = sal.end;
This mechanism is very accurate so long as the optimizer
doesn't move any instructions from the function body into the
prologue. If this happens, sal.end will be the last
instruction in the first hunk of prologue code just before
the first instruction that the scheduler has moved from
the body to the prologue.
In order to make sure that we scan all of the prologue
instructions, we use a slightly less accurate mechanism which
may scan more than necessary. To help compensate for this
lack of accuracy, the prologue scanning loop below contains
several clauses which'll cause the loop to terminate early if
an implausible prologue instruction is encountered.
The expression
prologue_start + 64
is a suitable endpoint since it accounts for the largest
possible prologue plus up to five instructions inserted by
the scheduler. */
if (prologue_end > prologue_start + 64)
{
prologue_end = prologue_start + 64; /* See above. */
}
}
else
{
@ -740,10 +793,7 @@ arm_scan_prologue (struct frame_info *fi)
PC is the address of the stmfd + 8. */
prologue_start = ADDR_BITS_REMOVE (read_memory_integer (fi->frame, 4))
- 8;
prologue_end = prologue_start + 64; /* This is all the insn's
that could be in the prologue,
plus room for 5 insn's inserted
by the scheduler. */
prologue_end = prologue_start + 64; /* See above. */
}
/* Now search the prologue looking for instructions that set up the
@ -833,6 +883,10 @@ arm_scan_prologue (struct frame_info *fi)
fi->fsr.regs[fp_start_reg++] = sp_offset;
}
}
else if ((insn & 0xf0000000) != 0xe0000000)
break; /* Condition not true, exit early */
else if ((insn & 0xfe200000) == 0xe8200000) /* ldm? */
break; /* Don't scan past a block load */
else
/* The optimizer might shove anything into the prologue,
so we just skip what we don't recognize. */
@ -973,6 +1027,40 @@ arm_init_extra_frame_info (int fromleaf, struct frame_info *fi)
}
else
#endif
/* Determine whether or not we're in a sigtramp frame.
Unfortunately, it isn't sufficient to test
fi->signal_handler_caller because this value is sometimes set
after invoking INIT_EXTRA_FRAME_INFO. So we test *both*
fi->signal_handler_caller and IN_SIGTRAMP to determine if we need
to use the sigcontext addresses for the saved registers.
Note: If an ARM IN_SIGTRAMP method ever needs to compare against
the name of the function, the code below will have to be changed
to first fetch the name of the function and then pass this name
to IN_SIGTRAMP. */
if (SIGCONTEXT_REGISTER_ADDRESS
&& (fi->signal_handler_caller || IN_SIGTRAMP (fi->pc, 0)))
{
CORE_ADDR sp;
if (!fi->next)
sp = read_sp();
else
sp = fi->next->frame - fi->next->frameoffset + fi->next->framesize;
for (reg = 0; reg < NUM_REGS; reg++)
fi->fsr.regs[reg] = SIGCONTEXT_REGISTER_ADDRESS (sp, fi->pc, reg);
/* FIXME: What about thumb mode? */
fi->framereg = SP_REGNUM;
fi->frame = read_memory_integer (fi->fsr.regs[fi->framereg], 4);
fi->framesize = 0;
fi->frameoffset = 0;
}
else
{
arm_scan_prologue (fi);

View File

@ -135,4 +135,26 @@ extern CORE_ADDR in_svr4_dynsym_resolve_code (CORE_ADDR pc, char *name);
#define IN_SOLIB_DYNSYM_RESOLVE_CODE in_svr4_dynsym_resolve_code */
#endif
/* When the ARM Linux kernel invokes a signal handler, the return
address points at a special instruction which'll trap back into
the kernel. These definitions are used to identify this bit of
code as a signal trampoline in order to support backtracing
through calls to signal handlers. */
int arm_linux_in_sigtramp (CORE_ADDR pc, char *name);
#define IN_SIGTRAMP(pc, name) arm_linux_in_sigtramp (pc, name)
/* Each OS has different mechanisms for accessing the various
registers stored in the sigcontext structure. These definitions
provide a mechanism by which the generic code in arm-tdep.c can
find the addresses at which various registers are saved at in the
sigcontext structure. If SIGCONTEXT_REGISTER_ADDRESS is not
defined, arm-tdep.c will define it to be 0. (See ia64-tdep.c and
ia64-linux-tdep.c to see what a similar mechanism looks like when
multi-arched.) */
extern CORE_ADDR arm_linux_sigcontext_register_address (CORE_ADDR, CORE_ADDR,
int);
#define SIGCONTEXT_REGISTER_ADDRESS arm_linux_sigcontext_register_address
#endif /* TM_ARMLINUX_H */