This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
Re: 5/5 - handle glibc pointer mangling jmp_bufs (x86/x86_64)
- From: Pedro Alves <pedro at codesourcery dot com>
- To: gdb-patches at sourceware dot org
- Date: Mon, 7 Apr 2008 23:25:50 +0100
- Subject: Re: 5/5 - handle glibc pointer mangling jmp_bufs (x86/x86_64)
- References: <200804070336.27192.pedro@codesourcery.com>
A Monday 07 April 2008 03:36:27, Pedro Alves wrote:
> 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.
Ooops, I only noticed now that due to a last-minute copy-pasting
I ended up posting a patch that missed a call to
set_gdbarch_get_longjmp_target on x86-pc-linux-gnu.
Updated patch attached.
--
Pedro Alves
2008-04-07 Pedro Alves <pedro@codesourcery.com>
* 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 <bits/setjmp.h>. */
+ 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];