860497a
commit 36783141cf090412e3e6f042f25f7f6c63d6a14a
860497a
Author: Florian Weimer <fweimer@redhat.com>
860497a
Date:   Thu Apr 22 11:07:43 2021 +0200
860497a
860497a
    nptl: Check for compatible GDB in nptl/tst-pthread-gdb-attach
860497a
    
860497a
    Also do not clear the subprocess environment, in case running
860497a
    GDB needs certain environment variables.
860497a
    
860497a
    (cherry picked from commit f553dc066071a4465321fbc122bed8a75afd996b)
860497a
860497a
diff --git a/nptl/tst-pthread-gdb-attach.c b/nptl/tst-pthread-gdb-attach.c
860497a
index 0603ad844defb8de..901a12003426d342 100644
860497a
--- a/nptl/tst-pthread-gdb-attach.c
860497a
+++ b/nptl/tst-pthread-gdb-attach.c
860497a
@@ -20,8 +20,12 @@
860497a
    whether libthread_db can be loaded, and that access to thread-local
860497a
    variables works.  */
860497a
 
860497a
+#include <elf.h>
860497a
 #include <errno.h>
860497a
+#include <fcntl.h>
860497a
+#include <stdbool.h>
860497a
 #include <stdlib.h>
860497a
+#include <string.h>
860497a
 #include <support/check.h>
860497a
 #include <support/support.h>
860497a
 #include <support/temp_file.h>
860497a
@@ -35,6 +39,49 @@
860497a
    the thread.  */
860497a
 __thread volatile int altered_by_debugger;
860497a
 
860497a
+/* Common prefix between 32-bit and 64-bit ELF.  */
860497a
+struct elf_prefix
860497a
+{
860497a
+  unsigned char e_ident[EI_NIDENT];
860497a
+  uint16_t e_type;
860497a
+  uint16_t e_machine;
860497a
+  uint32_t e_version;
860497a
+};
860497a
+_Static_assert (sizeof (struct elf_prefix) == EI_NIDENT + 8,
860497a
+                "padding in struct elf_prefix");
860497a
+
860497a
+/* Reads the ELF header from PATH.  Returns true if the header can be
860497a
+   read, false if the file is too short.  */
860497a
+static bool
860497a
+read_elf_header (const char *path, struct elf_prefix *elf)
860497a
+{
860497a
+  int fd = xopen (path, O_RDONLY, 0);
860497a
+  bool result = read (fd, elf, sizeof (*elf)) == sizeof (*elf);
860497a
+  xclose (fd);
860497a
+  return result;
860497a
+}
860497a
+
860497a
+/* Searches for "gdb" alongside the path variable.  See execvpe.  */
860497a
+static char *
860497a
+find_gdb (void)
860497a
+{
860497a
+  const char *path = getenv ("PATH");
860497a
+  if (path == NULL)
860497a
+    return NULL;
860497a
+  while (true)
860497a
+    {
860497a
+      const char *colon = strchrnul (path, ':');
860497a
+      char *candidate = xasprintf ("%.*s/gdb", (int) (colon - path), path);
860497a
+      if (access (candidate, X_OK) == 0)
860497a
+        return candidate;
860497a
+      free (candidate);
860497a
+      if (*colon == '\0')
860497a
+        break;
860497a
+      path = colon + 1;
860497a
+    }
860497a
+  return NULL;
860497a
+}
860497a
+
860497a
 /* Writes the GDB script to run the test to PATH.  */
860497a
 static void
860497a
 write_gdbscript (const char *path, int tested_pid)
860497a
@@ -105,6 +152,33 @@ in_subprocess (void)
860497a
 static int
860497a
 do_test (void)
860497a
 {
860497a
+  char *gdb_path = find_gdb ();
860497a
+  if (gdb_path == NULL)
860497a
+    FAIL_UNSUPPORTED ("gdb command not found in PATH: %s", getenv ("PATH"));
860497a
+
860497a
+  /* Check that libthread_db is compatible with the gdb architecture
860497a
+     because gdb loads it via dlopen.  */
860497a
+  {
860497a
+    char *threaddb_path = xasprintf ("%s/nptl_db/libthread_db.so",
860497a
+                                     support_objdir_root);
860497a
+    struct elf_prefix elf_threaddb;
860497a
+    TEST_VERIFY_EXIT (read_elf_header (threaddb_path, &elf_threaddb));
860497a
+    struct elf_prefix elf_gdb;
860497a
+    /* If the ELF header cannot be read or "gdb" is not an ELF file,
860497a
+       assume this is a wrapper script that can run.  */
860497a
+    if (read_elf_header (gdb_path, &elf_gdb)
860497a
+        && memcmp (&elf_gdb, ELFMAG, SELFMAG) == 0)
860497a
+      {
860497a
+        if (elf_gdb.e_ident[EI_CLASS] != elf_threaddb.e_ident[EI_CLASS])
860497a
+          FAIL_UNSUPPORTED ("GDB at %s has wrong class", gdb_path);
860497a
+        if (elf_gdb.e_ident[EI_DATA] != elf_threaddb.e_ident[EI_DATA])
860497a
+          FAIL_UNSUPPORTED ("GDB at %s has wrong data", gdb_path);
860497a
+        if (elf_gdb.e_machine != elf_threaddb.e_machine)
860497a
+          FAIL_UNSUPPORTED ("GDB at %s has wrong machine", gdb_path);
860497a
+      }
860497a
+    free (threaddb_path);
860497a
+  }
860497a
+
860497a
   pid_t tested_pid = xfork ();
860497a
   if (tested_pid == 0)
860497a
     in_subprocess ();
860497a
@@ -117,9 +191,8 @@ do_test (void)
860497a
   pid_t gdb_pid = xfork ();
860497a
   if (gdb_pid == 0)
860497a
     {
860497a
-      clearenv ();
860497a
       xdup2 (STDOUT_FILENO, STDERR_FILENO);
860497a
-      execlp ("gdb", "gdb", "-nx", "-batch", "-x", gdbscript, NULL);
860497a
+      execl (gdb_path, "gdb", "-nx", "-batch", "-x", gdbscript, NULL);
860497a
       if (errno == ENOENT)
860497a
         _exit (EXIT_UNSUPPORTED);
860497a
       else
860497a
@@ -137,6 +210,7 @@ do_test (void)
860497a
 
860497a
   free (tested_pid_string);
860497a
   free (gdbscript);
860497a
+  free (gdb_path);
860497a
   return 0;
860497a
 }
860497a