Re: RFC: Access TLS symbols without DWARF debuginfo

Daniel Jacobowitz
2006-10-10T03:22:37Z

On Fri, Aug 25, 2006 at 09:43:16AM -0400, Daniel Jacobowitz wrote:
> Thanks for doing this. It's a good idea, and the first step to not
> needing libthread_db to do this - in most cases the gdbarch vector
> should have enough knowledge. And overall the patch looks very nice.
>
> The same general comments apply as to your other patch, e.g. about a
> ChangeLog.
>
> Could you explain why you added a new kind of value for this? I'd have
> thought that when we wanted the address of the symbol, we should be
> able to resolve it. So instead of value_at_lazy_tls, we would resolve
> the address using the target. Expressions persist between threads and
> executions, but values shouldn't. That should simplify the code a
> little.

I never saw a response to this question, and the patch got folded into
one of your other big changes. Combining patches makes it harder to
review them, so I didn't get back to this until now.

I've committed the attached, based on your patch. I simplified it in a
couple of places. We don't need to worry about thread-local function
symbols, and evaluating the address earlier saves any changes to the
value infrastructure.

Thanks again for submitting this, and the testcase.

Eli, this patch added support for printing thread local (__thread)
variables in files without debugging information, on platforms where we
could already do it with debugging information. I'm undecided on
whether that's NEWS-worthy. What do you think?

I am going to keep reviewing patches, as I can find the time, but my
time to do so is limited; I really need help from the other maintainers
of GDB, and of course the better quality a patch the easier it is to
review :-) Jan, I'll try to look at some more of yours later.

--
Daniel Jacobowitz
CodeSourcery

2006-10-09 Jan Kratochvil <jan.kratochvil@redhat.com>
Daniel Jacobowitz <dan@codesourcery.com>

* Makefile.in (expprint.o, parse.o, target.o): Update.
* dwarf2loc.c (dwarf_expr_tls_address): Move body to
target_translate_tls_address. Call it.
* eval.c (evaluate_subexp_standard): Handle UNOP_MEMVAL_TLS.
* expprint.c (print_subexp_standard): Likewise.
(op_name_standard, dump_subexp_body_standard): Likewise.
* expression.h (enum exp_opcode): Add UNOP_MEMVAL_TLS.
(union exp_element): Add objfile.
* parse.c (write_exp_elt_objfile): New function.
(msym_tls_symbol_type): New.
(write_exp_msymbol): Handle TLS.
(operator_length_standard): Handle UNOP_MEMVAL_TLS.
(build_parse): Initialize msym_tls_symbol_type.
* parser-defs.h (write_exp_elt_objfile): New prototype.
* target.c (target_translate_tls_address): New.
* target.h (target_translate_tls_address): Add prototype.

2006-10-09 Jan Kratochvil <jan.kratochvil@redhat.com>

* gdb.threads/tls-nodebug.c, gdb.threads/tls-nodebug.exp: New test.

