/* DWARF2 EH unwinding support for Alpha Tru64.
Copyright (C) 2010 Free Software Foundation, Inc.
This file is part of GCC.
GCC 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, or (at your option) any later
version.
GCC 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 GCC; see the file COPYING3. If not see
. */
/* This file implements the MD_FALLBACK_FRAME_STATE_FOR macro, triggered when
the GCC table based unwinding process hits a frame for which no unwind info
has been registered. This typically occurs when raising an exception from a
signal handler, because the handler is actually called from the OS kernel.
The basic idea is to detect that we are indeed trying to unwind past a
signal handler and to fill out the GCC internal unwinding structures for
the OS kernel frame as if it had been directly called from the interrupted
context.
This is all assuming that the code to set the handler asked the kernel to
pass a pointer to such context information. */
/* --------------------------------------------------------------------------
-- Basic principles of operation:
--------------------------------------------------------------------------
1/ We first need a way to detect if we are trying to unwind past a signal
handler.
The typical method that is used on most platforms is to look at the code
around the return address we have and check if it matches the OS code
calling a handler. To determine what this code is expected to be, get a
breakpoint into a real signal handler and look at the code around the
return address. Depending on the library versions the pattern of the
signal handler is different; this is the reason why we check against more
than one pattern.
On this target, the return address is right after the call and every
instruction is 4 bytes long. For the simple case of a null dereference in
a single-threaded app, it went like:
# Check that we indeed have something we expect: the instruction right
# before the return address is within a __sigtramp function and is a call.
[... run gdb and break at the signal handler entry ...]
(gdb) x /i $ra-4
<__sigtramp+160>: jsr ra,(a3),0x3ff800d0ed4 <_fpdata+36468>
# Look at the code around that return address, and eventually observe a
# significantly large chunk of *constant* code right before the call:
(gdb) x /10i $ra-44
<__sigtramp+120>: lda gp,-27988(gp)
<__sigtramp+124>: ldq at,-18968(gp)
<__sigtramp+128>: lda t0,-1
<__sigtramp+132>: stq t0,0(at)
<__sigtramp+136>: ldq at,-18960(gp)
<__sigtramp+140>: ldl t1,8(at)
<__sigtramp+144>: ldq at,-18960(gp)
<__sigtramp+148>: stl t1,12(at)
<__sigtramp+152>: ldq at,-18960(gp)
<__sigtramp+156>: stl t0,8(at)
# The hexadecimal equivalent that we will have to match is:
(gdb) x /10x $ra-44
<__sigtramp+120>: 0x23bd92ac 0xa79db5e8 0x203fffff 0xb43c0000
<__sigtramp+136>: 0xa79db5f0 0xa05c0008 0xa79db5f0 0xb05c000c
<__sigtramp+152>: 0xa79db5f0 0xb03c0008
The problem observed on this target with this approach is that although
we found a constant set of instruction patterns there were some
gp-related offsets that made the machine code to differ from one
installation to another. This problem could have been overcome by masking
these offsets, but we found that it would be simpler and more efficient to
check whether the return address was part of a signal handler, by comparing
it against some expected code offset from __sigtramp.
# Check that we indeed have something we expect: the instruction
# right before the return address is within a __sigtramp
# function and is a call. We also need to obtain the offset
# between the return address and the start address of __sigtramp.
[... run gdb and break at the signal handler entry ...]
(gdb) x /2i $ra-4
<__sigtramp+160>: jsr ra,(a3),0x3ff800d0ed4 <_fpdata+36468>
<__sigtramp+164>: ldah gp,16381(ra)
(gdb) p (long)$ra - (long)&__sigtramp
$2 = 164
--------------------------------------------------------------------------
2/ Once we know we are going through a signal handler, we need a way to
retrieve information about the interrupted run-time context.
On this platform, the third handler's argument is a pointer to a structure
describing this context (struct sigcontext *). We unfortunately have no
direct way to transfer this value here, so a couple of tricks are required
to compute it.
As documented at least in some header files (e.g. sys/machine/context.h),
the structure the handler gets a pointer to is located on the stack. As of
today, while writing this macro, we have unfortunately not been able to
find a detailed description of the full stack layout at handler entry time,
so we'll have to resort to empirism :)
When unwinding here, we have the handler's CFA at hand, as part of the
current unwinding context which is one of our arguments. We presume that
for each call to a signal handler by the same kernel routine, the context's
structure location on the stack is always at the same offset from the
handler's CFA, and we compute that offset from bare observation:
For the simple case of a bare null dereference in a single-threaded app,
computing the offset was done using GNAT like this:
# Break on the first handler's instruction, before the prologue to have the
# CFA in $sp, and get there:
(gdb) b *&__gnat_error_handler
Breakpoint 1 at 0x120016090: file init.c, line 378.
(gdb) r
Program received signal SIGSEGV, Segmentation fault.
(gdb) c
Breakpoint 1, __gnat_error_handler (sig=..., sip=..., context=...)
# The displayed argument value are meaningless because we stopped before
# their final "homing". We know they are passed through $a0, $a1 and $a2
# from the ABI, though, so ...
# Observe that $sp and the context pointer are in the same (stack) area,
# and compute the offset:
(gdb) p /x $sp
$2 = 0x11fffbc80
(gdb) p /x $a2
$3 = 0x11fffbcf8
(gdb) p /x (long)$a2 - (long)$sp
$4 = 0x78
--------------------------------------------------------------------------
3/ Once we know we are unwinding through a signal handler and have the
address of the structure describing the interrupted context at hand, we
have to fill the internal frame-state/unwind-context structures properly
to allow the unwinding process to proceed.
Roughly, we are provided with an *unwinding* CONTEXT, describing the state
of some point P in the call chain we are unwinding through. The macro we
implement has to fill a "frame state" structure FS that describe the P's
caller state, by way of *rules* to compute its CFA, return address, and
**saved** registers *locations*.
For the case we are going to deal with, the caller is some kernel code
calling a signal handler, and:
o The saved registers are all in the interrupted run-time context,
o The CFA is the stack pointer value when the kernel code is entered, that
is, the stack pointer value at the interruption point, also part of the
interrupted run-time context.
o We want the return address to appear as the address of the active
instruction at the interruption point, so that the unwinder proceeds as
if the interruption had been a regular call. This address is also part
of the interrupted run-time context.
--
Also, note that there is an important difference between the return address
we need to claim for the kernel frame and the value of the return address
register at the interruption point.
The latter might be required to be able to unwind past the interrupted
routine, for instance if it is interrupted before saving the incoming
register value in its own frame, which may typically happen during stack
probes for stack-checking purposes.
It is then essential that the rules stated to locate the kernel frame
return address don't clobber the rules describing where is saved the return
address register at the interruption point, so some scratch register state
entry should be used for the former. We have DWARF_ALT_FRAME_RETURN_COLUMN
at hand exactly for that purpose.
--------------------------------------------------------------------------
4/ Depending on the context (single-threaded or multi-threaded app, ...),
the code calling the handler and the handler-cfa to interrupted-context
offset might change, so we use a simple generic data structure to track
the possible variants. */
/* This is the structure to wrap information about each possible sighandler
caller we may have to identify. */
typedef struct {
/* Expected return address when being called from a sighandler. */
void *ra_value;
/* Offset to get to the sigcontext structure from the handler's CFA
when the pattern matches. */
int cfa_to_context_offset;
} sighandler_call_t;
/* Helper macro for MD_FALLBACK_FRAME_STATE_FOR below.
Look at RA to see if it matches within a sighandler caller.
Set SIGCTX to the corresponding sigcontext structure (computed from
CFA) if it does, or to 0 otherwise. */
#define COMPUTE_SIGCONTEXT_FOR(RA,CFA,SIGCTX) \
do { \
/* Define and register the applicable patterns. */ \
extern void __sigtramp (void); \
\
sighandler_call_t sighandler_calls [] = { \
{__sigtramp + 164, 0x78} \
}; \
\
int n_patterns_to_match \
= sizeof (sighandler_calls) / sizeof (sighandler_call_t); \
\
int pn; /* pattern number */ \
\
int match = 0; /* Did last pattern match ? */ \
\
/* Try to match each pattern in turn. */ \
for (pn = 0; !match && pn < n_patterns_to_match; pn ++) \
match = ((RA) == sighandler_calls[pn].ra_value); \
\
(SIGCTX) = (struct sigcontext *) \
(match ? ((CFA) + sighandler_calls[pn - 1].cfa_to_context_offset) : 0); \
} while (0);
#include
#define REG_SP 30 /* hard reg for stack pointer */
#define REG_RA 26 /* hard reg for return address */
#define MD_FALLBACK_FRAME_STATE_FOR alpha_fallback_frame_state
static _Unwind_Reason_Code
alpha_fallback_frame_state (struct _Unwind_Context *context,
_Unwind_FrameState *fs)
{
/* Return address and CFA of the frame we're attempting to unwind through,
possibly a signal handler. */
void *ctx_ra = (void *)context->ra;
void *ctx_cfa = (void *)context->cfa;
/* CFA of the intermediate abstract kernel frame between the interrupted
code and the signal handler, if we're indeed unwinding through a signal
handler. */
void *k_cfa;
/* Pointer to the sigcontext structure pushed by the kernel when we're
unwinding through a signal handler. */
struct sigcontext *sigctx;
int i;
COMPUTE_SIGCONTEXT_FOR (ctx_ra, ctx_cfa, sigctx);
if (sigctx == 0)
return _URC_END_OF_STACK;
/* The kernel frame's CFA is exactly the stack pointer value at the
interruption point. */
k_cfa = (void *) sigctx->sc_regs [REG_SP];
/* State the rules to compute the CFA we have the value of: use the
previous CFA and offset by the difference between the two. See
uw_update_context_1 for the supporting details. */
fs->regs.cfa_how = CFA_REG_OFFSET;
fs->regs.cfa_reg = __builtin_dwarf_sp_column ();
fs->regs.cfa_offset = k_cfa - ctx_cfa;
/* Fill the internal frame_state structure with information stating
where each register of interest in the saved context can be found
from the CFA. */
/* The general registers are in sigctx->sc_regs. Leave out r31, which
is read-as-zero. It makes no sense restoring it, and we are going to
use the state entry for the kernel return address rule below.
This loop must cover at least all the callee-saved registers, and
we just don't bother specializing the set here. */
for (i = 0; i <= 30; i ++)
{
fs->regs.reg[i].how = REG_SAVED_OFFSET;
fs->regs.reg[i].loc.offset
= (void *) &sigctx->sc_regs[i] - (void *) k_cfa;
}
/* Ditto for the floating point registers in sigctx->sc_fpregs. */
for (i = 0; i <= 31; i ++)
{
fs->regs.reg[32+i].how = REG_SAVED_OFFSET;
fs->regs.reg[32+i].loc.offset
= (void *) &sigctx->sc_fpregs[i] - (void *) k_cfa;
}
/* State the rules to find the kernel's code "return address", which
is the address of the active instruction when the signal was caught,
in sigctx->sc_pc. Use DWARF_ALT_FRAME_RETURN_COLUMN since the return
address register is a general register and should be left alone. */
fs->retaddr_column = DWARF_ALT_FRAME_RETURN_COLUMN;
fs->regs.reg[DWARF_ALT_FRAME_RETURN_COLUMN].how = REG_SAVED_OFFSET;
fs->regs.reg[DWARF_ALT_FRAME_RETURN_COLUMN].loc.offset
= (void *) &sigctx->sc_pc - (void *) k_cfa;
fs->signal_frame = 1;
return _URC_NO_REASON;
}