[glibc] nptl: Skip tst-pthread-gdb-attach{, -static} on env mismatches

Adhemerval Zanella via Glibc-cvs <[email protected]> Mon, 25 May 2026 15:43:56 +0000 (GMT)
Newsgroups gmane.comp.lib.glibc.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=8eeed69baffbc57c955f3e0ba5ac5514d9fe72ac

commit 8eeed69baffbc57c955f3e0ba5ac5514d9fe72ac
Author: Adhemerval Zanella <[email protected]>
Date:   Thu May 21 13:30:47 2026 -0300

    nptl: Skip tst-pthread-gdb-attach{, -static} on env mismatches
    
    The test previously failed with confusing diagnostics in two situations
    that are properties of the runtime environment rather than of glibc:
    
    1. find_gdb only checked access(X_OK), which is true for directories
       too.  A 'gdb' directory ahead of /usr/bin in PATH (e.g. one holding
       gdb python helpers) was therefore returned as the gdb executable,
       and the subsequent execl failed with errno != ENOENT, causing the
       test to fail with `numeric comparison failure ... status 256'.
    
    2. The in-tree libthread_db.so.1 is built with -z mark-plt and
       therefore carries a versioned dependency on
       GLIBC_ABI_DT_X86_64_PLT in libc.so (see BZ #33212).  When the
       system gdb is linked against a libc older than 2.41, that version
       is not provided and gdb's dlopen of the in-tree libthread_db.so.1
       fails.  Thread debugging is then disabled, the gdb script's
       `thread 1' / `thread 2' commands fail, gdb exits non-zero, and the
       test reports a generic status mismatch.
    
    Two changes:
    
    * Require S_ISREG in find_gdb so a directory named 'gdb' on PATH is
      skipped, falling through to the next candidate.
    
    * Before running the real gdb scenario, run a minimal probe script
      that triggers libthread_db loading (set debug libthread-db 1; set
      libthread-db-search-path; file /proc/self/exe; start).  If the
      probe output contains `dlopen failed', mark the test UNSUPPORTED
      with a clear message instead of letting the real run fail.
    Reviewed-by: Carlos O'Donell <[email protected]>

Diff:
---
 nptl/tst-pthread-gdb-attach.c | 62 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 61 insertions(+), 1 deletion(-)

diff --git a/nptl/tst-pthread-gdb-attach.c b/nptl/tst-pthread-gdb-attach.c
index b183dc0f30..c737188a6c 100644
--- a/nptl/tst-pthread-gdb-attach.c
+++ b/nptl/tst-pthread-gdb-attach.c
@@ -25,6 +25,7 @@
 #include <fcntl.h>
 #include <signal.h>
 #include <stdbool.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <support/capture_subprocess.h>
@@ -37,6 +38,7 @@
 #include <support/xstdio.h>
 #include <support/xthread.h>
 #include <support/xunistd.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
 /* Starts out as zero, changed to 1 or 2 by the debugger, depending on
@@ -65,6 +67,51 @@ read_elf_header (const char *path, struct elf_prefix *elf)
   return result;
 }
 
+/* Runs gdb with a minimal probe script and returns true if gdb prints
+   'dlopen failed' while attempting to load libthread_db.  */
+static bool
+libthread_db_probe_fails (const char *gdb_path)
+{
+  support_need_proc ("Reads /proc/self/exec to probe gdb");
+
+  /* The probe inferior just needs to be any pthread-linked binary; reusing
+     /proc/self/exe avoids hard-coding a path.  */
+  char *self = xreadlink ("/proc/self/exe");
+
+  char *probe_script;
+  xclose (create_temp_file ("tst-pthread-gdb-attach-probe-", &probe_script));
+  FILE *fp = xfopen (probe_script, "w");
+  fprintf (fp,
+           "set debuginfod enabled off\n"
+           "set debug libthread-db 1\n"
+           "set auto-load safe-path %1$s/nptl_db\n"
+           "set libthread-db-search-path %1$s/nptl_db\n"
+           "file %2$s\n"
+           "start\n",
+           support_objdir_root, self);
+  xfclose (fp);
+  free (self);
+
+  char *cmd = xasprintf ("%s -nx -batch -x %s 2>&1",
+                         gdb_path, probe_script);
+  FILE *gdb_out = popen (cmd, "r");
+  free (cmd);
+
+  bool fails = false;
+  if (gdb_out != NULL)
+    {
+      char *line = NULL;
+      size_t linecap = 0;
+      while (xgetline (&line, &linecap, gdb_out) > 0)
+        if (strstr (line, "dlopen failed") != NULL)
+          fails = true;
+      free (line);
+      pclose (gdb_out);
+    }
+  free (probe_script);
+  return fails;
+}
+
 /* Searches for "gdb" alongside the path variable.  See execvpe.  */
 static char *
 find_gdb (void)
@@ -76,7 +123,10 @@ find_gdb (void)
     {
       const char *colon = strchrnul (path, ':');
       char *candidate = xasprintf ("%.*s/gdb", (int) (colon - path), path);
-      if (access (candidate, X_OK) == 0)
+      struct stat st;
+      if (access (candidate, X_OK) == 0
+          && stat (candidate, &st) == 0
+          && S_ISREG (st.st_mode))
         return candidate;
       free (candidate);
       if (*colon == '\0')
@@ -199,10 +249,20 @@ do_test (void)
           FAIL_UNSUPPORTED ("GDB at %s has wrong data", gdb_path);
         if (elf_gdb.e_machine != elf_threaddb.e_machine)
           FAIL_UNSUPPORTED ("GDB at %s has wrong machine", gdb_path);
+
       }
     free (threaddb_path);
   }
 
+  /* Probe gdb with a minimal script that triggers libthread_db loading.  If
+     the probe reports a dlopen failure (e.g. because the libc gdb is linked
+     against is older than the one in-tree libthread_db.so.1 requires, see
+     BZ #33212), skip the test as UNSUPPORTED.  */
+  if (libthread_db_probe_fails (gdb_path))
+    FAIL_UNSUPPORTED ("gdb cannot dlopen the in-tree libthread_db.so.1;"
+                      " its libc is missing a required ABI version"
+                      " (see BZ #33212)");
+
   /* Check if our subprocess can be debugged with ptrace.  */
   {
     int ptrace_scope = support_ptrace_scope ();