1 /* Smoke testing GDB process attach with thread-local variable access.
2 Copyright (C) 2021-2023 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
19 /* This test runs GDB against a forked copy of itself, to check
20 whether libthread_db can be loaded, and that access to thread-local
30 #include <support/capture_subprocess.h>
31 #include <support/check.h>
32 #include <support/xptrace.h>
33 #include <support/subprocess.h>
34 #include <support/support.h>
35 #include <support/temp_file.h>
36 #include <support/test-driver.h>
37 #include <support/xstdio.h>
38 #include <support/xthread.h>
39 #include <support/xunistd.h>
42 /* Starts out as zero, changed to 1 or 2 by the debugger, depending on
44 __thread
volatile int altered_by_debugger
;
46 /* Common prefix between 32-bit and 64-bit ELF. */
49 unsigned char e_ident
[EI_NIDENT
];
54 _Static_assert (sizeof (struct elf_prefix
) == EI_NIDENT
+ 8,
55 "padding in struct elf_prefix");
57 /* Reads the ELF header from PATH. Returns true if the header can be
58 read, false if the file is too short. */
60 read_elf_header (const char *path
, struct elf_prefix
*elf
)
62 int fd
= xopen (path
, O_RDONLY
, 0);
63 bool result
= read (fd
, elf
, sizeof (*elf
)) == sizeof (*elf
);
68 /* Searches for "gdb" alongside the path variable. See execvpe. */
72 const char *path
= getenv ("PATH");
77 const char *colon
= strchrnul (path
, ':');
78 char *candidate
= xasprintf ("%.*s/gdb", (int) (colon
- path
), path
);
79 if (access (candidate
, X_OK
) == 0)
89 /* Writes the GDB script to run the test to PATH. */
91 write_gdbscript (const char *path
, int tested_pid
)
93 FILE *fp
= xfopen (path
, "w");
95 "set trace-commands on\n"
96 "set debug libthread-db 1\n"
97 #if DO_ADD_SYMBOL_FILE
98 /* Do not do this unconditionally to work around a GDB
99 assertion failure: ../../gdb/symtab.c:6404:
100 internal-error: CORE_ADDR get_msymbol_address(objfile*,
101 const minimal_symbol*): Assertion `(objf->flags &
102 OBJF_MAINLINE) == 0' failed. */
103 "add-symbol-file %1$s/nptl/tst-pthread-gdb-attach\n"
105 "set auto-load safe-path %1$s/nptl_db\n"
106 "set libthread-db-search-path %1$s/nptl_db\n"
108 support_objdir_root
, tested_pid
);
109 fputs ("break debugger_inspection_point\n"
112 "print altered_by_debugger\n"
113 "print altered_by_debugger = 1\n"
115 "print altered_by_debugger\n"
116 "print altered_by_debugger = 2\n"
122 /* The test sets a breakpoint on this function and alters the
123 altered_by_debugger thread-local variable. */
124 void __attribute__ ((weak
))
125 debugger_inspection_point (void)
129 /* Thread function for the test thread in the subprocess. */
131 subprocess_thread (void *closure
)
133 /* Wait until altered_by_debugger changes the value away from 0. */
134 while (altered_by_debugger
== 0)
137 debugger_inspection_point ();
140 TEST_COMPARE (altered_by_debugger
, 2);
144 /* This function implements the subprocess under test. It creates a
145 second thread, waiting for its value to change to 2, and checks
146 that the main thread also changed its value to 1. */
148 in_subprocess (void *arg
)
150 pthread_t thr
= xpthread_create (NULL
, subprocess_thread
, NULL
);
151 TEST_VERIFY (xpthread_join (thr
) == NULL
);
152 TEST_COMPARE (altered_by_debugger
, 1);
157 gdb_process (const char *gdb_path
, const char *gdbscript
, pid_t
*tested_pid
)
159 /* Create a copy of current test to check with gdb. As the
160 target_process is a child of this gdb_process, gdb is also able
161 to attach to target_process if YAMA is configured to 1 =
162 "restricted ptrace". */
163 struct support_subprocess target
= support_subprocess (in_subprocess
, NULL
);
165 write_gdbscript (gdbscript
, target
.pid
);
166 *tested_pid
= target
.pid
;
168 xdup2 (STDOUT_FILENO
, STDERR_FILENO
);
169 execl (gdb_path
, "gdb", "-nx", "-batch", "-x", gdbscript
, NULL
);
171 _exit (EXIT_UNSUPPORTED
);
179 char *gdb_path
= find_gdb ();
180 if (gdb_path
== NULL
)
181 FAIL_UNSUPPORTED ("gdb command not found in PATH: %s", getenv ("PATH"));
183 /* Check that libthread_db is compatible with the gdb architecture
184 because gdb loads it via dlopen. */
186 char *threaddb_path
= xasprintf ("%s/nptl_db/libthread_db.so",
187 support_objdir_root
);
188 struct elf_prefix elf_threaddb
;
189 TEST_VERIFY_EXIT (read_elf_header (threaddb_path
, &elf_threaddb
));
190 struct elf_prefix elf_gdb
;
191 /* If the ELF header cannot be read or "gdb" is not an ELF file,
192 assume this is a wrapper script that can run. */
193 if (read_elf_header (gdb_path
, &elf_gdb
)
194 && memcmp (&elf_gdb
, ELFMAG
, SELFMAG
) == 0)
196 if (elf_gdb
.e_ident
[EI_CLASS
] != elf_threaddb
.e_ident
[EI_CLASS
])
197 FAIL_UNSUPPORTED ("GDB at %s has wrong class", gdb_path
);
198 if (elf_gdb
.e_ident
[EI_DATA
] != elf_threaddb
.e_ident
[EI_DATA
])
199 FAIL_UNSUPPORTED ("GDB at %s has wrong data", gdb_path
);
200 if (elf_gdb
.e_machine
!= elf_threaddb
.e_machine
)
201 FAIL_UNSUPPORTED ("GDB at %s has wrong machine", gdb_path
);
203 free (threaddb_path
);
206 /* Check if our subprocess can be debugged with ptrace. */
208 int ptrace_scope
= support_ptrace_scope ();
209 if (ptrace_scope
>= 2)
210 FAIL_UNSUPPORTED ("/proc/sys/kernel/yama/ptrace_scope >= 2");
214 xclose (create_temp_file ("tst-pthread-gdb-attach-", &gdbscript
));
216 /* Run 'gdb' on test subprocess which will be created in gdb_process.
217 The pid of the subprocess will be written to 'tested_pid'. */
218 pid_t
*tested_pid
= support_shared_allocate (sizeof (pid_t
));
220 pid_t gdb_pid
= xfork ();
222 gdb_process (gdb_path
, gdbscript
, tested_pid
);
225 TEST_COMPARE (xwaitpid (gdb_pid
, &status
, 0), gdb_pid
);
226 if (WIFEXITED (status
) && WEXITSTATUS (status
) == EXIT_UNSUPPORTED
)
227 /* gdb is not installed. */
228 return EXIT_UNSUPPORTED
;
229 TEST_COMPARE (status
, 0);
231 kill (*tested_pid
, SIGKILL
);
233 support_shared_free (tested_pid
);
239 #include <support/test-driver.c>