Index: Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.845
diff -u -p -r1.845 Makefile.in
--- Makefile.in 5 Oct 2006 19:42:31 -0000 1.845
+++ Makefile.in 9 Oct 2006 22:25:17 -0000
@@ -1982,7 +1982,7 @@ exec.o: exec.c $(defs_h) $(frame_h) $(in
$(xcoffsolib_h) $(observer_h)
expprint.o: expprint.c $(defs_h) $(symtab_h) $(gdbtypes_h) $(expression_h) \
$(value_h) $(language_h) $(parser_defs_h) $(user_regs_h) $(target_h) \
- $(gdb_string_h) $(block_h)
+ $(gdb_string_h) $(block_h) $(objfiles_h)
fbsd-nat.o: fbsd-nat.c $(defs_h) $(gdbcore_h) $(inferior_h) $(regcache_h) \
$(regset_h) $(gdb_assert_h) $(gdb_string_h) $(elf_bfd_h) \
$(fbsd_nat_h)
@@ -2429,7 +2429,7 @@ osabi.o: osabi.c $(defs_h) $(gdb_assert_
parse.o: parse.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \
$(frame_h) $(expression_h) $(value_h) $(command_h) $(language_h) \
$(f_lang_h) $(parser_defs_h) $(gdbcmd_h) $(symfile_h) $(inferior_h) \
- $(doublest_h) $(gdb_assert_h) $(block_h) $(source_h)
+ $(doublest_h) $(gdb_assert_h) $(block_h) $(source_h) $(objfiles_h)
p-exp.o: p-exp.c $(defs_h) $(gdb_string_h) $(expression_h) $(value_h) \
$(parser_defs_h) $(language_h) $(p_lang_h) $(bfd_h) $(symfile_h) \
$(objfiles_h) $(block_h)
@@ -2757,7 +2757,8 @@ symtab.o: symtab.c $(defs_h) $(symtab_h)
$(gdb_stat_h) $(cp_abi_h) $(observer_h)
target.o: target.c $(defs_h) $(gdb_string_h) $(target_h) $(gdbcmd_h) \
$(symtab_h) $(inferior_h) $(bfd_h) $(symfile_h) $(objfiles_h) \
- $(gdb_wait_h) $(dcache_h) $(regcache_h) $(gdb_assert_h) $(gdbcore_h)
+ $(gdb_wait_h) $(dcache_h) $(regcache_h) $(gdb_assert_h) $(gdbcore_h) \
+ $(exceptions_h)
target-memory.o: target-memory.c $(defs_h) $(vec_h) $(target_h) \
$(memory_map_h) $(gdb_assert_h)
thread.o: thread.c $(defs_h) $(symtab_h) $(frame_h) $(inferior_h) \
Index: dwarf2loc.c
===================================================================
RCS file: /cvs/src/src/gdb/dwarf2loc.c,v
retrieving revision 1.33
diff -u -p -r1.33 dwarf2loc.c
--- dwarf2loc.c 17 Dec 2005 22:33:59 -0000 1.33
+++ dwarf2loc.c 9 Oct 2006 22:25:17 -0000
@@ -189,86 +189,8 @@ static CORE_ADDR
dwarf_expr_tls_address (void *baton, CORE_ADDR offset)
{
struct dwarf_expr_baton *debaton = (struct dwarf_expr_baton *) baton;
- volatile CORE_ADDR addr = 0;

- if (target_get_thread_local_address_p ()
- && gdbarch_fetch_tls_load_module_address_p (current_gdbarch))
- {
- ptid_t ptid = inferior_ptid;
- struct objfile *objfile = debaton->objfile;
- volatile struct gdb_exception ex;
-
- TRY_CATCH (ex, RETURN_MASK_ALL)
- {
- CORE_ADDR lm_addr;
-
- /* Fetch the load module address for this objfile. */
- lm_addr = gdbarch_fetch_tls_load_module_address (current_gdbarch,
- 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_get_thread_local_address (ptid, lm_addr, offset);
- }
- /* If an error occurred, print TLS related messages here. Otherwise,
- throw the error to some higher catcher. */
- if (ex.reason < 0)
- {
- int objfile_is_library = (objfile->flags & OBJF_SHARED);
-
- switch (ex.error)
- {
- case TLS_NO_LIBRARY_SUPPORT_ERROR:
- error (_("Cannot find thread-local variables in this thread library."));
- break;
- case TLS_LOAD_MODULE_NOT_FOUND_ERROR:
- if (objfile_is_library)
- error (_("Cannot find shared library `%s' in dynamic"
- " linker's load module list"), objfile->name);
- else
- error (_("Cannot find executable file `%s' in dynamic"
- " linker's load module list"), objfile->name);
- break;
- case TLS_NOT_ALLOCATED_YET_ERROR:
- if (objfile_is_library)
- error (_("The inferior has not yet allocated storage for"
- " thread-local variables in\n"
- "the shared library `%s'\n"
- "for %s"),
- objfile->name, target_pid_to_str (ptid));
- else
- error (_("The inferior has not yet allocated storage for"
- " thread-local variables in\n"
- "the executable `%s'\n"
- "for %s"),
- objfile->name, target_pid_to_str (ptid));
- break;
- case TLS_GENERIC_ERROR:
- if (objfile_is_library)
- error (_("Cannot find thread-local storage for %s, "
- "shared library %s:\n%s"),
- target_pid_to_str (ptid),
- objfile->name, ex.message);
- else
- error (_("Cannot find thread-local storage for %s, "
- "executable file %s:\n%s"),
- target_pid_to_str (ptid),
- objfile->name, ex.message);
- break;
- default:
- throw_exception (ex);
- break;
- }
- }
- }
- /* 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"));
-
- return addr;
+ return target_translate_tls_address (debaton->objfile, offset);
}

/* Evaluate a location description, starting at DATA and with length
Index: eval.c
===================================================================
RCS file: /cvs/src/src/gdb/eval.c,v
retrieving revision 1.63
diff -u -p -r1.63 eval.c
--- eval.c 25 Jul 2006 04:24:50 -0000 1.63
+++ eval.c 9 Oct 2006 22:25:18 -0000
@@ -2019,6 +2019,21 @@ evaluate_subexp_standard (struct type *e
return value_at_lazy (exp->elts[pc + 1].type,
value_as_address (arg1));

+ case UNOP_MEMVAL_TLS:
+ (*pos) += 3;
+ arg1 = evaluate_subexp (expect_type, exp, pos, noside);
+ if (noside == EVAL_SKIP)
+ goto nosideret;
+ if (noside == EVAL_AVOID_SIDE_EFFECTS)
+ return value_zero (exp->elts[pc + 2].type, lval_memory);
+ else
+ {
+ CORE_ADDR tls_addr;
+ tls_addr = target_translate_tls_address (exp->elts[pc + 1].objfile,
+ value_as_address (arg1));
+ return value_at_lazy (exp->elts[pc + 2].type, tls_addr);
+ }
+
case UNOP_PREINCREMENT:
arg1 = evaluate_subexp (expect_type, exp, pos, noside);
if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
Index: expprint.c
===================================================================
RCS file: /cvs/src/src/gdb/expprint.c,v
retrieving revision 1.24
diff -u -p -r1.24 expprint.c
--- expprint.c 7 Aug 2006 03:30:54 -0000 1.24
+++ expprint.c 9 Oct 2006 22:25:18 -0000
@@ -31,6 +31,7 @@
#include "target.h"
#include "gdb_string.h"
#include "block.h"
+#include "objfiles.h"

#ifdef HAVE_CTYPE_H
#include <ctype.h>
@@ -414,6 +415,18 @@ print_subexp_standard (struct expression
fputs_filtered (")", stream);
return;

+ case UNOP_MEMVAL_TLS:
+ (*pos) += 3;
+ if ((int) prec > (int) PREC_PREFIX)
+ fputs_filtered ("(", stream);
+ fputs_filtered ("{", stream);
+ type_print (exp->elts[pc + 2].type, "", stream, 0);
+ fputs_filtered ("} ", stream);
+ print_subexp (exp, pos, stream, PREC_PREFIX);
+ if ((int) prec > (int) PREC_PREFIX)
+ fputs_filtered (")", stream);
+ return;
+
case BINOP_ASSIGN_MODIFY:
opcode = exp->elts[pc + 1].opcode;
(*pos) += 2;
@@ -694,6 +707,8 @@ op_name_standard (enum exp_opcode opcode
return "UNOP_CAST";
case UNOP_MEMVAL:
return "UNOP_MEMVAL";
+ case UNOP_MEMVAL_TLS:
+ return "UNOP_MEMVAL_TLS";
case UNOP_NEG:
return "UNOP_NEG";
case UNOP_LOGICAL_NOT:
@@ -999,6 +1014,16 @@ dump_subexp_body_standard (struct expres
fprintf_filtered (stream, ")");
elt = dump_subexp (exp, stream, elt + 2);
break;
+ case UNOP_MEMVAL_TLS:
+ fprintf_filtered (stream, "TLS type @");
+ gdb_print_host_address (exp->elts[elt + 1].type, stream);
+ fprintf_filtered (stream, " (__thread /* \"%s\" */ ",
+ (exp->elts[elt].objfile == NULL ? "(null)"
+ : exp->elts[elt].objfile->name));
+ type_print (exp->elts[elt + 1].type, NULL, stream, 0);
+ fprintf_filtered (stream, ")");
+ elt = dump_subexp (exp, stream, elt + 3);
+ break;
case OP_TYPE:
fprintf_filtered (stream, "Type @");
gdb_print_host_address (exp->elts[elt].type, stream);
Index: expression.h
===================================================================
RCS file: /cvs/src/src/gdb/expression.h,v
retrieving revision 1.18
diff -u -p -r1.18 expression.h
--- expression.h 17 Dec 2005 22:33:59 -0000 1.18
+++ expression.h 9 Oct 2006 22:25:18 -0000
@@ -234,6 +234,13 @@ enum exp_opcode
following subexpression. */
UNOP_MEMVAL,

+ /* UNOP_MEMVAL_TLS is followed by a `struct objfile' pointer in the next
+ exp_element and a type pointer in the following exp_element.
+ With another UNOP_MEMVAL_TLS at the end, this makes four exp_elements.
+ It casts the contents of the word offsetted by the value of the
+ following subexpression from the TLS specified by `struct objfile'. */
+ UNOP_MEMVAL_TLS,
+
/* UNOP_... operate on one value from a following subexpression
and replace it with a result. They take no immediate arguments. */

@@ -360,6 +367,7 @@ union exp_element
struct type *type;
struct internalvar *internalvar;
struct block *block;
+ struct objfile *objfile;
};

struct expression
Index: parse.c
===================================================================
RCS file: /cvs/src/src/gdb/parse.c,v
retrieving revision 1.53
diff -u -p -r1.53 parse.c
--- parse.c 6 Jul 2006 14:00:48 -0000 1.53
+++ parse.c 9 Oct 2006 22:25:18 -0000
@@ -53,6 +53,7 @@
#include "gdb_assert.h"
#include "block.h"
#include "source.h"
+#include "objfiles.h"

/* Standard set of definitions for printing, dumping, prefixifying,
* and evaluating expressions. */
@@ -219,6 +220,15 @@ write_exp_elt_block (struct block *b)
}

void
+write_exp_elt_objfile (struct objfile *objfile)
+{
+ union exp_element tmp;
+ memset (&tmp, 0, sizeof (union exp_element));
+ tmp.objfile = objfile;
+ write_exp_elt (tmp);
+}
+
+void
write_exp_elt_longcst (LONGEST expelt)
{
union exp_element tmp;
@@ -378,6 +388,7 @@ write_exp_bitstring (struct stoken str)
static struct type *msym_text_symbol_type;
static struct type *msym_data_symbol_type;
static struct type *msym_unknown_symbol_type;
+static struct type *msym_tls_symbol_type;

void
write_exp_msymbol (struct minimal_symbol *msymbol,
@@ -397,6 +408,22 @@ write_exp_msymbol (struct minimal_symbol

write_exp_elt_opcode (OP_LONG);

+ if (SYMBOL_BFD_SECTION (msymbol)->flags & SEC_THREAD_LOCAL)
+ {
+ bfd *bfd = SYMBOL_BFD_SECTION (msymbol)->owner;
+ struct objfile *ofp;
+
+ ALL_OBJFILES (ofp)
+ if (ofp->obfd == bfd)
+ break;
+
+ write_exp_elt_opcode (UNOP_MEMVAL_TLS);
+ write_exp_elt_objfile (ofp);
+ write_exp_elt_type (msym_tls_symbol_type);
+ write_exp_elt_opcode (UNOP_MEMVAL_TLS);
+ return;
+ }
+
write_exp_elt_opcode (UNOP_MEMVAL);
switch (msymbol->type)
{
@@ -904,6 +931,11 @@ operator_length_standard (struct express
args = 1;
break;

+ case UNOP_MEMVAL_TLS:
+ oplen = 4;
+ args = 1;
+ break;
+
case UNOP_ABS:
case UNOP_CAP:
case UNOP_CHR:
@@ -1341,6 +1373,10 @@ build_parse (void)
init_type (TYPE_CODE_INT, 1, 0,
"<variable (not text or data), no debug info>",
NULL);
+
+ msym_tls_symbol_type =
+ init_type (TYPE_CODE_INT, TARGET_INT_BIT / HOST_CHAR_BIT, 0,
+ "<thread local variable, no debug info>", NULL);
}

/* This function avoids direct calls to fprintf
Index: parser-defs.h
===================================================================
RCS file: /cvs/src/src/gdb/parser-defs.h,v
retrieving revision 1.20
diff -u -p -r1.20 parser-defs.h
--- parser-defs.h 17 Dec 2005 22:34:01 -0000 1.20
+++ parser-defs.h 9 Oct 2006 22:25:18 -0000
@@ -131,6 +131,8 @@ extern void write_exp_bitstring (struct

extern void write_exp_elt_block (struct block *);

+extern void write_exp_elt_objfile (struct objfile *objfile);
+
extern void write_exp_msymbol (struct minimal_symbol *,
struct type *, struct type *);

Index: target.c
===================================================================
RCS file: /cvs/src/src/gdb/target.c,v
retrieving revision 1.126
diff -u -p -r1.126 target.c
--- target.c 21 Sep 2006 14:00:53 -0000 1.126
+++ target.c 9 Oct 2006 22:25:18 -0000
@@ -39,6 +39,7 @@
#include "regcache.h"
#include "gdb_assert.h"
#include "gdbcore.h"
+#include "exceptions.h"

static void target_info (char *, int);

@@ -758,6 +759,92 @@ pop_target (void)
internal_error (__FILE__, __LINE__, _("failed internal consistency check"));
}

+/* Using the objfile specified in BATON, find the address for the
+ current thread's thread-local storage with offset OFFSET. */
+CORE_ADDR
+target_translate_tls_address (struct objfile *objfile, CORE_ADDR offset)
+{
+ volatile CORE_ADDR addr = 0;
+
+ if (target_get_thread_local_address_p ()
+ && gdbarch_fetch_tls_load_module_address_p (current_gdbarch))
+ {
+ ptid_t ptid = inferior_ptid;
+ volatile struct gdb_exception ex;
+
+ TRY_CATCH (ex, RETURN_MASK_ALL)
+ {
+ CORE_ADDR lm_addr;
+
+ /* Fetch the load module address for this objfile. */
+ lm_addr = gdbarch_fetch_tls_load_module_address (current_gdbarch,
+ 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_get_thread_local_address (ptid, lm_addr, offset);
+ }
+ /* If an error occurred, print TLS related messages here. Otherwise,
+ throw the error to some higher catcher. */
+ if (ex.reason < 0)
+ {
+ int objfile_is_library = (objfile->flags & OBJF_SHARED);
+
+ switch (ex.error)
+ {
+ case TLS_NO_LIBRARY_SUPPORT_ERROR:
+ error (_("Cannot find thread-local variables in this thread library."));
+ break;
+ case TLS_LOAD_MODULE_NOT_FOUND_ERROR:
+ if (objfile_is_library)
+ error (_("Cannot find shared library `%s' in dynamic"
+ " linker's load module list"), objfile->name);
+ else
+ error (_("Cannot find executable file `%s' in dynamic"
+ " linker's load module list"), objfile->name);
+ break;
+ case TLS_NOT_ALLOCATED_YET_ERROR:
+ if (objfile_is_library)
+ error (_("The inferior has not yet allocated storage for"
+ " thread-local variables in\n"
+ "the shared library `%s'\n"
+ "for %s"),
+ objfile->name, target_pid_to_str (ptid));
+ else
+ error (_("The inferior has not yet allocated storage for"
+ " thread-local variables in\n"
+ "the executable `%s'\n"
+ "for %s"),
+ objfile->name, target_pid_to_str (ptid));
+ break;
+ case TLS_GENERIC_ERROR:
+ if (objfile_is_library)
+ error (_("Cannot find thread-local storage for %s, "
+ "shared library %s:\n%s"),
+ target_pid_to_str (ptid),
+ objfile->name, ex.message);
+ else
+ error (_("Cannot find thread-local storage for %s, "
+ "executable file %s:\n%s"),
+ target_pid_to_str (ptid),
+ objfile->name, ex.message);
+ break;
+ default:
+ throw_exception (ex);
+ break;
+ }
+ }
+ }
+ /* 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"));
+
+ return addr;
+}
+
#undef MIN
#define MIN(A, B) (((A) <= (B)) ? (A) : (B))

Index: target.h
===================================================================
RCS file: /cvs/src/src/gdb/target.h,v
retrieving revision 1.89
diff -u -p -r1.89 target.h
--- target.h 21 Sep 2006 14:00:53 -0000 1.89
+++ target.h 9 Oct 2006 22:25:18 -0000
@@ -1227,6 +1227,9 @@ extern void target_preopen (int);

extern void pop_target (void);

+extern CORE_ADDR target_translate_tls_address (struct objfile *objfile,
+ CORE_ADDR offset);
+
/* Struct section_table maps address ranges to file sections. It is
mostly used with BFD files, but can be used without (e.g. for handling
raw disks, or files not in formats handled by BFD). */
Index: testsuite/gdb.threads/tls-nodebug.c
===================================================================
RCS file: testsuite/gdb.threads/tls-nodebug.c
diff -N testsuite/gdb.threads/tls-nodebug.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/tls-nodebug.c 9 Oct 2006 22:25:19 -0000
@@ -0,0 +1,10 @@
+/* Test accessing TLS based variable without any debug info compiled. */
+
+#include <pthread.h>
+
+__thread int thread_local = 42;
+
+int main(void)
+{
+ return 0;
+}
Index: testsuite/gdb.threads/tls-nodebug.exp
===================================================================
RCS file: testsuite/gdb.threads/tls-nodebug.exp
diff -N testsuite/gdb.threads/tls-nodebug.exp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/gdb.threads/tls-nodebug.exp 9 Oct 2006 22:25:19 -0000
@@ -0,0 +1,52 @@
+# tls.exp -- Expect script to test thread-local storage without debuginfo
+# Copyright (C) 2006 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+# Please email any bugs, comments, and/or additions to this file to:
+# bug-gdb@prep.ai.mit.edu
+
+set testfile tls-nodebug
+set srcfile ${testfile}.c
+set binfile ${objdir}/${subdir}/${testfile}
+
+if [istarget "*-*-linux"] then {
+ set target_cflags "-D_MIT_POSIX_THREADS"
+} else {
+ set target_cflags ""
+}
+
+if {[gdb_compile_pthreads "${srcdir}/${subdir}/${srcfile}" "${binfile}" executable []] != "" } {
+ return -1
+}
+
+gdb_exit
+gdb_start
+gdb_reinitialize_dir $srcdir/$subdir
+
+gdb_load ${binfile}
+if ![runto_main] then {
+ fail "Can't run to main"
+ return 0
+}
+
+# Formerly: Cannot access memory at address 0x0
+gdb_test "p thread_local" "= 42" "thread local storage"
+
+# Done!
+#
+gdb_exit
+
+return 0