From 711e434b390a0b953862435eb81f3b89e720b030 Mon Sep 17 00:00:00 2001 From: Pierre Muller Date: Fri, 16 Apr 2010 07:49:37 +0000 Subject: [PATCH] Support for Windows OS Thread Information Block. * NEWS: Document new feature. * remote.c (PACKET_qGetTIBAddr): New enum element. (remote_get_tib_address): New function. (init_remote_ops): Set to_get_tib_address field to remote_get_tib_address. (_initialize_remote): Add add_packet_config_cmd for PACKET_qGetTIBAddr. * target.c (update_current_target): Set default value for new to_get_tib_address field. * target.h (target_ops): New field to_get_tib_address. (target_get_tib_address): New macro. * 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_get_tib_address): New function. (init_windows_ops): Set to_get_tib_address field to remote_get_tib_address. (_initialize_windows_nat): Replace info_w32_cmdlist initialization by a call to init_w32_command_list. (info_w32_command, info_w32_cmdlist): Removed from here... to windows-tdep.c file. * windows-tdep.h (info_w32_cmdlist): Declare. (init_w32_command_list): New external function declaration. * windows-tdep.c: Add several headers. (info_w32_cmdlist): to here, made global. (thread_information_32): New struct. (thread_information_64): New struct. (TIB_NAME): New char array. (MAX_TIB32, MAX_TIB64, FULL_TIB_SIZE): New constants. (maint_display_all_tib): New static variable. (windows_get_tlb_type): New function. (tlb_value_read, tlb_value_write): New functions. (tlb_value_funcs): New static struct. (tlb_make_value): New function. (display_one_tib): New function. (display_tib): New function. (show_maint_show_all_tib):New function. (info_w32_command): Moved from windows-nat.c. (init_w32_command_list): New function. (_initialize_windows_tdep): New function. New "maint set/show show-all-tib" command New "$_tlb" internal variable. gdbserver/ChangeLog entry: * server.c (handle_query): Handle 'qGetTIBAddr' query. * target.h (target_ops): New get_tib_address field. * win32-low.h (win32_thread_info): Add thread_local_base field. * win32-low.c (child_add_thread): Add tlb argument. Set thread_local_base field to TLB. (get_child_debug_event): Adapt to child_add_thread change. (win32_get_tib_address): New function. (win32_target_ops): Set get_tib_address field to win32_get_tib_address. * linux-low.c (linux_target_ops): Set get_tib_address field to NULL. doc/ChangeLog entry: gdb.texinfo ($_tlb): Document new automatic convinience variable. (info w32 thread-information-block): Document new command. (qGetTIBAddress): Document new gdbserver query. (maint set/show show-all-tib): Document new command. --- gdb/ChangeLog | 48 +++++ gdb/NEWS | 15 ++ gdb/doc/ChangeLog | 7 + gdb/doc/gdb.texinfo | 47 +++++ gdb/gdbserver/ChangeLog | 13 ++ gdb/gdbserver/linux-low.c | 3 +- gdb/gdbserver/server.c | 23 +++ gdb/gdbserver/target.h | 3 + gdb/gdbserver/win32-low.c | 48 ++++- gdb/gdbserver/win32-low.h | 3 + gdb/remote.c | 48 +++++ gdb/target.c | 4 + gdb/target.h | 7 + gdb/windows-nat.c | 51 +++-- gdb/windows-tdep.c | 402 ++++++++++++++++++++++++++++++++++++++ gdb/windows-tdep.h | 4 + 16 files changed, 699 insertions(+), 27 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 7c2ec949aea..b424c7def6b 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,51 @@ +2010-04-16 Pierre Muller + + Support for Windows OS Thread Information Block. + * NEWS: Document new feature. + * remote.c (PACKET_qGetTIBAddr): New enum element. + (remote_get_tib_address): New function. + (init_remote_ops): Set to_get_tib_address field + to remote_get_tib_address. + (_initialize_remote): Add add_packet_config_cmd + for PACKET_qGetTIBAddr. + * target.c (update_current_target): Set default value for + new to_get_tib_address field. + * target.h (target_ops): New field to_get_tib_address. + (target_get_tib_address): New macro. + * 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_get_tib_address): New function. + (init_windows_ops): Set to_get_tib_address field + to remote_get_tib_address. + (_initialize_windows_nat): Replace info_w32_cmdlist + initialization by a call to init_w32_command_list. + (info_w32_command, info_w32_cmdlist): Removed from here... + to windows-tdep.c file. + * windows-tdep.h (info_w32_cmdlist): Declare. + (init_w32_command_list): New external function + declaration. + * windows-tdep.c: Add several headers. + (info_w32_cmdlist): to here, made global. + (thread_information_32): New struct. + (thread_information_64): New struct. + (TIB_NAME): New char array. + (MAX_TIB32, MAX_TIB64, FULL_TIB_SIZE): New constants. + (maint_display_all_tib): New static variable. + (windows_get_tlb_type): New function. + (tlb_value_read, tlb_value_write): New functions. + (tlb_value_funcs): New static struct. + (tlb_make_value): New function. + (display_one_tib): New function. + (display_tib): New function. + (show_maint_show_all_tib):New function. + (info_w32_command): Moved from windows-nat.c. + (init_w32_command_list): New function. + (_initialize_windows_tdep): New function. + New "maint set/show show-all-tib" command + New "$_tlb" internal variable. + 2010-04-16 Joel Brobecker * tui/tui-regs.c (tui_display_register): Add comment about diff --git a/gdb/NEWS b/gdb/NEWS index 936987358a0..5c5caf5fadb 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,21 @@ *** Changes since GDB 7.1 +* Windows Thread Information Block access. + + On Windows targets, GDB now supports displaying the Windows Thread + Information Block (TIB) structure. This structure is visible either + by using the new command `info w32 thread-information-block' or, by + dereferencing the new convenience variable named `$_tlb', a + thread-specific pointer to the TIB. This feature is also supported + when remote debugging using GDBserver. + +* New remote packets + +qGetTIBAddr + + Return the address of the Windows Thread Information Block of a given thread. + * The source command now accepts a -s option to force searching for the script in the source search path even if the script name specifies a directory. diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 0260d1e0333..bf650ccfb45 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,10 @@ +2010-04-16 Pierre Muller + + gdb.texinfo ($_tlb): Document new automatic convinience variable. + (info w32 thread-information-block): Document new command. + (qGetTIBAddress): Document new gdbserver query. + (maint set/show show-all-tib): Document new command. + 2010-04-15 Doug Evans * gdb.texinfo (Python API): Add progspaces section. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 8a224e0b30c..01d85938dd3 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -8054,6 +8054,15 @@ The variable @code{$_siginfo} contains extra signal information (@pxref{extra signal information}). Note that @code{$_siginfo} could be empty, if the application has not yet received any signals. For example, it will be empty before you execute the @code{run} command. + +@item $_tlb +@vindex $_tlb@r{, convenience variable} +The variable @code{$_tlb} is automatically set when debugging +applications running on MS-Windows in native mode or connected to +gdbserver that supports the @code{qGetTIBAddr} request. +@xref{General Query Packets}. +This variable contains the address of the thread information block. + @end table On HP-UX systems, if you refer to a function or variable name that @@ -15755,6 +15764,10 @@ are: @tab @code{qGetTLSAddr} @tab Displaying @code{__thread} variables +@item @code{get-thread-information-block-address} +@tab @code{qGetTIBAddr} +@tab Display MS-Windows Thread Information Block. + @item @code{search-memory} @tab @code{qSearch:memory} @tab @code{find} @@ -16534,6 +16547,11 @@ a long value to give the information about this given selector. Without argument, this command displays information about the six segment registers. +@item info w32 thread-information-block +This command displays thread specific information stored in the +Thread Information Block (readable on the X86 CPU family using @code{$fs} +selector for 32-bit programs and @code{$gs} for 64-bit programs). + @kindex info dll @item info dll This is a Cygwin-specific alias of @code{info shared}. @@ -29449,6 +29467,14 @@ enabled, the debug registers values are shown when @value{GDBN} inserts or removes a hardware breakpoint or watchpoint, and when the inferior triggers a hardware-assisted breakpoint or watchpoint. +@kindex maint set show-all-tib +@kindex maint show show-all-tib +@item maint set show-all-tib +@itemx maint show show-all-tib +Control whether to show all non zero areas within a 1k block starting +at thread local base, when using the @samp{info w32 thread-information-block} +command. + @kindex maint space @cindex memory used by commands @item maint space @@ -30662,6 +30688,27 @@ An error occurred. @var{nn} are hex digits. An empty reply indicates that @samp{qGetTLSAddr} is not supported by the stub. @end table +@item qGetTIBAddr:@var{thread-id} +@cindex get thread information block address +@cindex @samp{qGetTIBAddr} packet +Fetch address of the Windows OS specific Thread Information Block. + +@var{thread-id} is the thread ID associated with the thread. + +Reply: +@table @samp +@item @var{XX}@dots{} +Hex encoded (big endian) bytes representing the linear address of the +thread information block. + +@item E @var{nn} +An error occured. This means that either the thread was not found, or the +address could not be retrieved. + +@item +An empty reply indicates that @samp{qGetTIBAddr} is not supported by the stub. +@end table + @item qL @var{startflag} @var{threadcount} @var{nextthread} Obtain thread information from RTOS. Where: @var{startflag} (one hex digit) is one to indicate the first query and zero to indicate a diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 269fb085080..b4d509d2a3d 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,16 @@ +2010-04-16 Pierre Muller + + * server.c (handle_query): Handle 'qGetTIBAddr' query. + * target.h (target_ops): New get_tib_address field. + * win32-low.h (win32_thread_info): Add thread_local_base field. + * win32-low.c (child_add_thread): Add tlb argument. + Set thread_local_base field to TLB. + (get_child_debug_event): Adapt to child_add_thread change. + (win32_get_tib_address): New function. + (win32_target_ops): Set get_tib_address field to + win32_get_tib_address. + * linux-low.c (linux_target_ops): Set get_tib_address field to NULL. + 2010-04-12 Pedro Alves * linux-low.c (linux_mourn): Also remove the process. diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 9692f215713..6b7b40f9cd0 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -4334,7 +4334,8 @@ static struct target_ops linux_target_ops = { linux_read_pc, linux_write_pc, linux_thread_stopped, - linux_pause_all + linux_pause_all, + NULL, /* get_tib_address (Windows OS specific). */ }; static void diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 423a993f2be..9c05c7fcc53 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -1463,6 +1463,29 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) /* Otherwise, pretend we do not understand this packet. */ } + /* Windows OS Thread Information Block address support. */ + if (the_target->get_tib_address != NULL + && strncmp ("qGetTIBAddr:", own_buf, 12) == 0) + { + char *annex; + int n; + CORE_ADDR tlb; + ptid_t ptid = read_ptid (own_buf + 12, &annex); + + n = (*the_target->get_tib_address) (ptid, &tlb); + if (n == 1) + { + sprintf (own_buf, "%llx", tlb); + return; + } + else if (n == 0) + { + write_enn (own_buf); + return; + } + return; + } + /* Handle "monitor" commands. */ if (strncmp ("qRcmd,", own_buf, 6) == 0) { diff --git a/gdb/gdbserver/target.h b/gdb/gdbserver/target.h index 4cccb29394a..fbe191086d6 100644 --- a/gdb/gdbserver/target.h +++ b/gdb/gdbserver/target.h @@ -309,6 +309,9 @@ struct target_ops /* Pause all threads. */ void (*pause_all) (void); + + /* Read Thread Information Block address. */ + int (*get_tib_address) (ptid_t ptid, CORE_ADDR *address); }; extern struct target_ops *the_target; diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c index dfdb88369de..2ade6f76e1f 100644 --- a/gdb/gdbserver/win32-low.c +++ b/gdb/gdbserver/win32-low.c @@ -178,7 +178,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); @@ -189,6 +189,7 @@ child_add_thread (DWORD pid, DWORD tid, HANDLE h) 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 *) @@ -1455,7 +1456,8 @@ get_child_debug_event (struct target_waitstatus *ourstatus) /* 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: @@ -1485,7 +1487,8 @@ get_child_debug_event (struct target_waitstatus *ourstatus) /* 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 (¤t_event); #ifdef _WIN32_WCE @@ -1753,6 +1756,20 @@ wince_hostio_last_error (char *buf) } #endif +/* Write Windows OS Thread Information Block address. */ + +static int +win32_get_tib_address (ptid_t ptid, CORE_ADDR *addr) +{ + win32_thread_info *th; + th = thread_rec (ptid, 0); + if (th == NULL) + return 0; + if (addr != NULL) + *addr = th->thread_local_base; + return 1; +} + static struct target_ops win32_target_ops = { win32_create_inferior, win32_attach, @@ -1767,21 +1784,36 @@ static struct target_ops win32_target_ops = { win32_store_inferior_registers, win32_read_inferior_memory, win32_write_inferior_memory, - NULL, + NULL, /* lookup_symbols */ win32_request_interrupt, - NULL, + NULL, /* read_auxv */ win32_insert_point, win32_remove_point, win32_stopped_by_watchpoint, win32_stopped_data_address, - NULL, - NULL, - NULL, + NULL, /* read_offsets */ + NULL, /* get_tls_address */ + NULL, /* qxfer_spu */ #ifdef _WIN32_WCE wince_hostio_last_error, #else hostio_last_error_from_errno, #endif + NULL, /* qxfer_osdata */ + NULL, /* qxfer_siginfo */ + NULL, /* supports_non_stop */ + NULL, /* async */ + NULL, /* start_non_stop */ + NULL, /* supports_multi_process */ + NULL, /* handle_monitor_command */ + NULL, /* core_of_thread */ + NULL, /* process_qsupported */ + NULL, /* supports_tracepoints */ + NULL, /* read_pc */ + NULL, /* write_pc */ + NULL, /* thread_stopped */ + NULL, /* pause_all */ + win32_get_tib_address, }; /* Initialize the Win32 backend. */ diff --git a/gdb/gdbserver/win32-low.h b/gdb/gdbserver/win32-low.h index d5ddf16b516..6e1b8dc6e06 100644 --- a/gdb/gdbserver/win32-low.h +++ b/gdb/gdbserver/win32-low.h @@ -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; diff --git a/gdb/remote.c b/gdb/remote.c index 591c9f6d4fb..669c13b734a 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -1136,6 +1136,7 @@ enum { PACKET_qXfer_spu_write, PACKET_qXfer_osdata, PACKET_qXfer_threads, + PACKET_qGetTIBAddr, PACKET_qGetTLSAddr, PACKET_qSupported, PACKET_QPassSignals, @@ -8437,6 +8438,48 @@ remote_get_thread_local_address (struct target_ops *ops, return 0; } +/* Provide thread local base, i.e. Thread Information Block address. + Returns 1 if ptid is found and thread_local_base is non zero. */ + +int +remote_get_tib_address (ptid_t ptid, CORE_ADDR *addr) +{ + if (remote_protocol_packets[PACKET_qGetTIBAddr].support != PACKET_DISABLE) + { + struct remote_state *rs = get_remote_state (); + char *p = rs->buf; + char *endp = rs->buf + get_remote_packet_size (); + enum packet_result result; + + strcpy (p, "qGetTIBAddr:"); + p += strlen (p); + p = write_ptid (p, endp, ptid); + *p++ = '\0'; + + putpkt (rs->buf); + getpkt (&rs->buf, &rs->buf_size, 0); + result = packet_ok (rs->buf, + &remote_protocol_packets[PACKET_qGetTIBAddr]); + if (result == PACKET_OK) + { + ULONGEST result; + + unpack_varlen_hex (rs->buf, &result); + if (addr) + *addr = (CORE_ADDR) result; + return 1; + } + else if (result == PACKET_UNKNOWN) + error (_("Remote target doesn't support qGetTIBAddr packet")); + else + error (_("Remote target failed to process qGetTIBAddr request")); + } + else + error (_("qGetTIBAddr not supported or disabled on this target")); + /* Not reached. */ + return 0; +} + /* Support for inferring a target description based on the current architecture and the size of a 'g' packet. While the 'g' packet can have any size (since optional registers can be left off the @@ -9904,6 +9947,7 @@ Specify the serial device it is connected to\n\ remote_ops.to_set_circular_trace_buffer = remote_set_circular_trace_buffer; remote_ops.to_core_of_thread = remote_core_of_thread; remote_ops.to_verify_memory = remote_verify_memory; + remote_ops.to_get_tib_address = remote_get_tib_address; } /* Set up the extended remote vector by making a copy of the standard @@ -10323,6 +10367,10 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL, "qGetTLSAddr", "get-thread-local-storage-address", 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_qGetTIBAddr], + "qGetTIBAddr", "get-thread-information-block-address", + 0); + add_packet_config_cmd (&remote_protocol_packets[PACKET_bc], "bc", "reverse-continue", 0); diff --git a/gdb/target.c b/gdb/target.c index 5657f89b945..08b42a08887 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -660,6 +660,7 @@ update_current_target (void) INHERIT (to_get_raw_trace_data, t); INHERIT (to_set_disconnected_tracing, t); INHERIT (to_set_circular_trace_buffer, t); + INHERIT (to_get_tib_address, t); INHERIT (to_magic, t); /* Do not inherit to_memory_map. */ /* Do not inherit to_flash_erase. */ @@ -853,6 +854,9 @@ update_current_target (void) de_fault (to_set_circular_trace_buffer, (void (*) (int)) target_ignore); + de_fault (to_get_tib_address, + (int (*) (ptid_t, CORE_ADDR *)) + tcomplain); #undef de_fault /* Finally, position the target-stack beneath the squashed diff --git a/gdb/target.h b/gdb/target.h index 0d16139f45c..272571b7502 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -682,6 +682,10 @@ struct target_ops int (*to_verify_memory) (struct target_ops *, const gdb_byte *data, CORE_ADDR memaddr, ULONGEST size); + /* Return the address of the start of the Thread Information Block + a Windows OS specific feature. */ + int (*to_get_tib_address) (ptid_t ptid, CORE_ADDR *addr); + int to_magic; /* Need sub-structure for target machine related rather than comm related? */ @@ -1371,6 +1375,9 @@ extern int target_search_memory (CORE_ADDR start_addr, #define target_set_circular_trace_buffer(val) \ (*current_target.to_set_circular_trace_buffer) (val) +#define target_get_tib_address(ptid, addr) \ + (*current_target.to_get_tib_address) ((ptid), (addr)) + /* Command logging facility. */ #define target_log_command(p) \ diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c index f2f42f79d71..5966b882b63 100644 --- a/gdb/windows-nat.c +++ b/gdb/windows-nat.c @@ -191,6 +191,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; @@ -320,7 +321,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; @@ -335,6 +336,7 @@ windows_add_thread (ptid_t ptid, HANDLE h) 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); @@ -1074,15 +1076,6 @@ display_selectors (char * args, int from_tty) } } -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 (\ @@ -1271,9 +1264,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; } @@ -1447,7 +1442,9 @@ get_windows_debug_event (struct target_ops *ops, 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: @@ -1481,7 +1478,8 @@ get_windows_debug_event (struct target_ops *ops, /* 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; @@ -2266,6 +2264,24 @@ windows_xfer_partial (struct target_ops *ops, enum target_object object, } } +/* Provide thread local base, i.e. Thread Information Block address. + Returns 1 if ptid is found and sets *ADDR to thread_local_base. */ + +static int +windows_get_tib_address (ptid_t ptid, CORE_ADDR *addr) +{ + thread_info *th; + + th = thread_rec (ptid_get_tid (ptid), 0); + if (th == NULL) + return 0; + + if (addr != NULL) + *addr = th->thread_local_base; + + return 1; +} + static ptid_t windows_get_ada_task_ptid (long lwp, long thread) { @@ -2314,6 +2330,7 @@ init_windows_ops (void) windows_ops.to_has_execution = default_child_has_execution; windows_ops.to_pid_to_exec_file = windows_pid_to_exec_file; windows_ops.to_get_ada_task_ptid = windows_get_ada_task_ptid; + windows_ops.to_get_tib_address = windows_get_tib_address; i386_use_watchpoints (&windows_ops); @@ -2415,9 +2432,7 @@ Show whether to display kernel exceptions in child process."), NULL, 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."), diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c index cdf87d1308e..ba0fba9fa42 100644 --- a/gdb/windows-tdep.c +++ b/gdb/windows-tdep.c @@ -19,6 +19,352 @@ #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" +#include "gdbthread.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 active_rpc_handle; /* %fs:0x0028 */ + uint32_t thread_local_storage; /* %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 */ + uint64_t environment_pointer; /* %gs:0x0038 */ + uint64_t process_id; /* %gs:0x0040 */ + uint64_t current_thread_id; /* %gs:0x0048 */ + uint64_t active_rpc_handle; /* %gs:0x0050 */ + uint64_t thread_local_storage; /* %gs:0x0058 */ + uint64_t process_environment_block; /* %gs:0x0060 */ + uint64_t last_error_number; /* %gs:0x0068 */ + } +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 */ + " active_rpc_handle ", /* %fs:0x0028 */ + " thread_local_storage ", /* %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); +static const int FULL_TIB_SIZE = 0x1000; + +static int maint_display_all_tib = 0; + +/* Define Thread Local Base pointer type. */ + +static struct type * +windows_get_tlb_type (struct gdbarch *gdbarch) +{ + struct type *dword_ptr_type, *dword32_type, *void_ptr_type; + struct type *peb_ldr_type, *peb_ldr_ptr_type; + struct type *peb_type, *peb_ptr_type, *list_type, *list_ptr_type; + struct type *module_list_ptr_type; + struct type *tib_type, *seh_type, *tib_ptr_type, *seh_ptr_type; + + dword_ptr_type = arch_integer_type (gdbarch, gdbarch_ptr_bit (gdbarch), + 1, "DWORD_PTR"); + dword32_type = arch_integer_type (gdbarch, 32, + 1, "DWORD32"); + void_ptr_type = lookup_pointer_type (builtin_type (gdbarch)->builtin_void); + + /* list entry */ + + list_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); + TYPE_NAME (list_type) = xstrdup ("list"); + + list_ptr_type = arch_type (gdbarch, TYPE_CODE_PTR, + TYPE_LENGTH (void_ptr_type), NULL); + + module_list_ptr_type = void_ptr_type; + + append_composite_type_field (list_type, "forward_list", module_list_ptr_type); + append_composite_type_field (list_type, "backward_list", + module_list_ptr_type); + + /* Structured Exception Handler */ + + seh_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); + TYPE_NAME (seh_type) = xstrdup ("seh"); + + seh_ptr_type = arch_type (gdbarch, TYPE_CODE_PTR, + TYPE_LENGTH (void_ptr_type), NULL); + TYPE_TARGET_TYPE (seh_ptr_type) = seh_type; + + append_composite_type_field (seh_type, "next_seh", seh_ptr_type); + append_composite_type_field (seh_type, "handler", void_ptr_type); + + /* struct _PEB_LDR_DATA */ + peb_ldr_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); + TYPE_NAME (peb_ldr_type) = xstrdup ("peb_ldr_data"); + + append_composite_type_field (peb_ldr_type, "length", dword32_type); + append_composite_type_field (peb_ldr_type, "initialized", dword32_type); + append_composite_type_field (peb_ldr_type, "ss_handle", void_ptr_type); + append_composite_type_field (peb_ldr_type, "in_load_order", list_type); + append_composite_type_field (peb_ldr_type, "in_memory_order", list_type); + append_composite_type_field (peb_ldr_type, "in_init_order", list_type); + append_composite_type_field (peb_ldr_type, "entry_in_progress", + void_ptr_type); + peb_ldr_ptr_type = arch_type (gdbarch, TYPE_CODE_PTR, + TYPE_LENGTH (void_ptr_type), NULL); + TYPE_TARGET_TYPE (peb_ldr_ptr_type) = peb_ldr_type; + + + /* struct process environment block */ + peb_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); + TYPE_NAME (peb_type) = xstrdup ("peb"); + + /* First bytes contain several flags. */ + append_composite_type_field (peb_type, "flags", dword_ptr_type); + append_composite_type_field (peb_type, "mutant", void_ptr_type); + append_composite_type_field (peb_type, "image_base_address", void_ptr_type); + append_composite_type_field (peb_type, "ldr", peb_ldr_ptr_type); + append_composite_type_field (peb_type, "process_parameters", void_ptr_type); + append_composite_type_field (peb_type, "sub_system_data", void_ptr_type); + append_composite_type_field (peb_type, "process_heap", void_ptr_type); + append_composite_type_field (peb_type, "fast_peb_lock", void_ptr_type); + peb_ptr_type = arch_type (gdbarch, TYPE_CODE_PTR, + TYPE_LENGTH (void_ptr_type), NULL); + TYPE_TARGET_TYPE (peb_ptr_type) = peb_type; + + + /* struct thread information block */ + tib_type = arch_composite_type (gdbarch, NULL, TYPE_CODE_STRUCT); + TYPE_NAME (tib_type) = xstrdup ("tib"); + + /* uint32_t current_seh; %fs:0x0000 */ + append_composite_type_field (tib_type, "current_seh", seh_ptr_type); + /* uint32_t current_top_of_stack; %fs:0x0004 */ + append_composite_type_field (tib_type, "current_top_of_stack", void_ptr_type); + /* uint32_t current_bottom_of_stack; %fs:0x0008 */ + append_composite_type_field (tib_type, "current_bottom_of_stack", + void_ptr_type); + /* uint32_t sub_system_tib; %fs:0x000c */ + append_composite_type_field (tib_type, "sub_system_tib", void_ptr_type); + + /* uint32_t fiber_data; %fs:0x0010 */ + append_composite_type_field (tib_type, "fiber_data", void_ptr_type); + /* uint32_t arbitrary_data_slot; %fs:0x0014 */ + append_composite_type_field (tib_type, "arbitrary_data_slot", void_ptr_type); + /* uint32_t linear_address_tib; %fs:0x0018 */ + append_composite_type_field (tib_type, "linear_address_tib", void_ptr_type); + /* uint32_t environment_pointer; %fs:0x001c */ + append_composite_type_field (tib_type, "environment_pointer", void_ptr_type); + /* uint32_t process_id; %fs:0x0020 */ + append_composite_type_field (tib_type, "process_id", dword_ptr_type); + /* uint32_t current_thread_id; %fs:0x0024 */ + append_composite_type_field (tib_type, "thread_id", dword_ptr_type); + /* uint32_t active_rpc_handle; %fs:0x0028 */ + append_composite_type_field (tib_type, "active_rpc_handle", dword_ptr_type); + /* uint32_t thread_local_storage; %fs:0x002c */ + append_composite_type_field (tib_type, "thread_local_storage", void_ptr_type); + /* uint32_t process_environment_block; %fs:0x0030 */ + append_composite_type_field (tib_type, "process_environment_block", + peb_ptr_type); + /* uint32_t last_error_number; %fs:0x0034 */ + append_composite_type_field (tib_type, "last_error_number", dword_ptr_type); + + tib_ptr_type = arch_type (gdbarch, TYPE_CODE_PTR, + TYPE_LENGTH (void_ptr_type), NULL); + TYPE_TARGET_TYPE (tib_ptr_type) = tib_type; + + return tib_ptr_type; +} + +/* The $_tlb convenience variable is a bit special. We don't know + for sure the type of the value until we actually have a chance to + fetch the data. The type can change depending on gdbarch, so it is + also dependent on which thread you have selected. */ + +/* This function implements the lval_computed support for reading a + $_tlb value. */ + +static void +tlb_value_read (struct value *val) +{ + CORE_ADDR tlb; + struct type *type = check_typedef (value_type (val)); + + if (!target_get_tib_address (inferior_ptid, &tlb)) + error (_("Unable to read tlb")); + store_typed_address (value_contents_raw (val), type, tlb); +} + +/* This function implements the lval_computed support for writing a + $_tlb value. */ + +static void +tlb_value_write (struct value *v, struct value *fromval) +{ + error (_("Impossible to change the Thread Local Base")); +} + +static struct lval_funcs tlb_value_funcs = + { + tlb_value_read, + tlb_value_write + }; + + +/* Return a new value with the correct type for the tlb object of + the current thread using architecture GDBARCH. Return a void value + if there's no object available. */ + +static struct value * +tlb_make_value (struct gdbarch *gdbarch, struct internalvar *var) +{ + if (target_has_stack && !ptid_equal (inferior_ptid, null_ptid)) + { + struct type *type = windows_get_tlb_type (gdbarch); + return allocate_computed_value (type, &tlb_value_funcs, NULL); + } + + return allocate_value (builtin_type (gdbarch)->builtin_void); +} + + +/* Display thread information block of a given thread. */ + +static int +display_one_tib (ptid_t ptid) +{ + gdb_byte *tib = NULL; + gdb_byte *index; + CORE_ADDR thread_local_base; + ULONGEST i, val, max, max_name, size, tib_size; + ULONGEST sizeof_ptr = gdbarch_ptr_bit (target_gdbarch); + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch); + + if (sizeof_ptr == 64) + { + size = sizeof (uint64_t); + tib_size = sizeof (thread_information_64); + max = MAX_TIB64; + } + else + { + size = sizeof (uint32_t); + tib_size = sizeof (thread_information_32); + max = MAX_TIB32; + } + + max_name = max; + + if (maint_display_all_tib) + { + tib_size = FULL_TIB_SIZE; + max = tib_size / size; + } + + tib = alloca (tib_size); + + if (target_get_tib_address (ptid, &thread_local_base) == 0) + { + printf_filtered (_("Unable to get thread local base for %s\n"), + target_pid_to_str (ptid)); + return -1; + } + + if (target_read (¤t_target, TARGET_OBJECT_MEMORY, + NULL, tib, thread_local_base, tib_size) != tib_size) + { + printf_filtered (_("Unable to read thread information block for %s at \ +address %s\n"), + target_pid_to_str (ptid), + paddress (target_gdbarch, thread_local_base)); + return -1; + } + + printf_filtered (_("Thread Information Block %s at %s\n"), + target_pid_to_str (ptid), + paddress (target_gdbarch, thread_local_base)); + + index = (gdb_byte *) tib; + + /* All fields have the size of a pointer, this allows to iterate + using the same for loop for both layouts. */ + for (i = 0; i < max; i++) + { + val = extract_unsigned_integer (index, size, byte_order); + if (i < max_name) + printf_filtered (_("%s is 0x%s\n"), TIB_NAME[i], phex (val, size)); + else if (val != 0) + printf_filtered (_("TIB[0x%s] is 0x%s\n"), phex (i * size, 2), + phex (val, size)); + index += size; + } + 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) + { + struct thread_info *tp; + int gdb_id = value_as_long (parse_and_eval (args)); + + tp = find_thread_id (gdb_id); + + if (!tp) + error (_("Thread ID %d not known."), gdb_id); + + if (!target_thread_alive (tp->ptid)) + error (_("Thread ID %d has terminated."), gdb_id); + + display_one_tib (tp->ptid); + } + else if (!ptid_equal (inferior_ptid, null_ptid)) + display_one_tib (inferior_ptid); +} void windows_xfer_shared_library (const char* so_name, CORE_ADDR load_addr, @@ -36,3 +382,59 @@ windows_xfer_shared_library (const char* so_name, CORE_ADDR load_addr, obstack_grow_str (obstack, paddress (gdbarch, load_addr + 0x1000)); obstack_grow_str (obstack, "\"/>"); } + +static void +show_maint_show_all_tib (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + fprintf_filtered (file, _("Show all non-zero elements of Thread Information \ +Block is %s.\n"), value); +} + +static void +info_w32_command (char *args, int from_tty) +{ + help_list (info_w32_cmdlist, "info w32 ", class_info, gdb_stdout); +} + +static int w32_prefix_command_valid = 0; +void +init_w32_command_list (void) +{ + if (!w32_prefix_command_valid) + { + add_prefix_cmd ("w32", class_info, info_w32_command, + _("Print information specific to Win32 debugging."), + &info_w32_cmdlist, "info w32 ", 0, &infolist); + w32_prefix_command_valid = 1; + } +} + +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 ("tib", "thread-information-block", class_info, 1, + &info_w32_cmdlist); + + add_setshow_boolean_cmd ("show-all-tib", class_maintenance, + &maint_display_all_tib, _("\ +Set whether to display all non-zero fields of thread information block."), _("\ +Show whether to display all non-zero fields of thread information block."), _("\ +Use \"on\" to enable, \"off\" to disable.\n\ +If enabled, all non-zero fields of thread information block are displayed,\n\ +even if their meaning is unknown."), + NULL, + show_maint_show_all_tib, + &maintenance_set_cmdlist, + &maintenance_show_cmdlist); + + /* Explicitly create without lookup, since that tries to create a + value with a void typed value, and when we get here, gdbarch + isn't initialized yet. At this point, we're quite sure there + isn't another convenience variable of the same name. */ + create_internalvar_type_lazy ("_tlb", tlb_make_value); +} diff --git a/gdb/windows-tdep.h b/gdb/windows-tdep.h index 56f73d7ca41..cf1224d2526 100644 --- a/gdb/windows-tdep.h +++ b/gdb/windows-tdep.h @@ -21,6 +21,10 @@ struct obstack; struct gdbarch; +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 gdbarch *gdbarch, -- 2.30.2