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