]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Don't attempt to find TLS address when target has no registers
authorKevin Buettner <kevinb@redhat.com>
Thu, 24 Apr 2025 04:39:28 +0000 (21:39 -0700)
committerKevin Buettner <kevinb@redhat.com>
Thu, 24 Apr 2025 16:54:42 +0000 (09:54 -0700)
This commit fixes two bugs, one of which is Bug 25807, which occurs
when target_translate_tls_address() is called from
language_defn::read_var_value in findvar.c.  I found it while testing on
aarch64; it turned a KFAIL for gdb.threads/tls.exp: print a_thread_local
into a FAIL due to a GDB internal error.  Now, with this commit in place,
the KFAIL/FAIL turns into a PASS.

In addition to the existing test just noted, I've also added a test to
the new test case gdb.base/tls-nothreads.exp.  It'll be tested, using
different scenarios, up to 8 times:

PASS: gdb.base/tls-nothreads.exp: default: force_internal_tls=false: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: default: force_internal_tls=true: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: static: force_internal_tls=false: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: static: force_internal_tls=true: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads: force_internal_tls=false: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads: force_internal_tls=true: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads-static: force_internal_tls=false: after exit: print tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads-static: force_internal_tls=true: after exit: print tls_tbss_1

There is a related problem that occurs when target_translate_tls_address
is called from find_minsym_type_and_address() in minsyms.c.  It can be
observed when debugging a program without debugging symbols when the
program is not executing.  I've written a new test for this, but it's
(also) included in the new test case gdb.base/tls-nothreads.exp, found
later in this series.  Depending on the target, it can run up to 8
times using different scenarios.  E.g., on aarch64, I'm seeing these
PASSes, all of which test this change:

PASS: gdb.base/tls-nothreads.exp: default: force_internal_tls=false: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: default: force_internal_tls=true: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: static: force_internal_tls=false: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: static: force_internal_tls=true: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads: force_internal_tls=false: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads: force_internal_tls=true: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads-static: force_internal_tls=false: stripped: after exit: print (int) tls_tbss_1
PASS: gdb.base/tls-nothreads.exp: pthreads-static: force_internal_tls=true: stripped: after exit: print (int) tls_tbss_1

In an earlier version of this commit (v4), I was checking whether the
target has registers in language_defn::read_var_value in findvar.c and
in find_minsym_type_and_address in minsyms.c, printing suitable error
messages in each case.  In his review of this commit for the v4
series, Tom Tromey asked whether it would be better to do this check
in target_translate_tls_address.  I had considered doing that for the
v4 (and earlier) series, but I wanted to print slightly different
messages at each check.  Also, read_var_value in findvar.c was already
printing a message in some cases and I had arranged for the later
check in that function to match the original message.

However, while I had added a target-has-registers check at two of the
call sites for target_translate_tls_address, I hadn't added it at the
third call site which is in dwarf_expr_context::execute_stack_op() in
dwarf2/expr.c.  I believe that in most cases, this is handled by the
early check in language_defn::read_var_value...

  else if (sym_need == SYMBOL_NEEDS_REGISTERS && !target_has_registers ())
    error (_("Cannot read `%s' without registers"), var->print_name ());

...but it's entirely possible that dwarf_expr_context::execute_stack_op()
might get called in some other context.  So it makes sense to do the
target-has-registers check for that case too.  And rather than add yet
another check at that call site, I decided that moving the check and
error message to target_translate_tls_address makes sense.

I had to make the error messages that it prints somewhat more generic.
In particular, when called from language_defn::read_var_value, the
message printed by target_translate_tls_address no longer matches the
earlier message that could be printed (as shown above).  That meant
that the test cases which check for this message, gdb.threads/tls.exp,
and gdb.base/tls-nothreads.exp had to be adjusted to account for the
new message.  Also, I think it's valuable to the user to know (if
possible) the name of the variable that caused the error, so I've
added an optional parameter to target_translate_tls_address, providing
the name of the variable, if it's known.  Therefore, the message
that's printed when the target-has-registers test fails is one of the
following:

When the TLS variable isn't known (due to being called from
dwarf_expr_context::execute_stack_op):

    "Cannot translate TLS address without registers"

