mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-12-05 23:23:09 +00:00
This commit extends GDB for x86/Linux to include the NT_I386_TLS note
in generated core files (i.e. created with `generate-core-file` or
`gcore` command). This note contains the 3 per-thread TLS related
GDT (global descriptor table) entries, and is present for i386
binaries, or those compiled on x86-64 with -m32.
The approach I have taken to achieve this, is to make the 3 GDT
entries available within 3 new registers. I added these registers to
the org.gnu.gdb.i386.linux target description feature, as this feature
seemed perfectly named. As the new registers are optional I don't see
any harm in extending this existing feature. I did consider adding a
new feature with `tls` in the name, but this seemed excessive given
the existing feature.
Which GDT entries are used for TLS varies between i386 and x86-64
running in 32-bit mode. As such the registers are named with suffixes
0, 1, and 2, and it is left to GDB or gdbserver, to find the correct
GDT entries (based on the precise target) and place the contents into
these registers.
With this done, adding the relevant regset is sufficient to get the
tls contents emitted as a core file note. Support for emitting the
note into the generated core file relies on some BFD changes which
were made in an earlier commit:
commit ea6ec00ff4
Date: Fri Jul 25 19:51:58 2025 +0100
bfd: support for NT_386_TLS notes
The three new registers are readable and writable. Writing to one of
the new registers will update the relevant kernel GDT entry.
Each TLS GDT is represented by a 'struct user_desc' (see 'man 2
get_thread_area' for details), the first 4 bytes of each 'user_desc'
is the 'entry_number' field, this is the index of the GDT within the
kernel, and cannot be modified. Attempts to write to this region of
the register will be ignored, but will not give an error.
I did consider not including this part of the user_desc within the
register value, but this becomes difficult when we consider remote
targets, GDB would then need to figure out what these indexes were so
that the core file note could be generated. Sure, we probably could
figure the correct index values out, but I figure, why bother, we can
just pass them through in the register and know for certain that we
have the correct values.
For testing, there's a new test that covers the basic functionality,
including read/write access to the new registers, and checking that
the NT_386_TLS note is added to the core file, and that the note
contents can be read by GDB.
I also manually tested opening a core file generated from an old
GDB (so no NT_386_TLS notes) using a GDB with this patch. This works
fine, the new tls registers are not created as the NT_GDB_TDESC
note (the target description) doesn't include the new registers.
Out of interest I also patched an old version of GDB to avoid creating
the NT_GDB_TDESC, and created a core file. This core file contained
neither the NT_386_TLS nor NT_GDB_TDESC. When opening this core file
with a patched GDB, the new registers do show up, but their contents
are given as <unavailable>, which is exactly what we'd expect, GDB
builds a target description based on the architecture, the
architecture says these registers should exist, but they are missing
from the core file, hence, <unavailable>.
I also tested using a patched GDB with an old version of gdbserver,
the new registers don't show up as the old gdbserver doesn't send them
in its target description. And a core file created using the gcore
command in such a setup leaves no NT_386_TLS notes added, which is
what we'd expect.
And I also tested a new gdbserver running with an old version of GDB.
As the new tls registers are now mentioned in the target description,
then obviously, the old GDB does see the registers, and present them
to the user, however GDB doesn't know how to use these registers to
create a NT_386_TLS, so that note isn't added to any core files.
Also, while a new GDB places the tls registers into the 'system'
group, an old GDB doesn't do this, so the registers end up in the
'general' group by default. This means they show up within 'info
registers' output. This isn't ideal, but there's not much that can be
done about this.
Overall, I feel the combinations of old and new tools has been tested,
and the behaviours are what we'd want or expect.
I'm tagging this commit with PR gdb/15591, even though this patch
isn't directly related. That bug is for improving GDB's testing of
TLS support in core files. The test in this commit does do some very
simple reading of a TLS variable, but there's only two threads, and
one TLS variable, so it's not extensive. Additionally, the test in
this commit is x86 only, so this should not be considered a full
resolution to that bug. But still, it's something.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=15591
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Reviewed-By: Christina Schimpe <christina.schimpe@intel.com>
Reviewed-By: Keith Seitz <keiths@redhat.com>
48 lines
2.0 KiB
C
48 lines
2.0 KiB
C
/* Native-dependent code for GNU/Linux i386.
|
|
|
|
Copyright (C) 2024-2025 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#ifndef GDB_NAT_I386_LINUX_H
|
|
#define GDB_NAT_I386_LINUX_H
|
|
|
|
/* Does the current host support the GETFPXREGS request? The system header
|
|
file may or may not define it, but even if it is defined, the kernel
|
|
will return EIO if it's running on a pre-SSE processor.
|
|
|
|
Initially this will be TRIBOOL_UNKNOWN and should be changed to
|
|
TRIBOOL_FALSE if the ptrace call is attempted and fails or changed to
|
|
TRIBOOL_TRUE if the ptrace call is attempted and succeeds.
|
|
|
|
My instinct is to attach this to some architecture- or target-specific
|
|
data structure, but really, a particular GDB process can only run on top
|
|
of one kernel at a time. So it's okay - for this to be a global
|
|
variable. */
|
|
extern tribool have_ptrace_getfpxregs;
|
|
|
|
/* This constant defines the first GDT (Global Descriptor Table) entry
|
|
that the kernel allocates for holding TLS descriptors. There are three
|
|
entries, starting at this index which can be accessed using the
|
|
PTRACE_GET_THREAD_AREA and PTRACE_SET_THREAD_AREA ptrace calls. This
|
|
constant is only valid for true i386 kernels. For amd64 kernels
|
|
running in 32-bit mode (i.e. executables compiled -m32) there is a
|
|
different constant, see nat/amd64-linux.h. */
|
|
|
|
static inline constexpr int i386_initial_tls_gdt = 6;
|
|
|
|
#endif /* GDB_NAT_I386_LINUX_H */
|