From patchwork Fri May 6 08:51:27 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ken Werner X-Patchwork-Id: 1367 Return-Path: Delivered-To: unknown Received: from imap.gmail.com (74.125.159.109) by localhost6.localdomain6 with IMAP4-SSL; 08 Jun 2011 14:51:27 -0000 Delivered-To: patches@linaro.org Received: by 10.224.184.145 with SMTP id ck17cs131259qab; Fri, 6 May 2011 01:51:42 -0700 (PDT) Received: by 10.213.110.18 with SMTP id l18mr713337ebp.57.1304671900169; Fri, 06 May 2011 01:51:40 -0700 (PDT) Received: from mtagate1.uk.ibm.com (mtagate1.uk.ibm.com [194.196.100.161]) by mx.google.com with ESMTPS id m31si8209914wei.170.2011.05.06.01.51.39 (version=TLSv1/SSLv3 cipher=OTHER); Fri, 06 May 2011 01:51:40 -0700 (PDT) Received-SPF: neutral (google.com: 194.196.100.161 is neither permitted nor denied by best guess record for domain of ken.werner@linaro.org) client-ip=194.196.100.161; Authentication-Results: mx.google.com; spf=neutral (google.com: 194.196.100.161 is neither permitted nor denied by best guess record for domain of ken.werner@linaro.org) smtp.mail=ken.werner@linaro.org Received: from d06nrmr1707.portsmouth.uk.ibm.com (d06nrmr1707.portsmouth.uk.ibm.com [9.149.39.225]) by mtagate1.uk.ibm.com (8.13.1/8.13.1) with ESMTP id p468pcU0003677 for ; Fri, 6 May 2011 08:51:38 GMT Received: from d06av04.portsmouth.uk.ibm.com (d06av04.portsmouth.uk.ibm.com [9.149.37.216]) by d06nrmr1707.portsmouth.uk.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p468qp0R1798172 for ; Fri, 6 May 2011 09:52:51 +0100 Received: from d06av04.portsmouth.uk.ibm.com (loopback [127.0.0.1]) by d06av04.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p468pcSs007852 for ; Fri, 6 May 2011 02:51:38 -0600 Received: from localhost.localdomain (dyn-9-152-224-51.boeblingen.de.ibm.com [9.152.224.51]) by d06av04.portsmouth.uk.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p468pb1Z007777; Fri, 6 May 2011 02:51:38 -0600 From: Ken Werner To: libunwind-devel@nongnu.org Subject: [PATCH 2/2] Add support for handling signal frames on ARM Linux. Date: Fri, 6 May 2011 10:51:27 +0200 Message-Id: <1304671887-696-3-git-send-email-ken.werner@linaro.org> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1304671887-696-1-git-send-email-ken.werner@linaro.org> References: <1304671887-696-1-git-send-email-ken.werner@linaro.org> 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 --- include/tdep-arm/libunwind_i.h | 11 +++++ src/arm/Gresume.c | 74 +++++++++++++++++++++++-------- src/arm/Gstep.c | 95 ++++++++++++++++++++++++++++++++++++++++ src/arm/init.h | 5 ++ 4 files changed, 166 insertions(+), 19 deletions(-) diff --git a/include/tdep-arm/libunwind_i.h b/include/tdep-arm/libunwind_i.h index 271e1d3..508f6c8 100644 --- a/include/tdep-arm/libunwind_i.h +++ b/include/tdep-arm/libunwind_i.h @@ -61,7 +61,18 @@ struct unw_addr_space struct cursor { 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_sp; + unw_word_t sigcontext_pc; }; #define DWARF_GET_LOC(l) ((l).val) diff --git a/src/arm/Gresume.c b/src/arm/Gresume.c index 4bb4701..c870f5f 100644 --- a/src/arm/Gresume.c +++ b/src/arm/Gresume.c @@ -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. */ #include "unwind_i.h" +#include "offsets.h" #ifndef UNW_REMOTE_ONLY @@ -33,27 +34,62 @@ arm_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg) #ifdef __linux__ struct cursor *c = (struct cursor *) cursor; ucontext_t *uc = c->dwarf.as_arg; - unsigned long regs[10]; - - /* Copy the register contents to be restored. */ - regs[0] = uc->uc_mcontext.arm_r4; - regs[1] = uc->uc_mcontext.arm_r5; - regs[2] = uc->uc_mcontext.arm_r6; - regs[3] = uc->uc_mcontext.arm_r7; - regs[4] = uc->uc_mcontext.arm_r8; - 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__ ( - "ldmia %0, {r4-r12, lr}\n" - "mov sp, r12\n" - "bx lr\n" - : : "r" (regs) : - ); + + if (c->sigcontext_format == ARM_SCF_NONE) + { + /* Since there are no signals involved here we restore the non scratch + registers only. */ + unsigned long regs[10]; + regs[0] = uc->uc_mcontext.arm_r4; + regs[1] = uc->uc_mcontext.arm_r5; + regs[2] = uc->uc_mcontext.arm_r6; + regs[3] = uc->uc_mcontext.arm_r7; + regs[4] = uc->uc_mcontext.arm_r8; + 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; + + asm __volatile__ ( + "ldmia %0, {r4-r12, lr}\n" + "mov sp, r12\n" + "bx lr\n" + : : "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 printf ("%s: implement me\n", __FUNCTION__); #endif diff --git a/src/arm/Gstep.c b/src/arm/Gstep.c index 9164d78..f5fb441 100644 --- a/src/arm/Gstep.c +++ b/src/arm/Gstep.c @@ -27,6 +27,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "offsets.h" #include "ex_tables.h" +#include + #define arm_exidx_step UNW_OBJ(arm_exidx_step) static inline int @@ -71,6 +73,95 @@ arm_exidx_step (struct cursor *c) } 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 unw_step (unw_cursor_t *cursor) { struct cursor *c = (struct cursor *) cursor; @@ -78,6 +169,10 @@ unw_step (unw_cursor_t *cursor) 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. */ if (UNW_TRY_METHOD(UNW_ARM_METHOD_DWARF)) { diff --git a/src/arm/init.h b/src/arm/init.h index 1f8d7c1..767dbdc 100644 --- a/src/arm/init.h +++ b/src/arm/init.h @@ -58,6 +58,11 @@ common_init (struct cursor *c, unsigned use_prev_instr) if (ret < 0) 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. */ c->dwarf.args_size = 0;