2008-04-07 Pedro Alves * target.h (struct target_ops): Add new to_get_thread_control_block function pointer. (target_get_thread_control_block) (target_get_thread_control_block_p): New. * target.c (update_current_target): Inherit to_get_thread_control_block. * i386-tdep.h (i386_get_longjmp_target): Declare. * i386-tdep.c (i386_get_longjmp_target): Make public. * i386-linux-tdep.c (ror32): New macro. (i386_linux_pointer_demangle): New. (i386_linux_get_longjmp_target): New. (i386_linux_init_abi): Register i386_linux_get_longjmp_target as gdbarch_get_longjmp_target callback. * i386-linux-nat.c (i386_linux_get_thread_control_block): New. (_initialize_i386_linux_nat): Register i386_linux_get_thread_control_block as to_get_thread_control_block target method. * amd64-tdep.h (amd64_get_longjmp_target): Declare. * amd64-tdep.c (amd64_get_longjmp_target): Make public. * amd64-linux-tdep.c: Include "target.h" and "inferior.h". (ror64): New macro. (amd64_linux_pointer_demangle): New. (amd64_linux_get_longjmp_target): New. (amd64_linux_init_abi): Set tdep->jb_pc_offset. Register amd64_linux_get_longjmp_target as gdbarch_get_longjmp_target callback. * amd64-linux-nat.c (amd64_linux_get_thread_control_block): New. (_initialize_amd64_linux_nat): Register amd64_linux_get_thread_control_block as to_get_thread_control_block target method. * Makefile.in (amd64-linux-nat.o): Update. --- gdb/Makefile.in | 3 ++- gdb/amd64-linux-nat.c | 25 +++++++++++++++++++++++++ gdb/amd64-linux-tdep.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ gdb/amd64-tdep.c | 2 +- gdb/amd64-tdep.h | 2 ++ gdb/i386-linux-nat.c | 32 ++++++++++++++++++++++++++++++++ gdb/i386-linux-tdep.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ gdb/i386-tdep.c | 2 +- gdb/i386-tdep.h | 2 ++ gdb/target.c | 1 + gdb/target.h | 7 +++++++ 11 files changed, 166 insertions(+), 3 deletions(-) Index: src/gdb/i386-tdep.c =================================================================== --- src.orig/gdb/i386-tdep.c 2008-04-07 13:50:57.000000000 +0100 +++ src/gdb/i386-tdep.c 2008-04-07 23:13:41.000000000 +0100 @@ -1297,7 +1297,7 @@ i386_unwind_dummy_id (struct gdbarch *gd This address is copied into PC. This routine returns non-zero on success. */ -static int +int i386_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc) { gdb_byte buf[4]; Index: src/gdb/i386-linux-nat.c =================================================================== --- src.orig/gdb/i386-linux-nat.c 2008-04-07 02:41:06.000000000 +0100 +++ src/gdb/i386-linux-nat.c 2008-04-07 13:50:58.000000000 +0100 @@ -713,6 +713,36 @@ ps_get_thread_area (const struct ps_proc *(int *)base = desc[1]; return PS_OK; } + +static CORE_ADDR +i386_linux_get_thread_control_block (ptid_t ptid) +{ + int len = TYPE_LENGTH (builtin_type_void_func_ptr); + struct regcache *regcache = get_thread_regcache (ptid); + void *tcbhead; + ULONGEST gs; + const int reg_thread_area = 3; /* bits to scale down register value. */ + int idx; + ps_err_e ps_err; + struct ps_prochandle prochandle; + + regcache_cooked_read_unsigned (regcache, I386_GS_REGNUM, &gs); + + /* TODO: We should provide a pseudo-register with this info, and get + rid of this target method; or if this interface is the best we + can do, ps_get_thread_area should consume from this target + method. */ + + idx = gs >> reg_thread_area; + prochandle.pid = ptid_get_pid (ptid); + ps_err = ps_get_thread_area (&prochandle, prochandle.pid, idx, &tcbhead); + if (ps_err != PS_OK) + error (_("Error %d while getting the TCB of %s"), + (int) ps_err, target_pid_to_str (ptid)); + + return (CORE_ADDR) tcbhead; +} + /* The instruction for a GNU/Linux system call is: @@ -833,6 +863,8 @@ _initialize_i386_linux_nat (void) t->to_fetch_registers = i386_linux_fetch_inferior_registers; t->to_store_registers = i386_linux_store_inferior_registers; + t->to_get_thread_control_block = i386_linux_get_thread_control_block; + /* Register the target. */ linux_nat_add_target (t); linux_nat_set_new_thread (t, i386_linux_new_thread); Index: src/gdb/i386-linux-tdep.c =================================================================== --- src.orig/gdb/i386-linux-tdep.c 2008-04-07 02:41:05.000000000 +0100 +++ src/gdb/i386-linux-tdep.c 2008-04-07 23:19:23.000000000 +0100 @@ -402,6 +402,49 @@ static int i386_linux_sc_reg_offset[] = 0 * 4 /* %gs */ }; +/* Rotate right X by N bits. */ +#define ror32(x, n) (((x) >> ((int) (n))) | ((x) << (32 - (int) (n)))) + +static CORE_ADDR +i386_linux_pointer_demangle (CORE_ADDR address) +{ + CORE_ADDR tcbhead; + CORE_ADDR pointer_guard; + gdb_byte buf[4]; + + /* The pointer guard is stored in tcbhead, at + offsetof (tcbhead_t, pointer_guard). */ + + if (!target_get_thread_control_block_p ()) + /* Nothing we can do. */ + return address; + + tcbhead = target_get_thread_control_block (inferior_ptid); + + /* Wear sunglasses, please. This is private data. */ + + read_memory (tcbhead + 0x18 /* offsetof (tcbhead_t, pointer_guard) */, + buf, 4); + pointer_guard = extract_unsigned_integer (buf, 4); + + address = ror32 (address, 9); + address ^= pointer_guard; + return address; +} + +/* glibc pointer mangles the PC in jmp_buf. We unmangle it here. */ + +static int +i386_linux_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc) +{ + if (!i386_get_longjmp_target (frame, pc)) + return 0; + + *pc = i386_linux_pointer_demangle (*pc); + + return 1; +} + static void i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { @@ -423,6 +466,7 @@ i386_linux_init_abi (struct gdbarch_info tdep->sizeof_gregset = 17 * 4; tdep->jb_pc_offset = 20; /* From . */ + set_gdbarch_get_longjmp_target (gdbarch, i386_linux_get_longjmp_target); tdep->sigtramp_p = i386_linux_sigtramp_p; tdep->sigcontext_addr = i386_linux_sigcontext_addr; Index: src/gdb/i386-tdep.h =================================================================== --- src.orig/gdb/i386-tdep.h 2008-04-07 02:41:05.000000000 +0100 +++ src/gdb/i386-tdep.h 2008-04-07 23:13:41.000000000 +0100 @@ -200,6 +200,8 @@ extern void i386_elf_init_abi (struct gd /* Initialize a SVR4 architecture variant. */ extern void i386_svr4_init_abi (struct gdbarch_info, struct gdbarch *); + +extern int i386_get_longjmp_target (struct frame_info *, CORE_ADDR *); /* Functions and variables exported from i386bsd-tdep.c. */ Index: src/gdb/target.h =================================================================== --- src.orig/gdb/target.h 2008-04-07 02:41:06.000000000 +0100 +++ src/gdb/target.h 2008-04-07 13:50:58.000000000 +0100 @@ -432,6 +432,8 @@ struct target_ops CORE_ADDR load_module_addr, CORE_ADDR offset); + CORE_ADDR (*to_get_thread_control_block) (ptid_t ptid); + /* Request that OPS transfer up to LEN 8-bit bytes of the target's OBJECT. The OFFSET, for a seekable object, specifies the starting point. The ANNEX can be used to provide additional @@ -1018,6 +1020,11 @@ extern char *normal_pid_to_str (ptid_t p #define target_get_thread_local_address_p() \ (target_get_thread_local_address != NULL) +/* TCB. */ +#define target_get_thread_control_block \ + (current_target.to_get_thread_control_block) +#define target_get_thread_control_block_p() \ + (current_target.to_get_thread_control_block != NULL) /* Hardware watchpoint interfaces. */ Index: src/gdb/target.c =================================================================== --- src.orig/gdb/target.c 2008-04-07 02:41:06.000000000 +0100 +++ src/gdb/target.c 2008-04-07 13:50:58.000000000 +0100 @@ -468,6 +468,7 @@ update_current_target (void) INHERIT (to_find_memory_regions, t); INHERIT (to_make_corefile_notes, t); INHERIT (to_get_thread_local_address, t); + INHERIT (to_get_thread_control_block, t); /* Do not inherit to_read_description. */ INHERIT (to_magic, t); /* Do not inherit to_memory_map. */ Index: src/gdb/amd64-linux-nat.c =================================================================== --- src.orig/gdb/amd64-linux-nat.c 2008-04-07 02:41:05.000000000 +0100 +++ src/gdb/amd64-linux-nat.c 2008-04-07 13:50:58.000000000 +0100 @@ -388,6 +388,29 @@ ps_get_thread_area (const struct ps_proc } return PS_ERR; /* ptrace failed. */ } + +static CORE_ADDR +amd64_linux_get_thread_control_block (ptid_t ptid) +{ + int len = TYPE_LENGTH (builtin_type_void_func_ptr); + struct regcache *regcache = get_thread_regcache (ptid); + void *tcbhead; + ps_err_e ps_err; + struct ps_prochandle prochandle; + int idx = FS; + + /* TODO: We should provide a pseudo-register with this info, and get + rid of this target method; or if this interface is the best we + can do, ps_get_thread_area should consume from this target + method. */ + prochandle.pid = ptid_get_pid (ptid); + ps_err = ps_get_thread_area (&prochandle, prochandle.pid, idx, &tcbhead); + if (ps_err != PS_OK) + error (_("Error %d while getting the TCB of %s"), + (int) ps_err, target_pid_to_str (ptid)); + + return (CORE_ADDR) tcbhead; +} static void (*super_post_startup_inferior) (ptid_t ptid); @@ -431,6 +454,8 @@ _initialize_amd64_linux_nat (void) t->to_fetch_registers = amd64_linux_fetch_inferior_registers; t->to_store_registers = amd64_linux_store_inferior_registers; + t->to_get_thread_control_block = amd64_linux_get_thread_control_block; + /* Register the target. */ linux_nat_add_target (t); linux_nat_set_new_thread (t, amd64_linux_new_thread); Index: src/gdb/amd64-linux-tdep.c =================================================================== --- src.orig/gdb/amd64-linux-tdep.c 2008-04-07 02:41:06.000000000 +0100 +++ src/gdb/amd64-linux-tdep.c 2008-04-07 23:13:41.000000000 +0100 @@ -34,6 +34,9 @@ #include "amd64-tdep.h" #include "solib-svr4.h" +#include "target.h" +#include "inferior.h" + /* Mapping between the general-purpose registers in `struct user' format and GDB's register cache layout. */ @@ -257,6 +260,49 @@ amd64_linux_write_pc (struct regcache *r regcache_cooked_write_unsigned (regcache, AMD64_LINUX_ORIG_RAX_REGNUM, -1); } +/* Rotate right X by N bits. */ +#define ror64(x, n) (((x) >> ((int) (n))) | ((x) << (64 - (int) (n)))) + +static CORE_ADDR +amd64_linux_pointer_demangle (CORE_ADDR address) +{ + CORE_ADDR tcbhead; + CORE_ADDR pointer_guard; + gdb_byte buf[8]; + + /* The pointer guard is stored in tcbhead, at + offsetof (tcbhead_t, pointer_guard). */ + + if (!target_get_thread_control_block_p ()) + /* Nothing we can do. */ + return address; + + tcbhead = target_get_thread_control_block (inferior_ptid); + + /* Wear sunglasses, please. This is private data. */ + + read_memory (tcbhead + 0x30 /* offsetof (tcbhead_t, pointer_guard) */, + buf, 8); + pointer_guard = extract_unsigned_integer (buf, 8); + + address = ror64 (address, 17); + address ^= pointer_guard; + return address; +} + +/* glibc pointer mangles the PC in jmp_buf. We unmangle it here. */ + +static int +amd64_linux_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc) +{ + if (!amd64_get_longjmp_target (frame, pc)) + return 0; + + *pc = amd64_linux_pointer_demangle (*pc); + + return 1; +} + static void amd64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { @@ -273,6 +319,9 @@ amd64_linux_init_abi (struct gdbarch_inf tdep->sc_reg_offset = amd64_linux_sc_reg_offset; tdep->sc_num_regs = ARRAY_SIZE (amd64_linux_sc_reg_offset); + tdep->jb_pc_offset = 7 * 8; + set_gdbarch_get_longjmp_target (gdbarch, amd64_linux_get_longjmp_target); + /* GNU/Linux uses SVR4-style shared libraries. */ set_solib_svr4_fetch_link_map_offsets (gdbarch, svr4_lp64_fetch_link_map_offsets); Index: src/gdb/Makefile.in =================================================================== --- src.orig/gdb/Makefile.in 2008-04-07 13:50:56.000000000 +0100 +++ src/gdb/Makefile.in 2008-04-07 13:50:59.000000000 +0100 @@ -1860,7 +1860,8 @@ amd64fbsd-tdep.o: amd64fbsd-tdep.c $(def amd64-linux-nat.o: amd64-linux-nat.c $(defs_h) $(inferior_h) $(gdbcore_h) \ $(regcache_h) $(linux_nat_h) $(gdb_assert_h) $(gdb_string_h) \ $(gdb_proc_service_h) $(gregset_h) $(amd64_tdep_h) \ - $(i386_linux_tdep_h) $(amd64_nat_h) $(amd64_linux_tdep_h) + $(i386_linux_tdep_h) $(amd64_nat_h) $(amd64_linux_tdep_h) \ + $(target_h) $(inferior_h) amd64-linux-tdep.o: amd64-linux-tdep.c $(defs_h) $(frame_h) $(gdbcore_h) \ $(regcache_h) $(osabi_h) $(symtab_h) $(gdb_string_h) $(amd64_tdep_h) \ $(solib_svr4_h) $(gdbtypes_h) $(reggroups_h) $(amd64_linux_tdep_h) Index: src/gdb/amd64-tdep.h =================================================================== --- src.orig/gdb/amd64-tdep.h 2008-04-07 02:41:05.000000000 +0100 +++ src/gdb/amd64-tdep.h 2008-04-07 23:13:41.000000000 +0100 @@ -83,6 +83,8 @@ extern void amd64_supply_fxsave (struct extern void amd64_collect_fxsave (const struct regcache *regcache, int regnum, void *fxsave); + +extern int amd64_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc); /* Variables exported from amd64nbsd-tdep.c. */ Index: src/gdb/amd64-tdep.c =================================================================== --- src.orig/gdb/amd64-tdep.c 2008-04-07 13:50:57.000000000 +0100 +++ src/gdb/amd64-tdep.c 2008-04-07 23:13:41.000000000 +0100 @@ -1107,7 +1107,7 @@ amd64_regset_from_core_section (struct g address is copied into PC. This routine returns non-zero on success. */ -static int +int amd64_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc) { gdb_byte buf[8];