When the TLS variable is known (from either of the other two call sites
for target_translate_tls_address):

    "Cannot find address of TLS symbol `%s' without registers"

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=25807
Tested-By: Luis Machado <luis.machado@arm.com>
Approved-By: Luis Machado <luis.machado@arm.com>
gdb/findvar.c
gdb/minsyms.c
gdb/target.c
gdb/target.h
gdb/testsuite/gdb.threads/tls.exp

index 29389313f943daac6ff00c0c6d8a974551744f72..9da5c4831a6ffe23b5f75507efc6f0f673712e2f 100644 (file)
@@ -485,7 +485,8 @@ language_defn::read_var_value (struct symbol *var,
        /* Determine address of TLS variable. */
        if (obj_section
            && (obj_section->the_bfd_section->flags & SEC_THREAD_LOCAL) != 0)
-         addr = target_translate_tls_address (obj_section->objfile, addr);
+         addr = target_translate_tls_address (obj_section->objfile, addr,
+                                              var->print_name ());
       }
       break;
 
index 649a9f17c3e0ed70869fe6ea680ac09dea41375a..9ac3145cb629de27ecc1e3021466f7fc47c0e4e7 100644 (file)
@@ -1688,7 +1688,8 @@ find_minsym_type_and_address (minimal_symbol *msymbol,
     {
       /* Skip translation if caller does not need the address.  */
       if (address_p != NULL)
-       *address_p = target_translate_tls_address (objfile, addr);
+       *address_p = target_translate_tls_address
+                      (objfile, addr, bound_msym.minsym->print_name ());
       return builtin_type (objfile)->nodebug_tls_symbol;
     }
 
index 4a1964e664b3b34b36d4787d6ddf8f16c2f91bab..522bed8e939cc6bc098b3607b117017ee48540d2 100644 (file)
@@ -1250,11 +1250,21 @@ generic_tls_error (void)
               _("Cannot find thread-local variables on this target"));
 }
 
-/* Using the objfile specified in OBJFILE, find the address for the
-   current thread's thread-local storage with offset OFFSET.  */
+/* See target.h.  */
+
 CORE_ADDR
-target_translate_tls_address (struct objfile *objfile, CORE_ADDR offset)
+target_translate_tls_address (struct objfile *objfile, CORE_ADDR offset,
+                             const char *name)
 {
+  if (!target_has_registers ())
+    {
+      if (name == nullptr)
+       error (_("Cannot translate TLS address without registers"));
+      else
+       error (_("Cannot find address of TLS symbol `%s' without registers"),
+              name);
+    }
+
   volatile CORE_ADDR addr = 0;
   struct target_ops *target = current_inferior ()->top_target ();
   gdbarch *gdbarch = current_inferior ()->arch ();
index 004494dc3c484a2ba46f65206e21e1687098ddbb..2d3bac77bd2af53b689c785a66da15c01692b43a 100644 (file)
@@ -2472,8 +2472,14 @@ extern void target_pre_inferior ();
 
 extern void target_preopen (int);
 
+/* Using the objfile specified in OBJFILE, find the address for the
+   current thread's thread-local storage with offset OFFSET.  If it's
+   provided, NAME might be used to indicate the relevant variable
+   in an error message.  */
+
 extern CORE_ADDR target_translate_tls_address (struct objfile *objfile,
-                                              CORE_ADDR offset);
+                                              CORE_ADDR offset,
+                                              const char *name = nullptr);
 
 /* Return the "section" containing the specified address.  */
 const struct target_section *target_section_by_addr (struct target_ops *target,
index 1bc5df26233cf554eee2d77dce401654a43c15c4..73fada7bcd18617472cb4ee235fe187ecd76b564 100644 (file)
@@ -159,7 +159,7 @@ gdb_test_multiple "print a_thread_local" "" {
     -re -wrap "Cannot find thread-local variables on this target" {
        kfail "gdb/25807" $gdb_test_name
     }
-    -re -wrap "Cannot read .a_thread_local. without registers" {
+    -re -wrap "Cannot (?:read|find address of TLS symbol) .a_thread_local. without registers" {
        pass $gdb_test_name
     }
 }