Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* 5/5 - handle glibc pointer mangling jmp_bufs (x86/x86_64)
@ 2008-04-07  6:31 Pedro Alves
  0 siblings, 0 replies; 8+ messages in thread
From: Pedro Alves @ 2008-04-07  6:31 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 484 bytes --]

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

[-- Attachment #2: longjmp_linux_x86_amd64_pointer_demangle.diff --]
[-- Type: text/x-diff, Size: 13466 bytes --]

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-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;
+}
+
 \f
 
 /* 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 *);
 \f
 
 /* 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;
+}
 \f
 
 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);
 \f
 
 /* 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];

^ permalink raw reply	[flat|nested] 8+ messages in thread
* 5/5 - handle glibc pointer mangling jmp_bufs (x86/x86_64)
@ 2008-04-07  3:21 Pedro Alves
  2008-04-07 23:02 ` Pedro Alves
  0 siblings, 1 reply; 8+ messages in thread
From: Pedro Alves @ 2008-04-07  3:21 UTC (permalink / raw)
  To: gdb-patches

[-- Attachment #1: Type: text/plain, Size: 484 bytes --]

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

[-- Attachment #2: longjmp_linux_x86_amd64_pointer_demangle.diff --]
[-- Type: text/x-diff, Size: 13466 bytes --]

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-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;
+}
+
 \f
 
 /* 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 *);
 \f
 
 /* 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;
+}
 \f
 
 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);
 \f
 
 /* 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];

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2008-04-15 13:37 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-04-07  6:31 5/5 - handle glibc pointer mangling jmp_bufs (x86/x86_64) Pedro Alves
  -- strict thread matches above, loose matches on Subject: below --
2008-04-07  3:21 Pedro Alves
2008-04-07 23:02 ` Pedro Alves
2008-04-14 19:02   ` Daniel Jacobowitz
2008-04-14 19:24     ` Pedro Alves
2008-04-14 19:56       ` Daniel Jacobowitz
2008-04-15 12:54         ` Daniel Jacobowitz
2008-04-15 14:40           ` Pedro Alves

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox