From patchwork Thu Apr 23 18:59:55 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sandra Loosemore X-Patchwork-Id: 6406 Received: (qmail 31813 invoked by alias); 23 Apr 2015 19:01:00 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 31791 invoked by uid 89); 23 Apr 2015 19:00:58 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.1 required=5.0 tests=AWL, BAYES_00, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.2 X-HELO: relay1.mentorg.com Received: from relay1.mentorg.com (HELO relay1.mentorg.com) (192.94.38.131) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 23 Apr 2015 19:00:57 +0000 Received: from svr-orw-fem-06.mgc.mentorg.com ([147.34.97.120]) by relay1.mentorg.com with esmtp id 1YlMMo-0005km-23 from Sandra_Loosemore@mentor.com ; Thu, 23 Apr 2015 12:00:54 -0700 Received: from [IPv6:::1] (147.34.91.1) by SVR-ORW-FEM-06.mgc.mentorg.com (147.34.97.120) with Microsoft SMTP Server id 14.3.224.2; Thu, 23 Apr 2015 12:00:53 -0700 Message-ID: <5539412B.2020101@codesourcery.com> Date: Thu, 23 Apr 2015 12:59:55 -0600 From: Sandra Loosemore User-Agent: Mozilla/5.0 (X11; Linux i686; rv:17.0) Gecko/20130329 Thunderbird/17.0.5 MIME-Version: 1.0 To: , Yao Qi Subject: [patch 3/3, nios2] fixes for new implementation of signal handler trampolines References: <55393E8C.8090804@codesourcery.com> In-Reply-To: <55393E8C.8090804@codesourcery.com> Earlier versions of the nios2 kernel used to allocate code for signal handler trampolines on the stack, but when the port was accepted upstream it was changed to instead put the trampoline at a fixed address in low memory (0x1044). Moving the code off the stack changed the layout of the stack frame, so the first part of this fix involves updating the offset to the register save area. This is not an exported interface from the kernel; I noticed e.g. the existing aarch64 gdb support includes a huge block of comments explaining the kernel's signal handler stack frame layout but ultimately also relies on using magic numbers to access the register save area. I used a somewhat smaller block of comments for nios2 but I think now it is clear where the magic numbers come from and what kernel code this corresponds to. The second problem is that the trampoline is not writable by user processes so GDB cannot set software breakpoints there. I've tried to deal with that in the single-step hook by having it effectively step over the trampoline by setting the breakpoint on its return address, but for operations like "finish" or "advance" that use the stack unwinder to get the location to set the breakpoint, it seems like there is nothing to do but kfail the tests. OK to commit? -Sandra diff --git a/gdb/nios2-tdep.c b/gdb/nios2-tdep.c index 882c263..0de2f54 100644 --- a/gdb/nios2-tdep.c +++ b/gdb/nios2-tdep.c @@ -1570,7 +1570,7 @@ static const struct frame_unwind nios2_stub_frame_unwind = /* Determine where to set a single step breakpoint while considering branch prediction. */ -static CORE_ADDR +CORE_ADDR nios2_get_next_pc (struct frame_info *frame, CORE_ADDR pc) { struct gdbarch *gdbarch = get_frame_arch (frame); diff --git a/gdb/nios2-tdep.h b/gdb/nios2-tdep.h index af36c41..46eb3e2 100644 --- a/gdb/nios2-tdep.h +++ b/gdb/nios2-tdep.h @@ -77,4 +77,6 @@ struct gdbarch_tdep extern struct target_desc *tdesc_nios2_linux; extern struct target_desc *tdesc_nios2; +extern CORE_ADDR nios2_get_next_pc (struct frame_info *, CORE_ADDR); + #endif /* NIOS2_TDEP_H */ diff --git a/gdb/nios2-linux-tdep.c b/gdb/nios2-linux-tdep.c index dff1603..336faff 100644 --- a/gdb/nios2-linux-tdep.c +++ b/gdb/nios2-linux-tdep.c @@ -29,6 +29,7 @@ #include "linux-tdep.h" #include "glibc-tdep.h" #include "nios2-tdep.h" +#include "breakpoint.h" #include "features/nios2-linux.c" @@ -114,7 +115,24 @@ nios2_iterate_over_regset_sections (struct gdbarch *gdbarch, } /* Initialize a trad-frame cache corresponding to the tramp-frame. - FUNC is the address of the instruction TRAMP[0] in memory. */ + FUNC is the address of the instruction TRAMP[0] in memory. + + This ABI is not documented. It corresponds to rt_setup_ucontext in + the kernel arch/nios2/kernel/signal.c file. + + The key points are: + - The kernel creates a trampoline at the hard-wired address 0x1044. + - The stack pointer points to an object of type struct rt_sigframe. + The definition of this structure is not exported from the kernel. + The register save area is located at offset 152 bytes, and the + registers are saved as r1-r23, ra, fp, gp, ea, sp. + + This interface was implemented with kernel version 3.19 (the first + official mainline kernel). Older unofficial kernel versions used + incompatible conventions; we do not support those here. */ + +#define NIOS2_SIGRETURN_TRAMP_ADDR 0x1044 +#define NIOS2_SIGRETURN_REGSAVE_OFFSET 152 static void nios2_linux_rt_sigreturn_init (const struct tramp_frame *self, @@ -122,7 +140,8 @@ nios2_linux_rt_sigreturn_init (const struct tramp_frame *self, struct trad_frame_cache *this_cache, CORE_ADDR func) { - CORE_ADDR base = func + 41 * 4; + CORE_ADDR sp = get_frame_register_unsigned (next_frame, NIOS2_SP_REGNUM); + CORE_ADDR base = sp + NIOS2_SIGRETURN_REGSAVE_OFFSET; int i; for (i = 0; i < 23; i++) @@ -166,6 +185,32 @@ nios2_linux_syscall_next_pc (struct frame_info *frame) return pc + NIOS2_OPCODE_SIZE; } +/* Override the bare-metal software_single_step gdbarch method. + If the PC where we'd ordinarily want to set the breakpoint is + the signal trampoline at 0x1044, the kernel will not let us set a + breakpoint at that location. So, treat it as if it were a function call + and set the breakpoint on its return address instead. */ + +static int +nios2_linux_software_single_step (struct frame_info *frame) +{ + struct gdbarch *gdbarch = get_frame_arch (frame); + struct address_space *aspace; + CORE_ADDR next_pc = nios2_get_next_pc (frame, get_frame_pc (frame)); + + if (next_pc == NIOS2_SIGRETURN_TRAMP_ADDR) + { + frame = get_prev_frame (frame); + next_pc = frame_unwind_caller_pc (frame); + } + + aspace = get_frame_address_space (frame); + insert_single_step_breakpoint (gdbarch, aspace, next_pc); + + return 1; +} + + /* Hook function for gdbarch_register_osabi. */ static void @@ -191,6 +236,8 @@ nios2_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tramp_frame_prepend_unwinder (gdbarch, &nios2_linux_rt_sigreturn_tramp_frame); + /* Single stepping. */ + set_gdbarch_software_single_step (gdbarch, nios2_linux_software_single_step); tdep->syscall_next_pc = nios2_linux_syscall_next_pc; /* Index of target address word in glibc jmp_buf. */ @@ -204,14 +251,8 @@ extern initialize_file_ftype _initialize_nios2_linux_tdep; void _initialize_nios2_linux_tdep (void) { - - const struct bfd_arch_info *arch_info; - - for (arch_info = bfd_lookup_arch (bfd_arch_nios2, 0); - arch_info != NULL; - arch_info = arch_info->next) - gdbarch_register_osabi (bfd_arch_nios2, arch_info->mach, - GDB_OSABI_LINUX, nios2_linux_init_abi); + gdbarch_register_osabi (bfd_arch_nios2, 0, GDB_OSABI_LINUX, + nios2_linux_init_abi); initialize_tdesc_nios2_linux (); } diff --git a/gdb/testsuite/gdb.base/sigaltstack.exp b/gdb/testsuite/gdb.base/sigaltstack.exp index b65ea48..59a6c17 100644 --- a/gdb/testsuite/gdb.base/sigaltstack.exp +++ b/gdb/testsuite/gdb.base/sigaltstack.exp @@ -76,6 +76,7 @@ proc finish_test { pattern msg } { # don't gracefully fall back to single-stepping. setup_kfail gdb/8841 "i?86-*-linux*" setup_kfail gdb/8841 "*-*-openbsd*" + setup_kfail gdb/8841 "nios2-*-linux*" fail "$msg (could not set breakpoint)" } -re "$pattern.*${gdb_prompt} $" { diff --git a/gdb/testsuite/gdb.base/sigstep.exp b/gdb/testsuite/gdb.base/sigstep.exp index 3c9454c..800fa08 100644 --- a/gdb/testsuite/gdb.base/sigstep.exp +++ b/gdb/testsuite/gdb.base/sigstep.exp @@ -161,6 +161,14 @@ set in_handler_map { fail "$test (spurious SIGTRAP)" return } + -re "Cannot insert breakpoint 0.*${gdb_prompt} $" { + # Some platforms use a special read-only page for signal + # trampolines. We can't set a breakpoint there, and we + # don't gracefully fall back to single-stepping. + setup_kfail gdb/8841 "nios2-*-linux*" + fail "$test (could not set breakpoint)" + return + } -re "other handler location.*$gdb_prompt $" { pass $test } @@ -203,6 +211,7 @@ proc advancei { cmd } { # don't gracefully fall back to single-stepping. setup_kfail gdb/8841 "i?86-*-linux*" setup_kfail gdb/8841 "*-*-openbsd*" + setup_kfail gdb/8841 "nios2-*-linux*" fail "$test (could not set breakpoint)" return } @@ -225,6 +234,10 @@ proc advancei { cmd } { pass "$test" } -re "main .*${gdb_prompt} $" { + # Some targets cannot set single-step breakpoints on a + # signal trampoline and step over the trampoline + # instead of through it. + setup_kfail gdb/8841 "nios2-*-linux*" fail "$test (in main)" } -re "$inferior_exited_re normally.*${gdb_prompt} $" {