From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 28063 invoked by alias); 2 Jul 2002 15:23:20 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 28033 invoked from network); 2 Jul 2002 15:23:14 -0000 Received: from unknown (HELO zwingli.cygnus.com) (208.245.165.35) by sources.redhat.com with SMTP; 2 Jul 2002 15:23:14 -0000 Received: by zwingli.cygnus.com (Postfix, from userid 442) id 7967F5EA11; Tue, 2 Jul 2002 10:23:12 -0500 (EST) To: gdb-patches@sources.redhat.com Subject: RFC: initial TLS patch From: Jim Blandy Date: Tue, 02 Jul 2002 08:23:00 -0000 Message-ID: User-Agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.1 MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-SW-Source: 2002-07/txt/msg00025.txt.bz2 This is a work-in-progress, posted for comments. This code has never been executed, since I only last night got glibc and GCC to actually compile and run a program that uses `__thread', and GCC doesn't yet emit the Dwarf 2 extension for thread-local variables. If you actually want to try it out, remember to run both autoheader and autoconf after patching configure.in. But I'm mostly just hoping for eyeball reviews. Index: gdb/configure.in =================================================================== RCS file: /cvs/src/src/gdb/configure.in,v retrieving revision 1.88 diff -c -r1.88 configure.in *** gdb/configure.in 21 Jun 2002 23:48:39 -0000 1.88 --- gdb/configure.in 29 Jun 2002 03:38:37 -0000 *************** *** 599,604 **** --- 599,621 ---- AC_SUBST(CONFIG_LDFLAGS) fi + dnl See if we have a thread_db header file that #defines TD_NOTALLOC. + if test "x$ac_cv_header_thread_db_h" = "xyes"; then + AC_CACHE_CHECK([whether defines TD_NOTALLOC], + gdb_cv_thread_db_h_defines_td_notalloc, + AC_TRY_COMPILE( + [#include ], + [int i = TD_NOTALLOC;], + gdb_cv_thread_db_h_defines_td_notalloc=yes, + gdb_cv_thread_db_h_defines_td_notalloc=no + ) + ) + fi + if test "x$gdb_cv_thread_db_h_defines_td_notalloc" = "xyes"; then + AC_DEFINE(THREAD_DB_DEFINES_TD_NOTALLOC, 1, + [Define if defines the TD_NOTALLOC error code.]) + fi + dnl The CLI cannot be disabled yet, but may be in the future dnl Handle CLI sub-directory configury. Index: gdb/dwarf2read.c =================================================================== RCS file: /cvs/src/src/gdb/dwarf2read.c,v retrieving revision 1.60 diff -c -r1.60 dwarf2read.c *** gdb/dwarf2read.c 22 Jun 2002 00:05:59 -0000 1.60 --- gdb/dwarf2read.c 29 Jun 2002 03:38:40 -0000 *************** *** 389,394 **** --- 389,400 ---- this function, so we can't say which register it's relative to; use LOC_LOCAL. */ + static int is_thread_local; /* Variable is at a constant offset in the + thread-local storage block for the + current thread and the dynamic linker + module containing this expression. + decode_locdesc returns the offset from + that base. */ /* DW_AT_frame_base values for the current function. frame_base_reg is -1 if DW_AT_frame_base is missing, otherwise it *************** *** 4652,4657 **** --- 4658,4669 ---- { SYMBOL_CLASS (sym) = LOC_LOCAL; } + else if (is_thread_local) + { + SYMBOL_CLASS (sym) = LOC_THREAD_LOCAL_STATIC; + SYMBOL_OBJFILE (sym) = objfile; + SYMBOL_VALUE_ADDRESS (sym) = addr; + } else { fixup_symbol_section (sym, objfile); *************** *** 6152,6157 **** --- 6164,6170 ---- offreg = 0; isderef = 0; islocal = 0; + is_thread_local = 0; optimized_out = 1; while (i < size) *************** *** 6374,6379 **** --- 6387,6397 ---- if (i < size) complain (&dwarf2_complex_location_expr); break; + + case DW_OP_GNU_push_tls_address: + is_thread_local = 1; + stack[++stacki] = 0; + break; default: complain (&dwarf2_unsupported_stack_op, dwarf_stack_op_name (op)); Index: gdb/findvar.c =================================================================== RCS file: /cvs/src/src/gdb/findvar.c,v retrieving revision 1.34 diff -c -r1.34 findvar.c *** gdb/findvar.c 15 May 2002 01:01:56 -0000 1.34 --- gdb/findvar.c 29 Jun 2002 03:38:40 -0000 *************** *** 529,535 **** case LOC_BASEREG: case LOC_BASEREG_ARG: - case LOC_THREAD_LOCAL_STATIC: { struct value *regval; --- 529,534 ---- *************** *** 540,545 **** --- 539,564 ---- addr = value_as_address (regval); addr += SYMBOL_VALUE (var); break; + } + + case LOC_THREAD_LOCAL_STATIC: + { + /* We want to let the target / ABI-specific code construct + this value for us, so we need to dispose of the value + allocated for us above. */ + release_value (v); + value_free (v); + if (target_has_get_thread_local_value ()) + return target_get_thread_local_value (inferior_ptid, + SYMBOL_OBJFILE (var), + SYMBOL_VALUE (var), + SYMBOL_TYPE (var)); + /* It wouldn't be wrong here to try a gdbarch method, too; + finding TLS is an ABI-specific thing. But we don't do that + yet. */ + else + error ("Cannot find thread-local variables on this target"); + break; } case LOC_TYPEDEF: Index: gdb/gdb_thread_db.h =================================================================== RCS file: /cvs/src/src/gdb/gdb_thread_db.h,v retrieving revision 1.3 diff -c -r1.3 gdb_thread_db.h *** gdb/gdb_thread_db.h 6 Mar 2001 08:21:07 -0000 1.3 --- gdb/gdb_thread_db.h 29 Jun 2002 03:38:40 -0000 *************** *** 63,69 **** TD_NOTSD, /* No thread-specific data available. */ TD_MALLOC, /* Out of memory. */ TD_PARTIALREG, /* Not entire register set was read or written. */ ! TD_NOXREGS /* X register set not available for given thread. */ } td_err_e; --- 63,70 ---- TD_NOTSD, /* No thread-specific data available. */ TD_MALLOC, /* Out of memory. */ TD_PARTIALREG, /* Not entire register set was read or written. */ ! TD_NOXREGS, /* X register set not available for given thread. */ ! TD_NOTALLOC /* TLS memory not yet allocated. */ } td_err_e; Index: gdb/hpread.c =================================================================== RCS file: /cvs/src/src/gdb/hpread.c,v retrieving revision 1.21 diff -c -r1.21 hpread.c *** gdb/hpread.c 14 Jun 2002 14:34:25 -0000 1.21 --- gdb/hpread.c 29 Jun 2002 03:38:44 -0000 *************** *** 5743,5749 **** { /* Thread-local variable. */ ! SYMBOL_CLASS (sym) = LOC_THREAD_LOCAL_STATIC; SYMBOL_BASEREG (sym) = CR27_REGNUM; if (objfile->flags & OBJF_SHARED) --- 5743,5749 ---- { /* Thread-local variable. */ ! SYMBOL_CLASS (sym) = LOC_BASEREG; SYMBOL_BASEREG (sym) = CR27_REGNUM; if (objfile->flags & OBJF_SHARED) Index: gdb/printcmd.c =================================================================== RCS file: /cvs/src/src/gdb/printcmd.c,v retrieving revision 1.39 diff -c -r1.39 printcmd.c *** gdb/printcmd.c 11 May 2002 23:48:23 -0000 1.39 --- gdb/printcmd.c 29 Jun 2002 03:38:45 -0000 *************** *** 1275,1283 **** break; case LOC_THREAD_LOCAL_STATIC: ! printf_filtered ( ! "a thread-local variable at offset %ld from the thread base register %s", ! val, REGISTER_NAME (basereg)); break; case LOC_OPTIMIZED_OUT: --- 1275,1283 ---- break; case LOC_THREAD_LOCAL_STATIC: ! printf_filtered ("a thread-local variable at offset %ld in the " ! "thread-local storage for `%s'", ! val, SYMBOL_OBJFILE (sym)->name); break; case LOC_OPTIMIZED_OUT: Index: gdb/solib.c =================================================================== RCS file: /cvs/src/src/gdb/solib.c,v retrieving revision 1.50 diff -c -r1.50 solib.c *** gdb/solib.c 12 May 2002 04:20:06 -0000 1.50 --- gdb/solib.c 29 Jun 2002 03:38:46 -0000 *************** *** 843,848 **** --- 843,870 ---- do_clear_solib (NULL); } + + /* Search the current shared library list for an entry whose object + file is OBJFILE. Return zero if there is no such entry. */ + struct so_list * + get_solib_by_objfile (struct objfile *objfile) + { + struct so_list *l; + + for (l = so_list_head; l; l = l->next) + if (l->objfile == objfile) + return l; + + /* Refresh our list from the inferior, and try again. */ + update_solib_list (0, 0); + for (l = so_list_head; l; l = l->next) + if (l->objfile == objfile) + return l; + + return 0; + } + + void _initialize_solib (void) { Index: gdb/solist.h =================================================================== RCS file: /cvs/src/src/gdb/solist.h,v retrieving revision 1.7 diff -c -r1.7 solist.h *** gdb/solist.h 21 Oct 2001 19:20:30 -0000 1.7 --- gdb/solist.h 29 Jun 2002 03:38:46 -0000 *************** *** 106,111 **** --- 106,114 ---- /* Find solib binary file and open it. */ extern int solib_open (char *in_pathname, char **found_pathname); + /* Return the so_list entry whose object file is OBJFILE. */ + extern struct so_list *get_solib_by_objfile (struct objfile *OBJFILE); + /* FIXME: gdbarch needs to control this variable */ extern struct target_so_ops *current_target_so_ops; Index: gdb/symtab.h =================================================================== RCS file: /cvs/src/src/gdb/symtab.h,v retrieving revision 1.32 diff -c -r1.32 symtab.h *** gdb/symtab.h 15 May 2002 21:19:21 -0000 1.32 --- gdb/symtab.h 29 Jun 2002 03:38:47 -0000 *************** *** 587,594 **** LOC_UNRESOLVED, /* Value is at a thread-specific location calculated by a ! target-specific method. */ ! LOC_THREAD_LOCAL_STATIC, /* The variable does not actually exist in the program. --- 587,596 ---- LOC_UNRESOLVED, /* Value is at a thread-specific location calculated by a ! target-specific method. SYMBOL_OBJFILE gives the object file ! in which the symbol is defined; the symbol's value is the ! offset into that objfile's thread-local storage for the current ! thread. */ LOC_THREAD_LOCAL_STATIC, /* The variable does not actually exist in the program. *************** *** 661,670 **** { /* Used by LOC_BASEREG and LOC_BASEREG_ARG. */ short basereg; } aux_value; - /* Link to a list of aliases for this symbol. Only a "primary/main symbol may have aliases. */ struct alias_list *aliases; --- 663,677 ---- { /* Used by LOC_BASEREG and LOC_BASEREG_ARG. */ short basereg; + + /* The objfile in which this symbol is defined. To find a + thread-local variable (e.g., a variable declared with the + `__thread' storage class), we may need to know which shared + library it's in. */ + struct objfile *objfile; } aux_value; /* Link to a list of aliases for this symbol. Only a "primary/main symbol may have aliases. */ struct alias_list *aliases; *************** *** 680,685 **** --- 687,693 ---- #define SYMBOL_TYPE(symbol) (symbol)->type #define SYMBOL_LINE(symbol) (symbol)->line #define SYMBOL_BASEREG(symbol) (symbol)->aux_value.basereg + #define SYMBOL_OBJFILE(symbol) (symbol)->aux_value.objfile #define SYMBOL_ALIASES(symbol) (symbol)->aliases #define SYMBOL_RANGES(symbol) (symbol)->ranges Index: gdb/target.c =================================================================== RCS file: /cvs/src/src/gdb/target.c,v retrieving revision 1.36 diff -c -r1.36 target.c *** gdb/target.c 15 Jun 2002 21:07:58 -0000 1.36 --- gdb/target.c 29 Jun 2002 03:38:48 -0000 *************** *** 610,615 **** --- 610,616 ---- INHERIT (to_async_mask_value, t); INHERIT (to_find_memory_regions, t); INHERIT (to_make_corefile_notes, t); + INHERIT (to_get_thread_local_value, t); INHERIT (to_magic, t); #undef INHERIT Index: gdb/target.h =================================================================== RCS file: /cvs/src/src/gdb/target.h,v retrieving revision 1.24 diff -c -r1.24 target.h *** gdb/target.h 18 Apr 2002 18:09:06 -0000 1.24 --- gdb/target.h 29 Jun 2002 03:38:49 -0000 *************** *** 180,186 **** /* Returns zero to leave the inferior alone, one to interrupt it. */ extern int (*target_activity_function) (void); ! struct thread_info; /* fwd decl for parameter list below: */ struct target_ops { --- 180,191 ---- /* Returns zero to leave the inferior alone, one to interrupt it. */ extern int (*target_activity_function) (void); ! ! /* Forward declarations for these structure tags, used in target ! operation parameter lists. */ ! struct thread_info; ! struct value; ! struct type; struct target_ops { *************** *** 319,324 **** --- 324,341 ---- void *), void *); char * (*to_make_corefile_notes) (bfd *, int *); + + /* Return the thread-local value of type TYPE at OFFSET in the + thread-local storage for the thread PTID and the shared library + or executable file given by OBJFILE. If that block of + thread-local storage hasn't been allocated yet, this function + may return an error, or try to concoct an appropriate + (non-lvalue) value based on the initialization image. */ + struct value *(*to_get_thread_local_value) (ptid_t PTID, + struct objfile *OBJFILE, + CORE_ADDR OFFSET, + struct type *TYPE); + int to_magic; /* Need sub-structure for target machine related rather than comm related? */ *************** *** 1021,1026 **** --- 1038,1050 ---- #define target_make_corefile_notes(BFD, SIZE_P) \ (current_target.to_make_corefile_notes) (BFD, SIZE_P) + + + /* Thread-local values. */ + #define target_get_thread_local_value \ + (current_target.to_get_thread_local_value) + #define target_has_get_thread_local_value() \ + (target_get_thread_local_value != 0) /* Hook to call target-dependent code after reading in a new symbol table. */ Index: gdb/thread-db.c =================================================================== RCS file: /cvs/src/src/gdb/thread-db.c,v retrieving revision 1.22 diff -c -r1.22 thread-db.c *** gdb/thread-db.c 23 Mar 2002 17:38:13 -0000 1.22 --- gdb/thread-db.c 29 Jun 2002 03:38:49 -0000 *************** *** 32,37 **** --- 32,40 ---- #include "objfiles.h" #include "target.h" #include "regcache.h" + #include "solib.h" + #include "solist.h" + #include "value.h" #ifndef LIBTHREAD_DB_SO #define LIBTHREAD_DB_SO "libthread_db.so.1" *************** *** 108,113 **** --- 111,122 ---- prgregset_t gregs); static td_err_e (*td_thr_event_enable_p) (const td_thrhandle_t *th, int event); + struct link_map; + static td_err_e (*td_thr_tls_get_addr_p) (const td_thrhandle_t *th, + struct link_map *map, + size_t offset, + void **address); + /* Location of the thread creation event breakpoint. The code at this location in the child process will be called by the pthread library whenever a new thread is created. By setting a special breakpoint *************** *** 348,353 **** --- 357,363 ---- td_ta_set_event_p = dlsym (handle, "td_ta_set_event"); td_ta_event_getmsg_p = dlsym (handle, "td_ta_event_getmsg"); td_thr_event_enable_p = dlsym (handle, "td_thr_event_enable"); + td_thr_tls_get_addr_p = dlsym (handle, "td_thr_tls_get_addr"); return 1; } *************** *** 1003,1008 **** --- 1013,1095 ---- return normal_pid_to_str (ptid); } + static struct value * + thread_db_get_thread_local_value (ptid_t ptid, struct objfile *objfile, + CORE_ADDR offset, struct type *type) + { + if (is_thread (ptid)) + { + struct so_list *so; + int objfile_is_library = (objfile->flags & OBJF_SHARED); + td_err_e err; + td_thrhandle_t th; + void *address; + + if (! td_thr_tls_get_addr_p) + error ("Cannot find thread-local variables in this thread library."); + + so = get_solib_by_objfile (objfile); + if (! so) + error ((objfile_is_library + ? ("Cannot find shared library `%s' in dynamic linker's " + "module list") + : ("Cannot find executable file `%s' in dynamic linker's " + "module list")), + objfile->name); + + if (! so->lm_info) + error ("Missing `struct link_map' value in GDB's shared library list"); + + err = td_ta_map_id2thr_p (thread_agent, GET_THREAD (ptid), &th); + if (err != TD_OK) + error ("Cannot find thread %ld: %s", + (long) GET_THREAD (ptid), thread_db_err_str (err)); + + /* That cast there is pretty icky. But thread_db's interface + isn't cross-debugging clean, so there's not much we can do + about it. */ + err = td_thr_tls_get_addr_p (&th, (struct link_map *) so->lm_info, + offset, &address); + + #ifdef THREAD_DB_DEFINES_TD_NOTALLOC + if (err == TD_NOTALLOC) + /* Now, if libthread_db provided the initialization image's + address, we *could* try to build a non-lvalue value from + the initialization image. */ + error ((objfile_is_library + ? ("The inferior has not yet allocated storage for" + " thread-local variables in\n" + "the shared library `%s'\n" + "for the thread %ld") + : ("The inferior has not yet allocated storage for" + " thread-local variables in\n" + "the executable `%s'\n" + "for the thread %ld")), + objfile->name, + (long) GET_THREAD (ptid)); + #endif + + if (err != TD_OK) + error ((objfile_is_library + ? ("Cannot find thread-local storage for thread %ld, " + "shared library %s:\n%s") + : ("Cannot find thread-local storage for thread %ld, " + "executable file %s:\n%s")), + (long) GET_THREAD (ptid), + objfile->name, + thread_db_err_str (err)); + + /* Another cast assuming host == target. Joy. */ + return value_at_lazy (type, (CORE_ADDR) address, 0); + } + + if (target_beneath->to_get_thread_local_value) + return target_beneath->to_get_thread_local_value (ptid, objfile, + offset, type); + + error ("Cannot find thread-local values on this target."); + } + static void init_thread_db_ops (void) { *************** *** 1025,1030 **** --- 1112,1118 ---- thread_db_ops.to_pid_to_str = thread_db_pid_to_str; thread_db_ops.to_stratum = thread_stratum; thread_db_ops.to_has_thread_control = tc_schedlock; + thread_db_ops.to_get_thread_local_value = thread_db_get_thread_local_value; thread_db_ops.to_magic = OPS_MAGIC; }