2006-10-09 Jan Kratochvil <jan.kratochvil@redhat.com>
authorDaniel Jacobowitz <drow@false.org>
Tue, 10 Oct 2006 03:17:53 +0000 (03:17 +0000)
committerDaniel Jacobowitz <drow@false.org>
Tue, 10 Oct 2006 03:17:53 +0000 (03:17 +0000)
    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.

13 files changed:
gdb/ChangeLog
gdb/Makefile.in
gdb/dwarf2loc.c
gdb/eval.c
gdb/expprint.c
gdb/expression.h
gdb/parse.c
gdb/parser-defs.h
gdb/target.c
gdb/target.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.threads/tls-nodebug.c [new file with mode: 0644]
gdb/testsuite/gdb.threads/tls-nodebug.exp [new file with mode: 0644]

index 274a328a842454e89289f283deef1aad19af1cc6..c8605a9ea45ac6fd09cf012331e4d8a26c633f72 100644 (file)
@@ -1,3 +1,23 @@
+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>
 
        * solib.c (solib_open): Handle an empty solib_absolute_prefix like a
index 354fd200520c99e10c4e6c19fee52284c4f4a2f7..07892b857c4df5f3c971b258665b613b07a5f72a 100644 (file)
@@ -1982,7 +1982,7 @@ exec.o: exec.c $(defs_h) $(frame_h) $(inferior_h) $(target_h) $(gdbcmd_h) \
        $(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_h) $(gdb_string_h) $(osabi_h) \
 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) $(gdbtypes_h) $(gdbcore_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 76a99fa8560ade67824cb880b30c339d80b55da0..841fb8f2333f8378ce502971298bfc216d684aab 100644 (file)
@@ -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 d5d8969a7be045b1d22cfccf991789f6a55409f1..e1076e1efd43f2e29a2276f785a274721d10f184 100644 (file)
@@ -2019,6 +2019,21 @@ evaluate_subexp_standard (struct type *expect_type,
        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 c7633b5af1b35fd1bec7378d2149d9774577d6c0..f64c3e984ead58fa4a9e6fa9a2182ff1b6ff92b4 100644 (file)
@@ -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 *exp, int *pos,
        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 expression *exp,
       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 dd6184a171fd8540613069fbfab655b3f5089ff7..4d9205d7d8563166570d375a42f3817a8618b139 100644 (file)
@@ -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 5d949fd6e2a28c2f423208aae957df85063effe5..b0edaf426614b7f3ef980b727693ba07201581de 100644 (file)
@@ -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.  */
@@ -218,6 +219,15 @@ write_exp_elt_block (struct block *b)
   write_exp_elt (tmp);
 }
 
+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)
 {
@@ -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 *msymbol,
 
   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 expression *expr, int endpos,
       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 05061f77597ec229cc4983b952e69a43e6fbfdb3..6bfc958b8c84fb4933a3b706d46e882b0bb3f483 100644 (file)
@@ -131,6 +131,8 @@ extern void write_exp_bitstring (struct stoken);
 
 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 690e4303b51b088062a33c39df0e7f2edd5a8d49..a757f5347478ca7edbfb09c8bf195373d6ff71b9 100644 (file)
@@ -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 ffbaddc5cee7e59070603b399afcfc3001988eda..9399e76198e16141032ce0db2ee3d191ff2455fa 100644 (file)
@@ -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 ea04872fea5bdf487be3b2cb74c291ccfdb5d3cd..bbbd750eb1106732a025c14c0fd6e497ad45fd49 100644 (file)
@@ -1,3 +1,7 @@
+2006-10-09  Jan Kratochvil  <jan.kratochvil@redhat.com>
+
+       * gdb.threads/tls-nodebug.c, gdb.threads/tls-nodebug.exp: New test.
+
 2006-10-09  Daniel Jacobowitz  <dan@codesourcery.com>
 
        PR c++/2116
diff --git a/gdb/testsuite/gdb.threads/tls-nodebug.c b/gdb/testsuite/gdb.threads/tls-nodebug.c
new file mode 100644 (file)
index 0000000..73d96f0
--- /dev/null
@@ -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;
+}
diff --git a/gdb/testsuite/gdb.threads/tls-nodebug.exp b/gdb/testsuite/gdb.threads/tls-nodebug.exp
new file mode 100644 (file)
index 0000000..ee92a3f
--- /dev/null
@@ -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