Fix TLS access for -static -pthread

I have posted:
	TLS variables access for -static -lpthread executables
	https://sourceware.org/ml/libc-help/2014-03/msg00024.html
and the GDB patch below has been confirmed as OK for current glibcs.

Further work should be done for newer glibcs:
	Improve TLS variables glibc compatibility
	https://sourceware.org/bugzilla/show_bug.cgi?id=16954

Still the patch below implements the feature in a fully functional way backward
compatible with current glibcs, it depends on the following glibc source line:
	csu/libc-tls.c
	main_map->l_tls_modid = 1;

gdb/
2014-05-21  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Fix TLS access for -static -pthread.
	* linux-thread-db.c (struct thread_db_info): Add td_thr_tlsbase_p.
	(try_thread_db_load_1): Initialize it.
	(thread_db_get_thread_local_address): Call it if LM is zero.
	* target.c (target_translate_tls_address): Remove LM_ADDR zero check.
	* target.h (struct target_ops) (to_get_thread_local_address): Add
	load_module_addr comment.

gdb/gdbserver/
2014-05-21  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Fix TLS access for -static -pthread.
	* gdbserver/thread-db.c (struct thread_db): Add td_thr_tlsbase_p.
	(thread_db_get_tls_address): Call it if LOAD_MODULE is zero.
	(thread_db_load_search, try_thread_db_load_1): Initialize it.

gdb/testsuite/
2014-05-21  Jan Kratochvil  <jan.kratochvil@redhat.com>

	Fix TLS access for -static -pthread.
	* gdb.threads/staticthreads.c <HAVE_TLS> (tlsvar): New.
	<HAVE_TLS> (thread_function, main): Initialize it.
	* gdb.threads/staticthreads.exp: Try gdb_compile_pthreads for $have_tls.
	Add clean_restart.
	<$have_tls != "">: Check TLSVAR.

Message-ID: <20140410115204.GB16411@host2.jankratochvil.net>
This commit is contained in:
Jan Kratochvil 2014-05-21 16:25:53 +02:00
parent 0256a6ac4b
commit 5876f5032f
9 changed files with 131 additions and 32 deletions

View file

@ -1,3 +1,13 @@
2014-05-21 Jan Kratochvil <jan.kratochvil@redhat.com>
Fix TLS access for -static -pthread.
* linux-thread-db.c (struct thread_db_info): Add td_thr_tlsbase_p.
(try_thread_db_load_1): Initialize it.
(thread_db_get_thread_local_address): Call it if LM is zero.
* target.c (target_translate_tls_address): Remove LM_ADDR zero check.
* target.h (struct target_ops) (to_get_thread_local_address): Add
load_module_addr comment.
2014-05-21 Pedro Alves <palves@redhat.com> 2014-05-21 Pedro Alves <palves@redhat.com>
* dcache.c (dcache_read_memory_partial): If reading the cache line * dcache.c (dcache_read_memory_partial): If reading the cache line

View file

@ -1,3 +1,10 @@
2014-05-21 Jan Kratochvil <jan.kratochvil@redhat.com>
Fix TLS access for -static -pthread.
* gdbserver/thread-db.c (struct thread_db): Add td_thr_tlsbase_p.
(thread_db_get_tls_address): Call it if LOAD_MODULE is zero.
(thread_db_load_search, try_thread_db_load_1): Initialize it.
2014-05-20 Pedro Alves <palves@redhat.com> 2014-05-20 Pedro Alves <palves@redhat.com>
* linux-aarch64-low.c (aarch64_insert_point) * linux-aarch64-low.c (aarch64_insert_point)

View file

