diff options
author | Jose E. Marchesi <jose.marchesi@oracle.com> | 2013-11-18 04:37:05 -0800 |
---|---|---|
committer | Jose E. Marchesi <jose.marchesi@oracle.com> | 2013-11-18 04:37:05 -0800 |
commit | d0b5971ae7b8709c28a7a1638413426d8749d976 (patch) | |
tree | ed6837627bce0dc681f157afa2651fe563b02de4 /gdb/sparc64-linux-tdep.c | |
parent | Simplify dwarf2-frame.c:read_addr_from_reg. (diff) | |
download | binutils-gdb-d0b5971ae7b8709c28a7a1638413426d8749d976.tar.gz binutils-gdb-d0b5971ae7b8709c28a7a1638413426d8749d976.tar.bz2 binutils-gdb-d0b5971ae7b8709c28a7a1638413426d8749d976.zip |
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.
Diffstat (limited to 'gdb/sparc64-linux-tdep.c')
-rw-r--r-- | gdb/sparc64-linux-tdep.c | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/gdb/sparc64-linux-tdep.c b/gdb/sparc64-linux-tdep.c index 3f53f6c1acf..f6ddff0f34b 100644 --- a/gdb/sparc64-linux-tdep.c +++ b/gdb/sparc64-linux-tdep.c @@ -233,6 +233,50 @@ sparc64_linux_get_syscall_number (struct gdbarch *gdbarch, } +/* 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 @@ 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'. */ |