This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: 5/5 - handle glibc pointer mangling jmp_bufs (x86/x86_64)


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];

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]