sparc: support single-stepping over longjmp calls.
2013-11-18 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 commit is contained in:
parent
4b4589ada7
commit
d0b5971ae7
|
@ -1,3 +1,12 @@
|
|||
2013-11-18 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.
|
||||
|
||||
2013-11-18 Pedro Alves <palves@redhat.com>
|
||||
|
||||
* dwarf2-frame.c (read_addr_from_reg): Remove stale comment and
|
||||
|
|
|
@ -121,6 +121,37 @@ sparc_is_unimp_insn (CORE_ADDR pc)
|
|||
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)
|
||||
{
|
||||
/* The branch instructions featuring an annul bit can be identified
|
||||
by the following bit patterns:
|
||||
|
||||
OP=0
|
||||
OP2=1: Branch on Integer Condition Codes with Prediction (BPcc).
|
||||
OP2=2: Branch on Integer Condition Codes (Bcc).
|
||||
OP2=5: Branch on FP Condition Codes with Prediction (FBfcc).
|
||||
OP2=6: Branch on FP Condition Codes (FBcc).
|
||||
OP2=3 && Bit28=0:
|
||||
Branch on Integer Register with Prediction (BPr).
|
||||
|
||||
This leaves out ILLTRAP (OP2=0), SETHI/NOP (OP2=4) and the V8
|
||||
coprocessor branch instructions (Op2=7). */
|
||||
|
||||
const unsigned long insn = sparc_fetch_instruction (pc);
|
||||
const unsigned op2 = X_OP2 (insn);
|
||||
|
||||
if ((X_OP (insn) == 0)
|
||||
&& ((op2 == 1) || (op2 == 2) || (op2 == 5) || (op2 == 6)
|
||||
|| ((op2 == 3) && ((insn & 0x10000000) == 0))))
|
||||
return X_A (insn);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* OpenBSD/sparc includes StackGhost, which according to the author's
|
||||
website http://stackghost.cerias.purdue.edu "... transparently and
|
||||
automatically protects applications' stack frames; more
|
||||
|
|
|
@ -220,6 +220,8 @@ extern void sparc32_collect_fpregset (const struct sparc_fpregset *fpregset,
|
|||
const struct regcache *regcache,
|
||||
int regnum, void *fpregs);
|
||||
|
||||
extern int sparc_is_annulled_branch_insn (CORE_ADDR pc);
|
||||
|
||||
/* Functions and variables exported from sparc-sol2-tdep.c. */
|
||||
|
||||
/* Register offsets for Solaris 2. */
|
||||
|
|
|
@ -232,6 +232,50 @@ sparc64_linux_get_syscall_number (struct gdbarch *gdbarch,
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* 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
|
||||
|
@ -272,6 +316,9 @@ sparc64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
|
|||
/* 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'. */
|
||||
|
|
Loading…
Reference in New Issue