@ -88,6 +88,9 @@ struct thread_db
td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th, td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th,
psaddr_t map_address, psaddr_t map_address,
size_t offset, psaddr_t *address); size_t offset, psaddr_t *address);
td_err_e (*td_thr_tlsbase_p) (const td_thrhandle_t *th,
unsigned long int modid,
psaddr_t *base);
const char ** (*td_symbol_list_p) (void); const char ** (*td_symbol_list_p) (void);
}; };
@ -503,7 +506,10 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
if (thread_db == NULL || !thread_db->all_symbols_looked_up) if (thread_db == NULL || !thread_db->all_symbols_looked_up)
return TD_ERR; return TD_ERR;
if (thread_db->td_thr_tls_get_addr_p == NULL) /* If td_thr_tls_get_addr is missing rather do not expect td_thr_tlsbase
could work. */
if (thread_db->td_thr_tls_get_addr_p == NULL
|| (load_module == 0 && thread_db->td_thr_tlsbase_p == NULL))
return -1; return -1;
lwp = get_thread_lwp (thread); lwp = get_thread_lwp (thread);
@ -514,12 +520,28 @@ thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
saved_inferior = current_inferior; saved_inferior = current_inferior;
current_inferior = thread; current_inferior = thread;
/* Note the cast through uintptr_t: this interface only works if
a target address fits in a psaddr_t, which is a host pointer. if (load_module != 0)
So a 32-bit debugger can not access 64-bit TLS through this. */ {
err = thread_db->td_thr_tls_get_addr_p (&lwp->th, /* Note the cast through uintptr_t: this interface only works if
(psaddr_t) (uintptr_t) load_module, a target address fits in a psaddr_t, which is a host pointer.
offset, &addr); So a 32-bit debugger can not access 64-bit TLS through this. */
err = thread_db->td_thr_tls_get_addr_p (&lwp->th,
(psaddr_t) (uintptr_t) load_module,
offset, &addr);
}
else
{
/* This code path handles the case of -static -pthread executables:
https://sourceware.org/ml/libc-help/2014-03/msg00024.html
For older GNU libc r_debug.r_map is NULL. For GNU libc after
PR libc/16831 due to GDB PR threads/16954 LOAD_MODULE is also NULL.
The constant number 1 depends on GNU __libc_setup_tls
initialization of l_tls_modid to 1. */
err = thread_db->td_thr_tlsbase_p (&lwp->th, 1, &addr);
addr = (char *) addr + offset;
}
current_inferior = saved_inferior; current_inferior = saved_inferior;
if (err == TD_OK) if (err == TD_OK)
{ {
@ -571,6 +593,7 @@ thread_db_load_search (void)
tdb->td_ta_set_event_p = &td_ta_set_event; tdb->td_ta_set_event_p = &td_ta_set_event;
tdb->td_ta_event_getmsg_p = &td_ta_event_getmsg; tdb->td_ta_event_getmsg_p = &td_ta_event_getmsg;
tdb->td_thr_tls_get_addr_p = &td_thr_tls_get_addr; tdb->td_thr_tls_get_addr_p = &td_thr_tls_get_addr;
tdb->td_thr_tlsbase_p = &td_thr_tlsbase;
return 1; return 1;
} }
@ -639,6 +662,7 @@ try_thread_db_load_1 (void *handle)
CHK (0, tdb->td_ta_set_event_p = dlsym (handle, "td_ta_set_event")); CHK (0, tdb->td_ta_set_event_p = dlsym (handle, "td_ta_set_event"));
CHK (0, tdb->td_ta_event_getmsg_p = dlsym (handle, "td_ta_event_getmsg")); CHK (0, tdb->td_ta_event_getmsg_p = dlsym (handle, "td_ta_event_getmsg"));
CHK (0, tdb->td_thr_tls_get_addr_p = dlsym (handle, "td_thr_tls_get_addr")); CHK (0, tdb->td_thr_tls_get_addr_p = dlsym (handle, "td_thr_tls_get_addr"));
CHK (0, tdb->td_thr_tlsbase_p = dlsym (handle, "td_thr_tlsbase"));
#undef CHK #undef CHK

View file

@ -196,6 +196,9 @@ struct thread_db_info
td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th, td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th,
psaddr_t map_address, psaddr_t map_address,
size_t offset, psaddr_t *address); size_t offset, psaddr_t *address);
td_err_e (*td_thr_tlsbase_p) (const td_thrhandle_t *th,
unsigned long int modid,
psaddr_t *base);
}; };
/* List of known processes using thread_db, and the required /* List of known processes using thread_db, and the required
@ -799,6 +802,7 @@ try_thread_db_load_1 (struct thread_db_info *info)
info->td_ta_event_getmsg_p = dlsym (info->handle, "td_ta_event_getmsg"); info->td_ta_event_getmsg_p = dlsym (info->handle, "td_ta_event_getmsg");
info->td_thr_event_enable_p = dlsym (info->handle, "td_thr_event_enable"); info->td_thr_event_enable_p = dlsym (info->handle, "td_thr_event_enable");
info->td_thr_tls_get_addr_p = dlsym (info->handle, "td_thr_tls_get_addr"); info->td_thr_tls_get_addr_p = dlsym (info->handle, "td_thr_tls_get_addr");
info->td_thr_tlsbase_p = dlsym (info->handle, "td_thr_tlsbase");
if (thread_db_find_new_threads_silently (inferior_ptid) != 0) if (thread_db_find_new_threads_silently (inferior_ptid) != 0)
{ {
@ -1811,21 +1815,39 @@ thread_db_get_thread_local_address (struct target_ops *ops,
info = get_thread_db_info (ptid_get_pid (ptid)); info = get_thread_db_info (ptid_get_pid (ptid));
/* glibc doesn't provide the needed interface. */
if (!info->td_thr_tls_get_addr_p)
throw_error (TLS_NO_LIBRARY_SUPPORT_ERROR,
_("No TLS library support"));
/* Caller should have verified that lm != 0. */
gdb_assert (lm != 0);
/* Finally, get the address of the variable. */ /* Finally, get the address of the variable. */
/* Note the cast through uintptr_t: this interface only works if if (lm != 0)
a target address fits in a psaddr_t, which is a host pointer. {
So a 32-bit debugger can not access 64-bit TLS through this. */ /* glibc doesn't provide the needed interface. */
err = info->td_thr_tls_get_addr_p (&thread_info->private->th, if (!info->td_thr_tls_get_addr_p)
(psaddr_t)(uintptr_t) lm, throw_error (TLS_NO_LIBRARY_SUPPORT_ERROR,
offset, &address); _("No TLS library support"));
/* Note the cast through uintptr_t: this interface only works if
a target address fits in a psaddr_t, which is a host pointer.
So a 32-bit debugger can not access 64-bit TLS through this. */
err = info->td_thr_tls_get_addr_p (&thread_info->private->th,
(psaddr_t)(uintptr_t) lm,
offset, &address);
}
else
{
/* If glibc doesn't provide the needed interface throw an error
that LM is zero - normally cases it should not be. */
if (!info->td_thr_tlsbase_p)
throw_error (TLS_LOAD_MODULE_NOT_FOUND_ERROR,
_("TLS load module not found"));
/* This code path handles the case of -static -pthread executables:
https://sourceware.org/ml/libc-help/2014-03/msg00024.html
For older GNU libc r_debug.r_map is NULL. For GNU libc after
PR libc/16831 due to GDB PR threads/16954 LOAD_MODULE is also NULL.
The constant number 1 depends on GNU __libc_setup_tls
initialization of l_tls_modid to 1. */
err = info->td_thr_tlsbase_p (&thread_info->private->th,
1, &address);
address = (char *) address + offset;
}
#ifdef THREAD_DB_HAS_TD_NOTALLOC #ifdef THREAD_DB_HAS_TD_NOTALLOC
/* The memory hasn't been allocated, yet. */ /* The memory hasn't been allocated, yet. */

