mirror of
https://github.com/tobast/libunwind-eh_elf.git
synced 2025-02-18 02:51:41 +01:00
Add support for handling signal frames on ARM Linux.
This patch add support for resuming at a certain stack frame even if signal frames are involved. For restoring the registers the trampoline (sigreturn) is used. RT and non-RT signal frames are handled for both >=2.6.18 and <2.6.18 kernels. Signed-off-by: Ken Werner <ken.werner@linaro.org>
This commit is contained in:
parent
9533ea1a6a
commit
36511d3d1f
4 changed files with 166 additions and 19 deletions
|
@ -61,7 +61,18 @@ struct unw_addr_space
|
||||||
struct cursor
|
struct cursor
|
||||||
{
|
{
|
||||||
struct dwarf_cursor dwarf; /* must be first */
|
struct dwarf_cursor dwarf; /* must be first */
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
ARM_SCF_NONE, /* no signal frame */
|
||||||
|
ARM_SCF_LINUX_SIGFRAME, /* non-RT signal frame, kernel >=2.6.18 */
|
||||||
|
ARM_SCF_LINUX_RT_SIGFRAME, /* RT signal frame, kernel >=2.6.18 */
|
||||||
|
ARM_SCF_LINUX_OLD_SIGFRAME, /* non-RT signal frame, kernel < 2.6.18 */
|
||||||
|
ARM_SCF_LINUX_OLD_RT_SIGFRAME /* RT signal frame, kernel < 2.6.18 */
|
||||||
|
}
|
||||||
|
sigcontext_format;
|
||||||
unw_word_t sigcontext_addr;
|
unw_word_t sigcontext_addr;
|
||||||
|
unw_word_t sigcontext_sp;
|
||||||
|
unw_word_t sigcontext_pc;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DWARF_GET_LOC(l) ((l).val)
|
#define DWARF_GET_LOC(l) ((l).val)
|
||||||
|
|
|
@ -24,6 +24,7 @@ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
|
||||||
#include "unwind_i.h"
|
#include "unwind_i.h"
|
||||||
|
#include "offsets.h"
|
||||||
|
|
||||||
#ifndef UNW_REMOTE_ONLY
|
#ifndef UNW_REMOTE_ONLY
|
||||||
|
|
||||||
|
@ -33,27 +34,62 @@ arm_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
struct cursor *c = (struct cursor *) cursor;
|
struct cursor *c = (struct cursor *) cursor;
|
||||||
ucontext_t *uc = c->dwarf.as_arg;
|
ucontext_t *uc = c->dwarf.as_arg;
|
||||||
unsigned long regs[10];
|
|
||||||
|
|
||||||
/* Copy the register contents to be restored. */
|
if (c->sigcontext_format == ARM_SCF_NONE)
|
||||||
regs[0] = uc->uc_mcontext.arm_r4;
|
{
|
||||||
regs[1] = uc->uc_mcontext.arm_r5;
|
/* Since there are no signals involved here we restore the non scratch
|
||||||
regs[2] = uc->uc_mcontext.arm_r6;
|
registers only. */
|
||||||
regs[3] = uc->uc_mcontext.arm_r7;
|
unsigned long regs[10];
|
||||||
regs[4] = uc->uc_mcontext.arm_r8;
|
regs[0] = uc->uc_mcontext.arm_r4;
|
||||||
regs[5] = uc->uc_mcontext.arm_r9;
|
regs[1] = uc->uc_mcontext.arm_r5;
|
||||||
regs[6] = uc->uc_mcontext.arm_r10;
|
regs[2] = uc->uc_mcontext.arm_r6;
|
||||||
regs[7] = uc->uc_mcontext.arm_fp;
|
regs[3] = uc->uc_mcontext.arm_r7;
|
||||||
regs[8] = uc->uc_mcontext.arm_sp;
|
regs[4] = uc->uc_mcontext.arm_r8;
|
||||||
regs[9] = uc->uc_mcontext.arm_lr;
|
regs[5] = uc->uc_mcontext.arm_r9;
|
||||||
|
regs[6] = uc->uc_mcontext.arm_r10;
|
||||||
|
regs[7] = uc->uc_mcontext.arm_fp;
|
||||||
|
regs[8] = uc->uc_mcontext.arm_sp;
|
||||||
|
regs[9] = uc->uc_mcontext.arm_lr;
|
||||||
|
|
||||||
/* Restore the registers. */
|
asm __volatile__ (
|
||||||
asm __volatile__ (
|
"ldmia %0, {r4-r12, lr}\n"
|
||||||
"ldmia %0, {r4-r12, lr}\n"
|
"mov sp, r12\n"
|
||||||
"mov sp, r12\n"
|
"bx lr\n"
|
||||||
"bx lr\n"
|
: : "r" (regs) :
|
||||||
: : "r" (regs) :
|
);
|
||||||
);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* In case a signal frame is involved, we're using its trampoline which
|
||||||
|
calls sigreturn. */
|
||||||
|
struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr;
|
||||||
|
sc->arm_r0 = uc->uc_mcontext.arm_r0;
|
||||||
|
sc->arm_r1 = uc->uc_mcontext.arm_r1;
|
||||||
|
sc->arm_r2 = uc->uc_mcontext.arm_r2;
|
||||||
|
sc->arm_r3 = uc->uc_mcontext.arm_r3;
|
||||||
|
sc->arm_r4 = uc->uc_mcontext.arm_r4;
|
||||||
|
sc->arm_r5 = uc->uc_mcontext.arm_r5;
|
||||||
|
sc->arm_r6 = uc->uc_mcontext.arm_r6;
|
||||||
|
sc->arm_r7 = uc->uc_mcontext.arm_r7;
|
||||||
|
sc->arm_r8 = uc->uc_mcontext.arm_r8;
|
||||||
|
sc->arm_r9 = uc->uc_mcontext.arm_r9;
|
||||||
|
sc->arm_r10 = uc->uc_mcontext.arm_r10;
|
||||||
|
sc->arm_fp = uc->uc_mcontext.arm_fp;
|
||||||
|
sc->arm_ip = uc->uc_mcontext.arm_ip;
|
||||||
|
sc->arm_sp = uc->uc_mcontext.arm_sp;
|
||||||
|
sc->arm_lr = uc->uc_mcontext.arm_lr;
|
||||||
|
sc->arm_pc = uc->uc_mcontext.arm_pc;
|
||||||
|
/* clear the ITSTATE bits. */
|
||||||
|
sc->arm_cpsr &= 0xf9ff03ffUL;
|
||||||
|
|
||||||
|
/* Set the SP and the PC in order to continue execution at the modified
|
||||||
|
trampoline which restores the signal mask and the registers. */
|
||||||
|
asm __volatile__ (
|
||||||
|
"mov sp, %0\n"
|
||||||
|
"bx %1\n"
|
||||||
|
: : "r" (c->sigcontext_sp), "r" (c->sigcontext_pc) :
|
||||||
|
);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
printf ("%s: implement me\n", __FUNCTION__);
|
printf ("%s: implement me\n", __FUNCTION__);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -27,6 +27,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
#include "offsets.h"
|
#include "offsets.h"
|
||||||
#include "ex_tables.h"
|
#include "ex_tables.h"
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
#define arm_exidx_step UNW_OBJ(arm_exidx_step)
|
#define arm_exidx_step UNW_OBJ(arm_exidx_step)
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
|
@ -70,6 +72,95 @@ arm_exidx_step (struct cursor *c)
|
||||||
return (c->dwarf.ip == 0) ? 0 : 1;
|
return (c->dwarf.ip == 0) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PROTECTED int
|
||||||
|
unw_handle_signal_frame (unw_cursor_t *cursor)
|
||||||
|
{
|
||||||
|
struct cursor *c = (struct cursor *) cursor;
|
||||||
|
int ret;
|
||||||
|
unw_word_t sc_addr, sp, sp_addr = c->dwarf.cfa;
|
||||||
|
struct dwarf_loc sp_loc = DWARF_LOC (sp_addr, 0);
|
||||||
|
|
||||||
|
if ((ret = dwarf_get (&c->dwarf, sp_loc, &sp)) < 0)
|
||||||
|
return -UNW_EUNSPEC;
|
||||||
|
|
||||||
|
/* Obtain signal frame type (non-RT or RT). */
|
||||||
|
ret = unw_is_signal_frame (cursor);
|
||||||
|
|
||||||
|
/* Save the SP and PC to be able to return execution at this point
|
||||||
|
later in time (unw_resume). */
|
||||||
|
c->sigcontext_sp = c->dwarf.cfa;
|
||||||
|
c->sigcontext_pc = c->dwarf.ip;
|
||||||
|
|
||||||
|
/* Since kernel version 2.6.18 the non-RT signal frame starts with a
|
||||||
|
ucontext while the RT signal frame starts with a siginfo, followed
|
||||||
|
by a sigframe whose first element is an ucontext.
|
||||||
|
Prior 2.6.18 the non-RT signal frame starts with a sigcontext while
|
||||||
|
the RT signal frame starts with two pointers followed by a siginfo
|
||||||
|
and an ucontext. The first pointer points to the start of the siginfo
|
||||||
|
structure and the second one to the ucontext structure. */
|
||||||
|
|
||||||
|
if (ret == 1)
|
||||||
|
{
|
||||||
|
/* Handle non-RT signal frames. Check if the first word on the stack
|
||||||
|
is the magic number. */
|
||||||
|
if (sp == 0x5ac3c35a)
|
||||||
|
{
|
||||||
|
c->sigcontext_format = ARM_SCF_LINUX_SIGFRAME;
|
||||||
|
sc_addr = sp_addr + LINUX_UC_MCONTEXT_OFF;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c->sigcontext_format = ARM_SCF_LINUX_OLD_SIGFRAME;
|
||||||
|
sc_addr = sp_addr;
|
||||||
|
}
|
||||||
|
c->sigcontext_addr = sp_addr;
|
||||||
|
}
|
||||||
|
else if (ret == 2)
|
||||||
|
{
|
||||||
|
/* Handle RT signal frames. Check if the first word on the stack is a
|
||||||
|
pointer to the siginfo structure. */
|
||||||
|
if (sp == sp_addr + 8)
|
||||||
|
{
|
||||||
|
c->sigcontext_format = ARM_SCF_LINUX_OLD_RT_SIGFRAME;
|
||||||
|
c->sigcontext_addr = sp_addr + 8 + sizeof (siginfo_t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c->sigcontext_format = ARM_SCF_LINUX_RT_SIGFRAME;
|
||||||
|
c->sigcontext_addr = sp_addr + sizeof (siginfo_t);
|
||||||
|
}
|
||||||
|
sc_addr = c->sigcontext_addr + LINUX_UC_MCONTEXT_OFF;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return -UNW_EUNSPEC;
|
||||||
|
|
||||||
|
/* Update the dwarf cursor.
|
||||||
|
Set the location of the registers to the corresponding addresses of the
|
||||||
|
uc_mcontext / sigcontext structure contents. */
|
||||||
|
c->dwarf.loc[UNW_ARM_R0] = DWARF_LOC (sc_addr + LINUX_SC_R0_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R1] = DWARF_LOC (sc_addr + LINUX_SC_R1_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R2] = DWARF_LOC (sc_addr + LINUX_SC_R2_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R3] = DWARF_LOC (sc_addr + LINUX_SC_R3_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R4] = DWARF_LOC (sc_addr + LINUX_SC_R4_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R5] = DWARF_LOC (sc_addr + LINUX_SC_R5_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R6] = DWARF_LOC (sc_addr + LINUX_SC_R6_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R7] = DWARF_LOC (sc_addr + LINUX_SC_R7_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R8] = DWARF_LOC (sc_addr + LINUX_SC_R8_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R9] = DWARF_LOC (sc_addr + LINUX_SC_R9_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R10] = DWARF_LOC (sc_addr + LINUX_SC_R10_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R11] = DWARF_LOC (sc_addr + LINUX_SC_FP_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R12] = DWARF_LOC (sc_addr + LINUX_SC_IP_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R13] = DWARF_LOC (sc_addr + LINUX_SC_SP_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R14] = DWARF_LOC (sc_addr + LINUX_SC_LR_OFF, 0);
|
||||||
|
c->dwarf.loc[UNW_ARM_R15] = DWARF_LOC (sc_addr + LINUX_SC_PC_OFF, 0);
|
||||||
|
|
||||||
|
/* Set SP/CFA and PC/IP. */
|
||||||
|
dwarf_get (&c->dwarf, c->dwarf.loc[UNW_ARM_R13], &c->dwarf.cfa);
|
||||||
|
dwarf_get (&c->dwarf, c->dwarf.loc[UNW_ARM_R15], &c->dwarf.ip);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
PROTECTED int
|
PROTECTED int
|
||||||
unw_step (unw_cursor_t *cursor)
|
unw_step (unw_cursor_t *cursor)
|
||||||
{
|
{
|
||||||
|
@ -78,6 +169,10 @@ unw_step (unw_cursor_t *cursor)
|
||||||
|
|
||||||
Debug (1, "(cursor=%p)\n", c);
|
Debug (1, "(cursor=%p)\n", c);
|
||||||
|
|
||||||
|
/* Check if this is a signal frame. */
|
||||||
|
if (unw_is_signal_frame (cursor))
|
||||||
|
return unw_handle_signal_frame (cursor);
|
||||||
|
|
||||||
/* First, try DWARF-based unwinding. */
|
/* First, try DWARF-based unwinding. */
|
||||||
if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
|
if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF))
|
||||||
{
|
{
|
||||||
|
|
|
@ -58,6 +58,11 @@ common_init (struct cursor *c, unsigned use_prev_instr)
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
c->sigcontext_format = ARM_SCF_NONE;
|
||||||
|
c->sigcontext_addr = 0;
|
||||||
|
c->sigcontext_sp = 0;
|
||||||
|
c->sigcontext_pc = 0;
|
||||||
|
|
||||||
/* FIXME: Initialisation for other registers. */
|
/* FIXME: Initialisation for other registers. */
|
||||||
|
|
||||||
c->dwarf.args_size = 0;
|
c->dwarf.args_size = 0;
|
||||||
|
|
Loading…
Add table
Reference in a new issue