From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 30626 invoked by alias); 7 Apr 2008 02:36:57 -0000 Received: (qmail 30612 invoked by uid 22791); 7 Apr 2008 02:36:54 -0000 X-Spam-Check-By: sourceware.org Received: from mail.codesourcery.com (HELO mail.codesourcery.com) (65.74.133.4) by sourceware.org (qpsmtpd/0.31) with ESMTP; Mon, 07 Apr 2008 02:36:30 +0000 Received: (qmail 26259 invoked from network); 7 Apr 2008 02:36:27 -0000 Received: from unknown (HELO orlando.local) (pedro@127.0.0.2) by mail.codesourcery.com with ESMTPA; 7 Apr 2008 02:36:27 -0000 From: Pedro Alves Subject: 5/5 - handle glibc pointer mangling jmp_bufs (x86/x86_64) User-Agent: KMail/1.9.6 (enterprise 0.20070907.709405) MIME-Version: 1.0 To: gdb-patches@sourceware.org Date: Mon, 07 Apr 2008 03:21:00 -0000 Content-Type: Multipart/Mixed; boundary="Boundary-00=_riY+HScbTrIeScq" Message-Id: <200804070336.27192.pedro@codesourcery.com> X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2008-04/txt/msg00129.txt.bz2 --Boundary-00=_riY+HScbTrIeScq Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Content-Disposition: inline Content-length: 484 I came up with this patch so I could test the longjmp patches on x86_64-unknown-linux-gnu and x86-pc-linux-gnu. It was inspired on a 2006 patch by Jan Kratochvil. I'm not proposing this to go in, as it will brake glibc's where the pointer mangling is not implemented or is implemented differently. Maybe we could get around this 99% of the times by switching the unmangling algorithm based on glibc's version, although I don't know how to get at glibc's version. -- Pedro Alves --Boundary-00=_riY+HScbTrIeScq Content-Type: text/x-diff; charset="utf-8"; name="longjmp_linux_x86_amd64_pointer_demangle.diff" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="longjmp_linux_x86_amd64_pointer_demangle.diff" Content-length: 13466 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-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 | 43 +++++++++++++++++++++++++++++++++++++++++++ gdb/i386-tdep.c | 2 +- gdb/i386-tdep.h | 2 ++ gdb/target.c | 1 + gdb/target.h | 7 +++++++ 11 files changed, 165 insertions(+), 3 deletions(-) Index: src/gdb/i386-tdep.c =================================================================== --- src.orig/gdb/i386-tdep.c 2008-04-07 01:31:55.000000000 +0100 +++ src/gdb/i386-tdep.c 2008-04-07 01:32:50.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 01:31:55.000000000 +0100 +++ src/gdb/i386-linux-nat.c 2008-04-07 01:32:50.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 01:31:55.000000000 +0100 +++ src/gdb/i386-linux-tdep.c 2008-04-07 01:32:50.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) { Index: src/gdb/i386-tdep.h =================================================================== --- src.orig/gdb/i386-tdep.h 2008-04-07 01:31:55.000000000 +0100 +++ src/gdb/i386-tdep.h 2008-04-07 01:32:50.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 01:31:55.000000000 +0100 +++ src/gdb/target.h 2008-04-07 01:32:50.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 01:31:55.000000000 +0100 +++ src/gdb/target.c 2008-04-07 01:32:50.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 01:31:55.000000000 +0100 +++ src/gdb/amd64-linux-nat.c 2008-04-07 01:32:50.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 01:32:42.000000000 +0100 +++ src/gdb/amd64-linux-tdep.c 2008-04-07 01:33:11.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 01:31:55.000000000 +0100 +++ src/gdb/Makefile.in 2008-04-07 01:32:50.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 01:31:55.000000000 +0100 +++ src/gdb/amd64-tdep.h 2008-04-07 01:32:50.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 01:31:55.000000000 +0100 +++ src/gdb/amd64-tdep.c 2008-04-07 01:32:50.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]; --Boundary-00=_riY+HScbTrIeScq--