* [PATCH] gdb: use NT_386_TLS regset to access TLS GDT entries on i386 Linux
@ 2026-09-28 17:21 Simon Marchi
2026-09-29 9:37 ` Andrew Burgess
0 siblings, 1 reply; 3+ messages in thread
From: Simon Marchi @ 2026-09-28 17:21 UTC (permalink / raw)
To: gdb-patches; +Cc: Simon Marchi
Bug 34678 reports that a 32-bit GDB running on an x86-64 kernel can't
make inferior function calls:
(gdb) p f()
Couldn't get TLS area data: Invalid argument.
This happens because GDB fails to read the special registers holding the
TLS GDT entries, added in commit 91eee81d2353 ("gdb: include NT_I386_TLS
note in generated core files", 2025-11-20).
The failure isn't specifically related to inferior function calls, but
it is most visible there when GDB attempts to save all registers prior
to a function call.
These "registers" are read using
ptrace (PTRACE_GET_THREAD_AREA, pid, addr, data)
where `addr` is not an address, but the index of a GDT entry to read.
The valid indices depend on the arch of the kernel. For an i386 kernel,
the valid range is [6, 8], while for an x86-64 kernel, the valid range
is [12, 14].
As commit 91eee81d2353 properly noted, the indices really depend on the
kernel, not on how GDB or the inferior program were built. On an x86-64
kernel, even when GDB and/or the inferior are 32-bit programs, we need
to query the x86-64 indices:
/* 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. */
However, the implementation isn't quite right, since it bases the
decision on whether GDB itself is a 32-bit or 64-bit program. So we get
it wrong when GDB is a 32-bit program, debugging a 32-bit program, on a
64-bit kernel. GDB queries the i386 indices, which gets an EINVAL
reply, because it should have used the x86-64 indices.
Also, according to Claude (I couldn't test since I don't have a machine
with x32 userland), PTRACE_GET_THREAD_AREA and PTRACE_SET_THREAD_AREA
are not supported for x32 tracers: the kernel's x32_arch_ptrace doesn't
handle them, so they would fail with EIO. An x32 GDB debugging an i386
program therefore couldn't read the TLS registers either.
Fix both problems by using the NT_386_TLS regset instead, with
PTRACE_GETREGSET and PTRACE_SETREGSET. This regset contains the three
TLS GDT entries, so accessing it doesn't require knowing their indices.
When reading, the kernel fills the entry_number field of each entry with
the right index. When writing, it ignores the entry_number fields.
This is the same data as the NT_386_TLS core file note, which these
registers are used to produce.
This also makes it possible to read the three entries with a single
ptrace call, instead of three.
Remove the i386_initial_tls_gdt constants, which are now unused, along
with nat/amd64-linux.h, which only contained one of them.
Tested by running gdb.arch/i386-tls-regs.exp on all these
configurations:
- x86-64 kernel, 64-bit GDB, native
- x86-64 kernel, 64-bit GDB, gdbserver
- x86-64 kernel, 32-bit GDB, native
- x86-64 kernel, 32-bit GDB, gdbserver
- i386 kernel, 32-bit GDB, native
- i386 kernel, 32-bit GDB, gdbserver
This test would previously fail in the "x86-64 kernel, 32-bit GDB"
configs.
This is a regression in GDB 18, so this patch would need to be
cherry-picked to the gdb-18-branch.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=34678
Change-Id: Id169e59218b632ff331402d1f04e69158b3002c9
---
gdb/Makefile.in | 1 -
gdb/nat/amd64-linux.h | 29 -----------------------------
gdb/nat/i386-linux.h | 10 ----------
gdb/nat/x86-linux.c | 28 ++++------------------------
4 files changed, 4 insertions(+), 64 deletions(-)
delete mode 100644 gdb/nat/amd64-linux.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index d1574ec2d2cb..5ce91dc70721 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1547,7 +1547,6 @@ HFILES_NO_SRCDIR = \
nat/aarch64-pauth-linux.h \
nat/aarch64-scalable-linux-ptrace.h \
nat/aarch64-scalable-linux-sigcontext.h \
- nat/amd64-linux.h \
nat/amd64-linux-siginfo.h \
nat/fork-inferior.h \
nat/gdb_ptrace.h \
diff --git a/gdb/nat/amd64-linux.h b/gdb/nat/amd64-linux.h
deleted file mode 100644
index 18427eb67b26..000000000000
--- a/gdb/nat/amd64-linux.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Native-dependent code for GNU/Linux amd64.
-
- Copyright (C) 2025-2026 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_AMD64_LINUX_H
-#define GDB_NAT_AMD64_LINUX_H
-
-/* See nat/i386-linux.h for a full description of this constant. This is
- the version used when GDB is compiled for amd64, and is running an
- executable compiled with -m32. */
-
-static inline constexpr int i386_initial_tls_gdt = 12;
-
-#endif /* GDB_NAT_AMD64_LINUX_H */
diff --git a/gdb/nat/i386-linux.h b/gdb/nat/i386-linux.h
index ec5fc3140a16..000f33cbc2c3 100644
--- a/gdb/nat/i386-linux.h
+++ b/gdb/nat/i386-linux.h
@@ -34,14 +34,4 @@
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 */
diff --git a/gdb/nat/x86-linux.c b/gdb/nat/x86-linux.c
index 16391dcde244..838ca70fb9db 100644
--- a/gdb/nat/x86-linux.c
+++ b/gdb/nat/x86-linux.c
@@ -27,12 +27,6 @@
#include "nat/gdb_ptrace.h"
#include <sys/user.h>
-#ifndef __x86_64__
-#include "nat/i386-linux.h"
-#else
-#include "nat/amd64-linux.h"
-#endif
-
/* Per-thread arch-specific data we want to keep. */
struct arch_lwp_info
@@ -198,16 +192,9 @@ i386_ptrace_get_tls_data (int pid, gdb::array_view<user_desc> buffer)
{
gdb_assert (buffer.size () == 3);
- for (int i = 0; i < 3; ++i)
- {
- void *addr = (void *) (uintptr_t) (i386_initial_tls_gdt + i);
- void *data = buffer.slice (i, 1).data ();
+ iovec iov { buffer.data (), buffer.size () * sizeof (user_desc) };
- if (ptrace (PTRACE_GET_THREAD_AREA, pid, addr, data) < 0)
- return false;
- }
-
- return true;
+ return ptrace (PTRACE_GETREGSET, pid, NT_386_TLS, &iov) == 0;
}
/* See nat/x86-linux.h. */
@@ -217,14 +204,7 @@ i386_ptrace_set_tls_data (int pid, gdb::array_view<user_desc> buffer)
{
gdb_assert (buffer.size () == 3);
- for (int i = 0; i < 3; ++i)
- {
- void *addr = (void *) (uintptr_t) (i386_initial_tls_gdt + i);
- void *data = buffer.slice (i, 1).data ();
+ iovec iov { buffer.data (), buffer.size () * sizeof (user_desc) };
- if (ptrace (PTRACE_SET_THREAD_AREA, pid, addr, data) < 0)
- return false;
- }
-
- return true;
+ return ptrace (PTRACE_SETREGSET, pid, NT_386_TLS, &iov) == 0;
}
base-commit: 421bec796fa3a06179856da870a13cf71ddf5df2
--
2.55.0
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH] gdb: use NT_386_TLS regset to access TLS GDT entries on i386 Linux
2026-09-28 17:21 [PATCH] gdb: use NT_386_TLS regset to access TLS GDT entries on i386 Linux Simon Marchi
@ 2026-09-29 9:37 ` Andrew Burgess
2026-09-29 11:38 ` Joos, Christina
0 siblings, 1 reply; 3+ messages in thread
From: Andrew Burgess @ 2026-09-29 9:37 UTC (permalink / raw)
To: Simon Marchi, gdb-patches; +Cc: Simon Marchi
Simon Marchi <simon.marchi@efficios.com> writes:
> Bug 34678 reports that a 32-bit GDB running on an x86-64 kernel can't
> make inferior function calls:
>
> (gdb) p f()
> Couldn't get TLS area data: Invalid argument.
>
> This happens because GDB fails to read the special registers holding the
> TLS GDT entries, added in commit 91eee81d2353 ("gdb: include NT_I386_TLS
> note in generated core files", 2025-11-20).
>
> The failure isn't specifically related to inferior function calls, but
> it is most visible there when GDB attempts to save all registers prior
> to a function call.
>
> These "registers" are read using
>
> ptrace (PTRACE_GET_THREAD_AREA, pid, addr, data)
>
> where `addr` is not an address, but the index of a GDT entry to read.
> The valid indices depend on the arch of the kernel. For an i386 kernel,
> the valid range is [6, 8], while for an x86-64 kernel, the valid range
> is [12, 14].
>
> As commit 91eee81d2353 properly noted, the indices really depend on the
> kernel, not on how GDB or the inferior program were built. On an x86-64
> kernel, even when GDB and/or the inferior are 32-bit programs, we need
> to query the x86-64 indices:
>
> /* 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. */
>
> However, the implementation isn't quite right, since it bases the
> decision on whether GDB itself is a 32-bit or 64-bit program. So we get
> it wrong when GDB is a 32-bit program, debugging a 32-bit program, on a
> 64-bit kernel. GDB queries the i386 indices, which gets an EINVAL
> reply, because it should have used the x86-64 indices.
>
> Also, according to Claude (I couldn't test since I don't have a machine
> with x32 userland), PTRACE_GET_THREAD_AREA and PTRACE_SET_THREAD_AREA
> are not supported for x32 tracers: the kernel's x32_arch_ptrace doesn't
> handle them, so they would fail with EIO. An x32 GDB debugging an i386
> program therefore couldn't read the TLS registers either.
>
> Fix both problems by using the NT_386_TLS regset instead, with
> PTRACE_GETREGSET and PTRACE_SETREGSET. This regset contains the three
> TLS GDT entries, so accessing it doesn't require knowing their indices.
> When reading, the kernel fills the entry_number field of each entry with
> the right index. When writing, it ignores the entry_number fields.
> This is the same data as the NT_386_TLS core file note, which these
> registers are used to produce.
>
> This also makes it possible to read the three entries with a single
> ptrace call, instead of three.
>
> Remove the i386_initial_tls_gdt constants, which are now unused, along
> with nat/amd64-linux.h, which only contained one of them.
>
> Tested by running gdb.arch/i386-tls-regs.exp on all these
> configurations:
>
> - x86-64 kernel, 64-bit GDB, native
> - x86-64 kernel, 64-bit GDB, gdbserver
> - x86-64 kernel, 32-bit GDB, native
> - x86-64 kernel, 32-bit GDB, gdbserver
> - i386 kernel, 32-bit GDB, native
> - i386 kernel, 32-bit GDB, gdbserver
>
> This test would previously fail in the "x86-64 kernel, 32-bit GDB"
> configs.
>
> This is a regression in GDB 18, so this patch would need to be
> cherry-picked to the gdb-18-branch.
Thanks for fixing this, and for the well written commit message.
Just today someone emailed me off-list reporting this problem:
https://inbox.sourceware.org/binutils/CAG_eJLe_vuR93fE6H4hENPh3=R2XE72T2tL1ZaTnY93zW2J5-Q@mail.gmail.com
which did make it to the gdb-help list via a CC on a follow up post, but
unfortunately never made it into a release blocking bug. The issue here
is that PTRACE_GET_THREAD_AREA and PTRACE_SET_THREAD_AREA don't exist
for older glibc, they were added in glibc 2.27 I believe, which is after
the glibc 2.23 the bug was reported against.
The NT_386_TLS that this patch uses instead was added in glibc 2.10, so
this patch should fix that build issue problem on older glibc too.
For both master and gdb-18-branch:
Approved-By: Andrew Burgess <aburgess@redhat.com>
Thanks,
Andrew
^ permalink raw reply [flat|nested] 3+ messages in thread* RE: [PATCH] gdb: use NT_386_TLS regset to access TLS GDT entries on i386 Linux
2026-09-29 9:37 ` Andrew Burgess
@ 2026-09-29 11:38 ` Joos, Christina
0 siblings, 0 replies; 3+ messages in thread
From: Joos, Christina @ 2026-09-29 11:38 UTC (permalink / raw)
To: Andrew Burgess, Simon Marchi, gdb-patches; +Cc: Simon Marchi
> -----Original Message-----
> From: Andrew Burgess <aburgess@redhat.com>
> Sent: Dienstag, 29. September 2026 11:38
> To: Simon Marchi <simon.marchi@efficios.com>; gdb-patches@sourceware.org
> Cc: Simon Marchi <simon.marchi@efficios.com>
> Subject: Re: [PATCH] gdb: use NT_386_TLS regset to access TLS GDT entries on
> i386 Linux
>
> Simon Marchi <simon.marchi@efficios.com> writes:
>
> > Bug 34678 reports that a 32-bit GDB running on an x86-64 kernel can't
> > make inferior function calls:
> >
> > (gdb) p f()
> > Couldn't get TLS area data: Invalid argument.
> >
> > This happens because GDB fails to read the special registers holding
> > the TLS GDT entries, added in commit 91eee81d2353 ("gdb: include
> > NT_I386_TLS note in generated core files", 2025-11-20).
> >
> > The failure isn't specifically related to inferior function calls, but
> > it is most visible there when GDB attempts to save all registers prior
> > to a function call.
> >
> > These "registers" are read using
> >
> > ptrace (PTRACE_GET_THREAD_AREA, pid, addr, data)
> >
> > where `addr` is not an address, but the index of a GDT entry to read.
> > The valid indices depend on the arch of the kernel. For an i386
> > kernel, the valid range is [6, 8], while for an x86-64 kernel, the
> > valid range is [12, 14].
> >
> > As commit 91eee81d2353 properly noted, the indices really depend on
> > the kernel, not on how GDB or the inferior program were built. On an
> > x86-64 kernel, even when GDB and/or the inferior are 32-bit programs,
> > we need to query the x86-64 indices:
> >
> > /* 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. */
> >
> > However, the implementation isn't quite right, since it bases the
> > decision on whether GDB itself is a 32-bit or 64-bit program. So we
> > get it wrong when GDB is a 32-bit program, debugging a 32-bit program,
> > on a 64-bit kernel. GDB queries the i386 indices, which gets an
> > EINVAL reply, because it should have used the x86-64 indices.
> >
> > Also, according to Claude (I couldn't test since I don't have a
> > machine with x32 userland), PTRACE_GET_THREAD_AREA and
> > PTRACE_SET_THREAD_AREA are not supported for x32 tracers: the kernel's
> > x32_arch_ptrace doesn't handle them, so they would fail with EIO. An
> > x32 GDB debugging an i386 program therefore couldn't read the TLS registers
> either.
> >
> > Fix both problems by using the NT_386_TLS regset instead, with
> > PTRACE_GETREGSET and PTRACE_SETREGSET. This regset contains the three
> > TLS GDT entries, so accessing it doesn't require knowing their indices.
> > When reading, the kernel fills the entry_number field of each entry
> > with the right index. When writing, it ignores the entry_number fields.
> > This is the same data as the NT_386_TLS core file note, which these
> > registers are used to produce.
> >
> > This also makes it possible to read the three entries with a single
> > ptrace call, instead of three.
> >
> > Remove the i386_initial_tls_gdt constants, which are now unused, along
> > with nat/amd64-linux.h, which only contained one of them.
> >
> > Tested by running gdb.arch/i386-tls-regs.exp on all these
> > configurations:
> >
> > - x86-64 kernel, 64-bit GDB, native
> > - x86-64 kernel, 64-bit GDB, gdbserver
> > - x86-64 kernel, 32-bit GDB, native
> > - x86-64 kernel, 32-bit GDB, gdbserver
> > - i386 kernel, 32-bit GDB, native
> > - i386 kernel, 32-bit GDB, gdbserver
> >
> > This test would previously fail in the "x86-64 kernel, 32-bit GDB"
> > configs.
> >
> > This is a regression in GDB 18, so this patch would need to be
> > cherry-picked to the gdb-18-branch.
>
> Thanks for fixing this, and for the well written commit message.
>
> Just today someone emailed me off-list reporting this problem:
>
>
> https://inbox.sourceware.org/binutils/CAG_eJLe_vuR93fE6H4hENPh3=R2XE72
> T2tL1ZaTnY93zW2J5-Q@mail.gmail.com
>
> which did make it to the gdb-help list via a CC on a follow up post, but
> unfortunately never made it into a release blocking bug. The issue here is that
> PTRACE_GET_THREAD_AREA and PTRACE_SET_THREAD_AREA don't exist for
> older glibc, they were added in glibc 2.27 I believe, which is after the glibc 2.23
> the bug was reported against.
>
> The NT_386_TLS that this patch uses instead was added in glibc 2.10, so this
> patch should fix that build issue problem on older glibc too.
>
> For both master and gdb-18-branch:
>
> Approved-By: Andrew Burgess <aburgess@redhat.com>
>
> Thanks,
> Andrew
I plan to look at this in a few hours, but since Andrew has already approved it, it's of course fine with me if you go ahead.
This is probably a bit urgent.
Since x32 is mentioned here, just FYI, in case you haven't seen it:
https://www.phoronix.com/news/Linux-x32-ABI-2026
Christina
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-09-29 11:39 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-28 17:21 [PATCH] gdb: use NT_386_TLS regset to access TLS GDT entries on i386 Linux Simon Marchi
2026-09-29 9:37 ` Andrew Burgess
2026-09-29 11:38 ` Joos, Christina
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox