This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: [PATCH] [SPARC64] Figure out where a longjmp will land
- From: jose dot marchesi at oracle dot com (Jose E. Marchesi)
- To: Joel Brobecker <brobecker at adacore dot com>
- Cc: Tom Tromey <tromey at redhat dot com>, gdb-patches at sourceware dot org
- Date: Thu, 17 Oct 2013 13:33:43 +0200
- Subject: Re: [PATCH] [SPARC64] Figure out where a longjmp will land
- Authentication-results: sourceware.org; auth=none
- References: <87y563k71b dot fsf at oracle dot com> <87d2nffr53 dot fsf at fleche dot redhat dot com> <87txgrjupm dot fsf at oracle dot com> <20131017055736 dot GC3302 at adacore dot com>
> 2013-10-08 Jose E. Marchesi <jose.marchesi@oracle.com>
>
> * sparc-tdep.c (sparc_is_annulled_branch_insn): New function.
> * sparc-tdep.h: And its prototype.
>
> * sparc64-linux-tdep.c (sparc64_linux_get_longjmp_target): New
> function.
> (sparc64_linux_init_abi): Register the get_longjmp_target hook.
This looks reasonable to me, with a minor correction requested below.
But Sparc patches normally get reviewed by MarkK, so can you give it
another week before checking the final version in? (remember that
when you check something in that is different from the patch already
posted, an updated patch should be sent to the list, for the
record).
Noted, but I don't have write access to the CVS so I can't check
anything in.
I assume that this was validated against our testsuite, and that
no new regressions were detected?
Yes. I always run the testsuite for regressions before submitting any
patch. I tested this on sparc64-unknown-linux-gnu.
> +/* Figure out where a longjmp will land. Get the args out of the
> + output registers. We expect the first arg to be a pointer to the
> + jmp_buf structure from which we extract the address that we will
> + land at. This address is copied into PC. This routine returns
> + non-zero on success. */
> +
> +static int
> +sparc64_linux_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
This function is expected to implement a gdbarch callback, so you do
not need to repeat the spec, which is expected to already be documented
in gdbarch.h. Instead, use:
/* Implement the "get_longjmp_target" gdbarch method. */
Amended patch below.
2013-10-08 Jose E. Marchesi <jose.marchesi@oracle.com>
* sparc-tdep.c (sparc_is_annulled_branch_insn): New function.
* sparc-tdep.h: And its prototype.
* sparc64-linux-tdep.c (sparc64_linux_get_longjmp_target): New
function.
(sparc64_linux_init_abi): Register the get_longjmp_target hook.
Index: sparc-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/sparc-tdep.c,v
retrieving revision 1.233
diff -u -r1.233 sparc-tdep.c
--- sparc-tdep.c 24 Jun 2013 22:18:32 -0000 1.233
+++ sparc-tdep.c 17 Oct 2013 11:27:05 -0000
@@ -121,6 +121,17 @@
return ((insn & 0xc1c00000) == 0);
}
+/* Return non-zero if the instruction corresponding to PC is an
+ "annulled" branch, i.e. the annul bit is set. */
+
+int
+sparc_is_annulled_branch_insn (CORE_ADDR pc)
+{
+ const unsigned long insn = sparc_fetch_instruction (pc);
+
+ return X_A (insn);
+}
+
/* OpenBSD/sparc includes StackGhost, which according to the author's
website http://stackghost.cerias.purdue.edu "... transparently and
automatically protects applications' stack frames; more
Index: sparc-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/sparc-tdep.h,v
retrieving revision 1.33
diff -u -r1.33 sparc-tdep.h
--- sparc-tdep.h 1 Jan 2013 06:32:51 -0000 1.33
+++ sparc-tdep.h 17 Oct 2013 11:27:05 -0000
@@ -202,6 +202,12 @@
extern void sparc_collect_rwindow (const struct regcache *regcache,
CORE_ADDR sp, int regnum);
+
+
+extern int sparc_is_annulled_branch_insn (CORE_ADDR pc);
+
+
+
/* Register offsets for SunOS 4. */
extern const struct sparc_gregset sparc32_sunos4_gregset;
extern const struct sparc_fpregset sparc32_sunos4_fpregset;
Index: sparc64-linux-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/sparc64-linux-tdep.c,v
retrieving revision 1.31
diff -u -r1.31 sparc64-linux-tdep.c
--- sparc64-linux-tdep.c 1 Jan 2013 06:32:51 -0000 1.31
+++ sparc64-linux-tdep.c 17 Oct 2013 11:27:05 -0000
@@ -233,6 +233,50 @@
}
+/* Implement the "get_longjmp_target" gdbarch method. */
+
+static int
+sparc64_linux_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ CORE_ADDR jb_addr;
+ gdb_byte buf[8];
+
+ jb_addr = get_frame_register_unsigned (frame, SPARC_O0_REGNUM);
+
+ /* setjmp and longjmp in SPARC64 are implemented in glibc using the
+ setcontext and getcontext system calls respectively. These
+ system calls operate on ucontext_t structures, which happen to
+ partially have the same structure than jmp_buf. However the
+ ucontext returned by getcontext, and thus the jmp_buf structure
+ returned by setjmp, contains the context of the trap instruction
+ in the glibc __[sig]setjmp wrapper, not the context of the user
+ code calling setjmp.
+
+ %o7 in the jmp_buf structure is stored at offset 18*8 in the
+ mc_gregs array, which is itself located at offset 32 into
+ jmp_buf. See bits/setjmp.h. This register contains the address
+ of the 'call setjmp' instruction in user code.
+
+ In order to determine the longjmp target address in the
+ initiating frame we need to examine the call instruction itself,
+ in particular whether the annul bit is set. If it is not set
+ then we need to jump over the instruction at the delay slot. */
+
+ if (target_read_memory (jb_addr + 32 + (18 * 8), buf, 8))
+ return 0;
+
+ *pc = extract_unsigned_integer (buf, 8, gdbarch_byte_order (gdbarch));
+
+ if (!sparc_is_annulled_branch_insn (*pc))
+ *pc += 4; /* delay slot insn */
+ *pc += 4; /* call insn */
+
+ return 1;
+}
+
+
static void
sparc64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
@@ -272,6 +316,9 @@
/* Make sure we can single-step over signal return system calls. */
tdep->step_trap = sparc64_linux_step_trap;
+ /* Make sure we can single-step over longjmp calls. */
+ set_gdbarch_get_longjmp_target (gdbarch, sparc64_linux_get_longjmp_target);
+
set_gdbarch_write_pc (gdbarch, sparc64_linux_write_pc);
/* Functions for 'catch syscall'. */