View file

@ -757,10 +757,6 @@ target_translate_tls_address (struct objfile *objfile, CORE_ADDR offset)
/* Fetch the load module address for this objfile. */ /* Fetch the load module address for this objfile. */
lm_addr = gdbarch_fetch_tls_load_module_address (target_gdbarch (), lm_addr = gdbarch_fetch_tls_load_module_address (target_gdbarch (),
objfile); objfile);
/* If it's 0, throw the appropriate exception. */
if (lm_addr == 0)
throw_error (TLS_LOAD_MODULE_NOT_FOUND_ERROR,
_("TLS load module not found"));
addr = target->to_get_thread_local_address (target, ptid, addr = target->to_get_thread_local_address (target, ptid,
lm_addr, offset); lm_addr, offset);

View file

@ -605,7 +605,8 @@ struct target_ops
thread-local storage for the thread PTID and the shared library thread-local storage for the thread PTID and the shared library
or executable file given by OBJFILE. If that block of or executable file given by OBJFILE. If that block of
thread-local storage hasn't been allocated yet, this function thread-local storage hasn't been allocated yet, this function
may return an error. */ may return an error. LOAD_MODULE_ADDR may be zero for statically
linked multithreaded inferiors. */
CORE_ADDR (*to_get_thread_local_address) (struct target_ops *ops, CORE_ADDR (*to_get_thread_local_address) (struct target_ops *ops,
ptid_t ptid, ptid_t ptid,
CORE_ADDR load_module_addr, CORE_ADDR load_module_addr,

View file

@ -1,3 +1,12 @@
2014-05-21 Jan Kratochvil <jan.kratochvil@redhat.com>
Fix TLS access for -static -pthread.
* gdb.threads/staticthreads.c <HAVE_TLS> (tlsvar): New.
<HAVE_TLS> (thread_function, main): Initialize it.
* gdb.threads/staticthreads.exp: Try gdb_compile_pthreads for $have_tls.
Add clean_restart.
<$have_tls != "">: Check TLSVAR.
2014-05-21 Pedro Alves <palves@redhat.com> 2014-05-21 Pedro Alves <palves@redhat.com>
* gdb.base/dcache-line-read-error.c: New. * gdb.base/dcache-line-read-error.c: New.

View file

@ -28,10 +28,17 @@
sem_t semaphore; sem_t semaphore;
#ifdef HAVE_TLS
__thread int tlsvar;
#endif
void * void *
thread_function (void *arg) thread_function (void *arg)
{ {
printf ("Thread executing\n"); #ifdef HAVE_TLS
tlsvar = 2;
#endif
printf ("Thread executing\n"); /* tlsvar-is-set */
while (sem_wait (&semaphore) != 0) while (sem_wait (&semaphore) != 0)
{ {
if (errno != EINTR) if (errno != EINTR)
@ -57,6 +64,9 @@ main (int argc, char **argv)
return -1; return -1;
} }
#ifdef HAVE_TLS
tlsvar = 1;
#endif
/* Create a thread, wait for it to complete. */ /* Create a thread, wait for it to complete. */
{ {

View file

@ -22,11 +22,16 @@
standard_testfile standard_testfile
set static_flag "-static" set static_flag "-static"
if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \ foreach have_tls { "-DHAVE_TLS" "" } {
executable \ if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" \
[list debug "additional_flags=${static_flag}" \ executable \
]] != "" } { [list debug "additional_flags=${static_flag} ${have_tls}" \
return -1 ]] == "" } {
break
}
if { $have_tls == "" } {
return -1
}
} }
clean_restart ${binfile} clean_restart ${binfile}
@ -89,3 +94,18 @@ gdb_test_multiple "quit" "$test" {
pass "$test" pass "$test"
} }
} }
clean_restart ${binfile}
if { "$have_tls" != "" } {
if ![runto_main] {
return -1
}
gdb_breakpoint [gdb_get_line_number "tlsvar-is-set"]
gdb_continue_to_breakpoint "tlsvar-is-set" ".* tlsvar-is-set .*"
gdb_test "p tlsvar" " = 2" "tlsvar in thread"
gdb_test "thread 1" ".*"
# Unwind from pthread_join.
gdb_test "up 10" " in main .*"
gdb_test "p tlsvar" " = 1" "tlsvar in main"
}