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
4 changed files with 89 additions and 0 deletions
|
@ -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>
|
2013-11-18 Pedro Alves <palves@redhat.com>
|
||||||
|
|
||||||
* dwarf2-frame.c (read_addr_from_reg): Remove stale comment and
|
* 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 ((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
|
/* OpenBSD/sparc includes StackGhost, which according to the author's
|
||||||
website http://stackghost.cerias.purdue.edu "... transparently and
|
website http://stackghost.cerias.purdue.edu "... transparently and
|
||||||
automatically protects applications' stack frames; more
|
automatically protects applications' stack frames; more
|
||||||
|
|
|
@ -220,6 +220,8 @@ extern void sparc32_collect_fpregset (const struct sparc_fpregset *fpregset,
|
||||||
const struct regcache *regcache,
|
const struct regcache *regcache,
|
||||||
int regnum, void *fpregs);
|
int regnum, void *fpregs);
|
||||||
|
|
||||||
|
extern int sparc_is_annulled_branch_insn (CORE_ADDR pc);
|
||||||
|
|
||||||
/* Functions and variables exported from sparc-sol2-tdep.c. */
|
/* Functions and variables exported from sparc-sol2-tdep.c. */
|
||||||
|
|
||||||
/* Register offsets for Solaris 2. */
|
/* Register offsets for Solaris 2. */
|
||||||
|
|
|
@ -232,6 +232,50 @@ sparc64_linux_get_syscall_number (struct gdbarch *gdbarch,
|
||||||
return ret;
|
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
|
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. */
|
/* Make sure we can single-step over signal return system calls. */
|
||||||
tdep->step_trap = sparc64_linux_step_trap;
|
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);
|
set_gdbarch_write_pc (gdbarch, sparc64_linux_write_pc);
|
||||||
|
|
||||||
/* Functions for 'catch syscall'. */
|
/* Functions for 'catch syscall'. */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue