[RFC-v2] Add windows Thread Information Block

Pierre Muller
2009-07-01T14:41:53Z

I tried to look at the siginfo stuff as
Daniel suggested, but found out finally that
there is already a TARGET_OBJECT_OSDATA type
that can be used for this case.

Here is a new version of the patch that
works for me both native and remote.
For the remote it should also work if
the host OS has a different endianess and different sizes
for standard types as I use explicit types and
extract_unsigned_integer functions.
For now I left it as a "info w32" sub command.

info w32 thread-information-block
with
info w32 tib
as an alias.

But the code itself tries to also implement w64
version of the thread information block.
Thus, maybe it is better not to change it "info tib"?

Once this is accepted, I would like to
move "info w32 selector" to windows-tdep.c and
implement this in the server.
Using the first field of the thread information block,
I also would like to add a "info w32 structured-exception-handling"
command that would display the chain of thread-specific pushed exception
handlers.
Like in
http://sourceware.org/ml/gdb-patches/2009-01/msg00307.html
but using the thread_local_base field.

Concerning the suggestion to make a convenience variable,
I am a bit unsure:
if I define a new convenience variable named
$_tib
that is a pointer to the current thread information block,
the problem is that this would mean
the doing something like

(gdb) set $_tib.current_seh = my_val
would change the value of the first field,
which seems nice, but is also dangerous as
this $_tib is thread specific.
Using such a convenience variable would then require that
at each thread switch we reload the variable,
or at least that we tag it as 'invalid' (or is 'lazy' OK or this?)
I have no idea if/how this is possible?

Concerning the documentation, I would also like that we
first decide if we want it to become
"info thread-information-block" or stay
"info w32 thread-information-block".

Comments welcome,

Pierre Muller
Pascal language support maintainer for GDB

2009-07-01 Pierre Muller <muller@ics.u-strasbg.fr>

* windows-nat.c (thread_info): Add THREAD_LOCAL_BASE field.
(windows_add_thread): Add TLB argument of type 'void *'.
(fake_create_process): Adapt windows_add_thread call.
(get_windows_debug_event): Idem.
(windows_xfer_partial): Handle TARGET_OBJECT_OSDATA type.
(_initialize_windows_nat): Replace info_w32_cmdlist
initialization by a call to init_w32_command_list.
* windows-nat.c (info_w32_command): Moved from here...
* windows-tdep.c (info_w32_command): to here.
* windows-nat.c (info_w32_cmdlist): Deleted, move to...
* windows-tdep.c (info_w32_cmdlist): to here, made global.
* windows-tdep.h (info_w32_cmdlist): Declare.
* windows-tdep.c (thread_information_32): New struct.
(thread_information_64): New struct.
(TIB_NAME): New char array.
(MAX_TIB32, MAX_TIB64): New constants.
(display_one_tib): New function.
(display_tib): New function.
(init_w32_command_list): New function.
(_initialize_windows_tdep): New function.
* windows-tdep.h (init_w32_command_list): New external function
declaration.

gdbserver/ChangeLog entry:

2009-07-01 Pierre Muller <muller@ics.u-strasbg.fr>

* gdbserver/win32-low.h (win32_thread_info): Add THREAD_LOCAL_BASE
field.
* gdbserver/win32-low.c (child_add_thread): Add TLB argument.
(get_child_debug_event): Adapt to child_add_thread change.
(win32_qxfer_osdata): New function.
(win32_target_op): Set qxfer_osdata field to win32_qxfer_osdata.

Index: windows-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/windows-nat.c,v
retrieving revision 1.195
diff -u -p -r1.195 windows-nat.c
--- windows-nat.c 17 Jun 2009 18:44:23 -0000 1.195
+++ windows-nat.c 1 Jul 2009 14:10:39 -0000
@@ -57,6 +57,7 @@
#include "solist.h"
#include "solib.h"
#include "xml-support.h"
+#include "target.h"

#include "i386-tdep.h"
#include "i387-tdep.h"
@@ -155,6 +156,7 @@ typedef struct thread_info_struct
struct thread_info_struct *next;
DWORD id;
HANDLE h;
+ CORE_ADDR thread_local_base;
char *name;
int suspended;
int reload_context;
@@ -284,7 +286,7 @@ thread_rec (DWORD id, int get_context)

/* Add a thread to the thread list. */
static thread_info *
-windows_add_thread (ptid_t ptid, HANDLE h)
+windows_add_thread (ptid_t ptid, HANDLE h, void *tlb)
{
thread_info *th;
DWORD id;
@@ -299,6 +301,7 @@ windows_add_thread (ptid_t ptid, HANDLE
th = XZALLOC (thread_info);
th->id = id;
th->h = h;
+ th->thread_local_base = (CORE_ADDR) (uintptr_t) tlb;
th->next = thread_head.next;
thread_head.next = th;
add_thread (ptid);
@@ -984,15 +987,6 @@ display_selectors (char * args, int from
}
}

-static struct cmd_list_element *info_w32_cmdlist = NULL;
-
-static void
-info_w32_command (char *args, int from_tty)
-{
- help_list (info_w32_cmdlist, "info w32 ", class_info, gdb_stdout);
-}
-
-
#define DEBUG_EXCEPTION_SIMPLE(x) if (debug_exceptions) \
printf_unfiltered ("gdb: Target exception %s at %s\n", x, \
host_address_to_string (\
@@ -1181,9 +1175,11 @@ fake_create_process (void)
/* We can not debug anything in that case. */
}
main_thread_id = current_event.dwThreadId;
- current_thread = windows_add_thread (ptid_build
(current_event.dwProcessId, 0,
-
current_event.dwThreadId),
-
current_event.u.CreateThread.hThread);
+ current_thread = windows_add_thread (
+ ptid_build (current_event.dwProcessId, 0,
+ current_event.dwThreadId),
+ current_event.u.CreateThread.hThread,
+ current_event.u.CreateThread.lpThreadLocalBase);
return main_thread_id;
}

@@ -1357,7 +1353,8 @@ get_windows_debug_event (struct target_o
retval = current_event.dwThreadId;
th = windows_add_thread (ptid_build (current_event.dwProcessId, 0,
current_event.dwThreadId),
- current_event.u.CreateThread.hThread);
+ current_event.u.CreateThread.hThread,
+
current_event.u.CreateThread.lpThreadLocalBase);
break;

case EXIT_THREAD_DEBUG_EVENT:
@@ -1390,7 +1387,8 @@ get_windows_debug_event (struct target_o
/* Add the main thread */
th = windows_add_thread (ptid_build (current_event.dwProcessId, 0,
current_event.dwThreadId),
- current_event.u.CreateProcessInfo.hThread);
+ current_event.u.CreateProcessInfo.hThread,
+ current_event.u.CreateProcessInfo.lpThreadLocalBase);
retval = current_event.dwThreadId;
break;

@@ -2103,6 +2101,11 @@ windows_xfer_partial (struct target_ops
const char *annex, gdb_byte *readbuf,
const gdb_byte *writebuf, ULONGEST offset, LONGEST len)
{
+ ULONGEST val;
+ thread_info *th;
+ char info_type[20];
+ char *type_end;
+
switch (object)
{
case TARGET_OBJECT_MEMORY:
@@ -2114,6 +2117,31 @@ windows_xfer_partial (struct target_ops
len, 1/*write*/, NULL, ops);
return -1;

+ case TARGET_OBJECT_OSDATA:
+ memset (info_type, 0, sizeof (info_type));
+ val = 0;
+ sscanf (annex, "%s %llu", info_type, &val);
+ if (strcmp (info_type, "tlb:") == 0 && readbuf)
+ {
+ th = thread_rec (val, 0);
+ if (th == NULL)
+ return -1;
+
+ if (len == 8)
+ {
+ uint64_t tlb = th->thread_local_base;
+ memcpy ((void *)readbuf, (void *) &tlb, len);
+ return len;
+ }
+ else if (len == 4)
+ {
+ uint32_t tlb = th->thread_local_base;
+ memcpy ((void *)readbuf, (void *) &tlb, len);
+ return len;
+ }
+ }
+ return -1;
+
case TARGET_OBJECT_LIBRARIES:
return windows_xfer_shared_libraries (ops, object, annex, readbuf,
writebuf, offset, len);
@@ -2271,9 +2299,7 @@ Show whether to display kernel exception
NULL, /* FIXME: i18n: */
&setlist, &showlist);

- add_prefix_cmd ("w32", class_info, info_w32_command,
- _("Print information specific to Win32 debugging."),
- &info_w32_cmdlist, "info w32 ", 0, &infolist);
+ init_w32_command_list ();

add_cmd ("selector", class_info, display_selectors,
_("Display selectors infos."),
Index: windows-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/windows-tdep.c,v
retrieving revision 1.3
diff -u -p -r1.3 windows-tdep.c
--- windows-tdep.c 13 Jan 2009 04:14:07 -0000 1.3
+++ windows-tdep.c 1 Jul 2009 14:10:39 -0000
@@ -19,6 +19,160 @@
#include "windows-tdep.h"
#include "gdb_obstack.h"
#include "xml-support.h"
+#include "gdbarch.h"
+#include "target.h"
+#include "value.h"
+#include "inferior.h"
+#include "command.h"
+#include "gdbcmd.h"
+
+struct cmd_list_element *info_w32_cmdlist;
+
+typedef struct thread_information_block_32
+ {
+ uint32_t current_seh; /* %fs:0x0000 */
+ uint32_t current_top_of_stack; /* %fs:0x0004 */
+ uint32_t current_bottom_of_stack; /* %fs:0x0008 */
+ uint32_t sub_system_tib; /* %fs:0x000c */
+ uint32_t fiber_data; /* %fs:0x0010 */
+ uint32_t arbitrary_data_slot; /* %fs:0x0014 */
+ uint32_t linear_address_tib; /* %fs:0x0018 */
+ uint32_t environment_pointer; /* %fs:0x001c */
+ uint32_t process_id; /* %fs:0x0020 */
+ uint32_t current_thread_id; /* %fs:0x0024 */
+ uint32_t thread_local_storage; /* %fs:0x0028 */
+ uint32_t active_rpc_handle; /* %fs:0x002c */
+ uint32_t process_environment_block; /* %fs:0x0030 */
+ uint32_t last_error_number; /* %fs:0x0034 */
+ }
+thread_information_32;
+
+typedef struct thread_information_block_64
+ {
+ uint64_t current_seh; /* %gs:0x0000 */
+ uint64_t current_top_of_stack; /* %gs:0x0008 */
+ uint64_t current_bottom_of_stack; /* %gs:0x0010 */
+ uint64_t sub_system_tib; /* %gs:0x0018 */
+ uint64_t fiber_data; /* %gs:0x0020 */
+ uint64_t arbitrary_data_slot; /* %gs:0x0028 */
+ uint64_t linear_address_tib; /* %gs:0x0030 */
+ }
+thread_information_64;
+
+
+static const
+char* TIB_NAME[] =
+ {
+ " current_seh ", /* %fs:0x0000 */
+ " current_top_of_stack ", /* %fs:0x0004 */
+ " current_bottom_of_stack ", /* %fs:0x0008 */
+ " sub_system_tib ", /* %fs:0x000c */
+ " fiber_data ", /* %fs:0x0010 */
+ " arbitrary_data_slot ", /* %fs:0x0014 */
+ " linear_address_tib ", /* %fs:0x0018 */
+ " environment_pointer ", /* %fs:0x001c */
+ " process_id ", /* %fs:0x0020 */
+ " current_thread_id ", /* %fs:0x0024 */
+ " thread_local_storage ", /* %fs:0x0028 */
+ " active_rpc_handle ", /* %fs:0x002c */
+ " process_environment_block ", /* %fs:0x0030 */
+ " last_error_number " /* %fs:0x0034 */
+ };
+
+static const int
+MAX_TIB32 = sizeof (thread_information_32) / sizeof (uint32_t);
+static const int
+MAX_TIB64 = sizeof (thread_information_64) / sizeof (uint64_t);
+
+/* Display thread information block of a given thread. */
+static int
+display_one_tib (ULONGEST ThreadId)
+{
+#define PTID_STRING_SIZE 40
+ char annex[PTID_STRING_SIZE];
+ char *annex_end = annex + PTID_STRING_SIZE;
+ gdb_byte *tib = NULL;
+ CORE_ADDR thread_local_base;
+ ULONGEST size, tib_size;
+ ULONGEST sizeof_ptr = gdbarch_ptr_bit (current_gdbarch);
+
+ if (sizeof_ptr == 32)
+ {
+ size = sizeof (uint32_t);
+ tib_size = sizeof (thread_information_32);
+ }
+ else
+ {
+ size = sizeof (uint64_t);
+ tib_size = sizeof (thread_information_64);
+ }
+ tib = alloca (tib_size);
+
+ /* This needs to be changed if multi-process support is added. */
+ strcpy (annex, "tlb: ");
+ strcat (annex, pulongest (ThreadId));
+
+ if (target_read (&current_target, TARGET_OBJECT_OSDATA,
+ annex, tib, 0, size) != size)
+ {
+ printf_filtered ("Unable to get thread local base for ThreadId %s\n",
+ pulongest (ThreadId));
+ return -1;
+ }
+ thread_local_base = extract_unsigned_integer (tib, size);
+ if (target_read (&current_target, TARGET_OBJECT_MEMORY,
+ annex, tib, thread_local_base, tib_size) != tib_size)
+ {
+ printf_filtered ("Unable to read thread information block for
ThreadId %s at address %s\n",
+ pulongest (ThreadId), paddr (thread_local_base));
+ return -1;
+ }
+
+ if (sizeof_ptr == 32)
+ {
+ uint32_t *index = (uint32_t *) tib;
+ int i;
+ uint32_t val;
+
+ printf_filtered ("Thread Information Block %s at %s\n",
+ pulongest (ThreadId), paddr (thread_local_base));
+ for (i = 0; i < MAX_TIB32; i++)
+ {
+ val = extract_unsigned_integer ((gdb_byte *) &(index [i]), 4);
+ printf_filtered ("%s is 0x%s\n", TIB_NAME [i], paddr (val));
+ }
+ }
+ else
+ {
+ uint64_t *index = (uint64_t *) tib;
+ int i;
+ uint64_t val;
+ printf_filtered ("Thread Information Block %s\n", pulongest
(ThreadId));
+ for (i = 0; i < MAX_TIB64; i++)
+ {
+ val = extract_unsigned_integer ((gdb_byte *) &(index [i]), 8);
+ printf_filtered ("%s is 0x%s\n", TIB_NAME [i], paddr (val));
+ }
+ }
+ return 1;
+}
+
+/* Display thread information block of a thread specified by ARGS.
+ If ARGS is empty, display thread information block of current_thread
+ if current_thread is non NULL.
+ Otherwise ARGS is parsed and converted to a integer that should
+ be the windows ThreadID (not the internal GDB thread ID). */
+static void
+display_tib (char * args, int from_tty)
+{
+ if (args)
+ {
+ ULONGEST id = (ULONGEST) parse_and_eval_long (args);
+ display_one_tib (id);
+ }
+ else if (!ptid_equal (inferior_ptid, null_ptid))
+ display_one_tib (ptid_get_tid (inferior_ptid));
+}

void
windows_xfer_shared_library (const char* so_name, CORE_ADDR load_addr,
@@ -37,3 +191,29 @@ windows_xfer_shared_library (const char*
obstack_grow_str (obstack, p);
obstack_grow_str (obstack, "\"/></library>");
}
+
+static void
+info_w32_command (char *args, int from_tty)
+{
+ help_list (info_w32_cmdlist, "info w32 ", class_info, gdb_stdout);
+}
+
+void
+init_w32_command_list (void)
+{
+ if (!info_w32_cmdlist)
+ add_prefix_cmd ("w32", class_info, info_w32_command,
+ _("Print information specific to Win32 debugging."),
+ &info_w32_cmdlist, "info w32 ", 0, &infolist);
+}
+
+void
+_initialize_windows_tdep (void)
+{
+ init_w32_command_list ();
+ add_cmd ("thread-information-block", class_info, display_tib,
+ _("Display thread information block."),
+ &info_w32_cmdlist);
+ add_alias_cmd ("thread_information-block", "tib", class_info, 1,
+ &info_w32_cmdlist);
+}
Index: windows-tdep.h
===================================================================
RCS file: /cvs/src/src/gdb/windows-tdep.h,v
retrieving revision 1.2
diff -u -p -r1.2 windows-tdep.h
--- windows-tdep.h 13 Jan 2009 04:14:07 -0000 1.2
+++ windows-tdep.h 1 Jul 2009 13:46:35 -0000
@@ -20,6 +20,10 @@

struct obstack;

+extern struct cmd_list_element *info_w32_cmdlist;
+
+extern void init_w32_command_list (void);
+
extern void windows_xfer_shared_library (const char* so_name,
CORE_ADDR load_addr,
struct obstack *obstack);
Index: gdbserver/win32-low.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/win32-low.c,v
retrieving revision 1.37
diff -u -p -r1.37 win32-low.c
--- gdbserver/win32-low.c 30 Jun 2009 16:35:25 -0000 1.37
+++ gdbserver/win32-low.c 1 Jul 2009 13:46:35 -0000
@@ -175,7 +175,7 @@ thread_rec (ptid_t ptid, int get_context

/* Add a thread to the thread list. */
static win32_thread_info *
-child_add_thread (DWORD pid, DWORD tid, HANDLE h)
+child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb)
{
win32_thread_info *th;
ptid_t ptid = ptid_build (pid, tid, 0);
@@ -186,6 +186,7 @@ child_add_thread (DWORD pid, DWORD tid,
th = xcalloc (1, sizeof (*th));
th->tid = tid;
th->h = h;
+ th->thread_local_base = (CORE_ADDR) (uintptr_t) tlb;

add_thread (ptid, th);
set_inferior_regcache_data ((struct thread_info *)
@@ -1427,7 +1428,8 @@ get_child_debug_event (struct target_wai
/* Record the existence of this thread. */
child_add_thread (current_event.dwProcessId,
current_event.dwThreadId,
- current_event.u.CreateThread.hThread);
+ current_event.u.CreateThread.hThread,
+ current_event.u.CreateThread.lpThreadLocalBase);
break;

case EXIT_THREAD_DEBUG_EVENT:
@@ -1455,7 +1457,8 @@ get_child_debug_event (struct target_wai
/* Add the main thread. */
child_add_thread (current_event.dwProcessId,
main_thread_id,
- current_event.u.CreateProcessInfo.hThread);
+ current_event.u.CreateProcessInfo.hThread,
+
current_event.u.CreateProcessInfo.lpThreadLocalBase);

ourstatus->value.related_pid = debug_event_ptid (&current_event);
#ifdef _WIN32_WCE
@@ -1723,6 +1726,42 @@ wince_hostio_last_error (char *buf)
}
#endif

+/* Read/Write Windows OS information. */
+int
+win32_qxfer_osdata (const char *info_type,
+ unsigned char *readbuf, unsigned const char *writebuf,
+ CORE_ADDR offset, int len)
+
+{
+ if (strncmp (info_type, "tlb: ", 5) == 0 && readbuf)
+ {
+ unsigned long long tid;
+ char type_name[20];
+ ptid_t ptid;
+ win32_thread_info *th;
+ sscanf (info_type, "%s %llu", type_name, &tid);
+ ptid_build (current_process_id, 0, tid);
+ th = thread_rec (ptid, 0);
+ if (th == NULL)
+ return 0;
+
+ if (len == 8)
+ {
+ uint64_t tlb = th->thread_local_base;
+ memcpy (readbuf, &tlb, len);
+ return len;
+ }
+ else if (len == 4)
+ {
+ uint32_t tlb = th->thread_local_base;
+ memcpy (readbuf, &tlb, len);
+ return len;
+ }
+ }
+ return -1;
+}
+
+
static struct target_ops win32_target_ops = {
win32_create_inferior,
win32_attach,
@@ -1751,6 +1790,7 @@ static struct target_ops win32_target_op
#else
hostio_last_error_from_errno,
#endif
+ win32_qxfer_osdata,
};

/* Initialize the Win32 backend. */
Index: gdbserver/win32-low.h
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/win32-low.h,v
retrieving revision 1.10
diff -u -p -r1.10 win32-low.h
--- gdbserver/win32-low.h 30 Jun 2009 16:35:25 -0000 1.10
+++ gdbserver/win32-low.h 1 Jul 2009 13:46:35 -0000
@@ -28,6 +28,9 @@ typedef struct win32_thread_info
/* The handle to the thread. */
HANDLE h;

+ /* Thread Information Block address. */
+ CORE_ADDR thread_local_base;
+
/* Non zero if SuspendThread was called on this thread. */
int suspended;