From: Aditya Vidyadhar Kamath <aditya.kamath1@ibm.com>
This patch adds test cases to all the 4 types of TLS variables in a shared library
on AIX.
which is
1: local-exec
2: initial-exec
3: global-dynamic
4: local-dynamic
le_var1 and le_var2 are local-exec in the main executable.
gd_var1 and gd_var2 are initial exec if the compiler chose R_TLS_IE via compiler options otherwise is local dynamic.
gd_var1, gd_var2 are local dynamic.
ld_only_var is local dynamic since it is used in the shared library only and not in main executable.
[See: powerpc-aix-tls-main.c]
This test prints all of them and checks if we can access them in AIX.
When we run this on AIX we get,
gmake check RUNTESTFLAGS='gdb.threads/thread_events.exp CC_FOR_TARGET="/opt/freeware/bin/gcc" CXX_FOR_TARGET="/opt/freeware/bin/g++" CXXFLAGS_FOR_TARGET="-O0 -w -g -gdwarf -maix64" CFLAGS_FOR_TARGET="-O0 -w -g -gdwarf -maix64"'
Running target unix
Using /opt/freeware/share/dejagnu/baseboards/unix.exp as board description file for target.
Using /opt/freeware/share/dejagnu/config/unix.exp as generic interface file for target.
Using /current_gdb/binutils-gdb/gdb/testsuite/config/unix.exp as tool-and-target-specific interface file.
Running /current_gdb/binutils-gdb/gdb/testsuite/gdb.threads/thread_events.exp ...
=== gdb Summary ===
# of expected passes 11
/current_gdb/binutils-gdb/gdb/gdb version 19.0.50.20260819-git -nw -nx -q -iex "set height 0" -iex "set width 0" -data-directory /current_gdb/binutils-gdb/gdb/data-directory -iex "set interactive-mode on"
---
gdb/testsuite/gdb.arch/powerpc-aix-tls-lib.c | 66 ++++++++
gdb/testsuite/gdb.arch/powerpc-aix-tls-main.c | 69 ++++++++
gdb/testsuite/gdb.arch/powerpc-aix-tls.exp | 147 ++++++++++++++++++
3 files changed, 282 insertions(+)
create mode 100644 gdb/testsuite/gdb.arch/powerpc-aix-tls-lib.c
create mode 100644 gdb/testsuite/gdb.arch/powerpc-aix-tls-main.c
create mode 100644 gdb/testsuite/gdb.arch/powerpc-aix-tls.exp
diff --git a/gdb/testsuite/gdb.arch/powerpc-aix-tls-lib.c b/gdb/testsuite/gdb.arch/powerpc-aix-tls-lib.c
new file mode 100644
index 00000000000..3f34d2e570b
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/powerpc-aix-tls-lib.c
@@ -0,0 +1,66 @@
+/* Copyright (C) 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 <https://urldefense.proofpoint.com/v2/url?u=http-3A__www.gnu.org_licenses_&d=DwIDAg&c=BSDicqBQBDjDI9RkVyTcHQ&r=f-oUQ8ByG1nZ71OI9p76qywCPh7mxzU69hBYnkP9Nis&m=66_Kp84mmQEKNZ7tKE-MfzLaqfgXmgYhLjZwTzzdWoOPrQlE1jHOFQhT8b6nDb4a&s=qdq2vvKujGmQ9cWtAL7q8LKQY1jPVN2ll2n_X9kaJYA&e=
>. */
+
+/* Global-dynamic TLS variables exported from this shared library.
+ The main executable accesses these (R_TLS + R_TLSM relocs), which means
+ the TOC slots will be emitted in the main executable's .loader section.
+ Within this library the compiler may use local-dynamic or initial-exec
+ depending on flags, but the main exe always uses global-dynamic for
+ cross-module symbols. */
+
+/* gd_var1 and gd_var2 are exported TLS variables that the main executable
+ imports via global-dynamic. They are also accessed here via local-dynamic
+ so that R_TLS_LD + R_TLSML relocs appear in this library's .loader. */
+__thread int gd_var1 = 10;
+__thread int gd_var2 = 20;
+
+/* ld_only_var is a local-dynamic variable that is ONLY accessed from within
+ this shared library (never referenced in the main executable). This
+ exercises the access-dependency limitation: GDB must be able to resolve it
+ even though there is no R_TLS/R_TLSM slot for it in the main exe's loader
+ section. */
+__thread int ld_only_var = 30;
+
+/* Access gd_var1, gd_var2 and ld_only_var from within the library so that
+ the compiler emits R_TLS_LD / R_TLSML relocs into this .so's .loader
+ section. Force local-dynamic model explicitly. */
+int
+lib_set_tls_vars (int a, int b, int c)
+{
+ gd_var1 = a;
+ gd_var2 = b;
+ ld_only_var = c;
+ return gd_var1 + gd_var2 + ld_only_var;
+}
+
+int
+lib_get_gd_var1 (void)
+{
+ return gd_var1;
+}
+
+int
+lib_get_gd_var2 (void)
+{
+ return gd_var2;
+}
+
+int
+lib_get_ld_only (void)
+{
+ return ld_only_var;
+}
diff --git a/gdb/testsuite/gdb.arch/powerpc-aix-tls-main.c b/gdb/testsuite/gdb.arch/powerpc-aix-tls-main.c
new file mode 100644
index 00000000000..1a8a5561e71
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/powerpc-aix-tls-main.c
@@ -0,0 +1,69 @@
+/* Copyright (C) 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 <https://urldefense.proofpoint.com/v2/url?u=http-3A__www.gnu.org_licenses_&d=DwIDAg&c=BSDicqBQBDjDI9RkVyTcHQ&r=f-oUQ8ByG1nZ71OI9p76qywCPh7mxzU69hBYnkP9Nis&m=66_Kp84mmQEKNZ7tKE-MfzLaqfgXmgYhLjZwTzzdWoOPrQlE1jHOFQhT8b6nDb4a&s=qdq2vvKujGmQ9cWtAL7q8LKQY1jPVN2ll2n_X9kaJYA&e=
>. */
+
+/* This test exercises all four XCOFF TLS models on 64-bit AIX:
+ - local-exec (variables in the main executable, TP-relative offset)
+ - initial-exec (R_TLS_IE in which the compiler uses this for extern __thread
+ variables in the main exe when linked without -brtl)
+ - global-dynamic (R_TLS + R_TLSM in which cross-module access to variables
+ in a shared library)
+ - local-dynamic (R_TLS_LD + R_TLSML The within-module access inside the
+ shared library; also exercises the access-dependency
+ case for ld_only_var which is never accessed here) */
+
+/* local-exec TLS variables: defined in the main executable. */
+__thread int le_var1 = 100;
+__thread int le_var2 = 200;
+
+/* Declaration of TLS variables from the shared library.
+ The compiler uses global-dynamic model for these cross-module accesses,
+ emitting R_TLS + R_TLSM pairs in the .loader section. */
+extern __thread int gd_var1;
+extern __thread int gd_var2;
+/* ld_only_var is intentionally NOT declared here we only look it up via
+ GDB's symbol lookup to exercise the access-dependency fallback. This
+ is an edge case. */
+
+extern int lib_set_tls_vars (int, int, int);
+extern int lib_get_gd_var1 (void);
+extern int lib_get_gd_var2 (void);
+extern int lib_get_ld_only (void);
+
+volatile int sink;
+
+int
+main (void)
+{
+ /* Set local-exec variables to known values. */
+ le_var1 = 11;
+ le_var2 = 22;
+
+ /* Access cross-module globals -- forces R_TLS + R_TLSM into the main
+ executable's .loader section for gd_var1 and gd_var2. */
+ gd_var1 = 33;
+ gd_var2 = 44;
+
+ /* Call into the library so the library-side TLS is initialised. This
+ also exercises the local-dynamic model inside the shared library (the
+ compiler will use R_TLS_LD for within-module accesses to gd_var1,
+ gd_var2, and ld_only_var inside the .so). */
+ lib_set_tls_vars (33, 44, 55);
+
+ sink = le_var1 + le_var2 + gd_var1 + gd_var2; /* breakpoint here */
+
+ return sink;
+}
diff --git a/gdb/testsuite/gdb.arch/powerpc-aix-tls.exp b/gdb/testsuite/gdb.arch/powerpc-aix-tls.exp
new file mode 100644
index 00000000000..824884dffc9
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/powerpc-aix-tls.exp
@@ -0,0 +1,147 @@
+# Copyright (C) 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 <https://urldefense.proofpoint.com/v2/url?u=http-3A__www.gnu.org_licenses_&d=DwIDAg&c=BSDicqBQBDjDI9RkVyTcHQ&r=f-oUQ8ByG1nZ71OI9p76qywCPh7mxzU69hBYnkP9Nis&m=66_Kp84mmQEKNZ7tKE-MfzLaqfgXmgYhLjZwTzzdWoOPrQlE1jHOFQhT8b6nDb4a&s=qdq2vvKujGmQ9cWtAL7q8LKQY1jPVN2ll2n_X9kaJYA&e=
>.
+
+# Test GDB TLS variable access on 64-bit AIX (XCOFF) covering all four
+# TLS models:
+#
+# 1. local-exec [le_var1, le_var2 variables defined in the main executable]
+# GDB reads the XCOFF symbol value directly as a TP-relative offset.
+#
+# 2. initial-exec [gd_var1, gd_var2 accessed from the main executable]
+# If the compiler chose R_TLS_IE for cross-module accesses
+# GDB reads the TOC slot and adds it to TP.
+#
+# 3. global-dynamic [gd_var1, gd_var2 via R_TLS + R_TLSM]
+# GDB reads the module-id from the R_TLSM slot, looks up
+# thread_vector[mod_id] to find the TLS block base, and adds the per-variable
+# offset from the R_TLS slot.
+#
+# 4. local-dynamic [ld_only_var which is only accessed from inside the .so of this test case]
+# GDB reads the module-id from the R_TLSML slot in the .so, looks
+# up thread_vector[mod_id], and adds the within-module offset from R_TLS_LD.
+# ld_only_var is never referenced in the main executable, so there
+# is no per-variable TOC slot for it in the main executbale; So GDB must use
+# the access-dependency fallback path which is rs6000_aix_compute_tls_address ().
+
+require {istarget "powerpc*-*-aix*"}
+
+standard_testfile powerpc-aix-tls-main.c
+set srcfile_lib powerpc-aix-tls-lib.c
+set binfile_lib [standard_output_file powerpc-aix-tls-lib.so]
+
+# Build shared library
+# On AIX with GCC the standard gdb_compile_shlib helper appends
+# "-Wl,-soname,<name>" which the AIX ld interprets as an input file
+# (COFF mismatch error). Instead we do the two-step compile+link
+# manually. First compile the .c to a -fPIC .o, then link it to a .so
+# with -shared -Wl,-bexpall -lpthread (needed for __tls_get_addr).
+set lib_obj [standard_output_file powerpc-aix-tls-lib.c.o]
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile_lib}" \
+ "${lib_obj}" \
+ object \
+ {debug additional_flags=-fPIC}] != "" } {
+ unsupported "failed to compile shared library object"
+ return
+}
+if { [gdb_compile "${lib_obj}" \
+ "${binfile_lib}" \
+ executable \
+ {debug additional_flags=-shared \
+ additional_flags=-Wl,-bexpall \
+ additional_flags=-lpthread}] != "" } {
+ unsupported "failed to link shared library"
+ return
+}
+
+# Build main executable linked against the shared library
+# Pass the .so directly to gcc; on AIX with GCC the shared lib goes on
+# the link command line as a regular file argument.
+if { [gdb_compile "${srcdir}/${subdir}/${srcfile}" \
+ "${binfile}" \
+ executable \
+ [list debug \
+ additional_flags=${binfile_lib} \
+ additional_flags=-lpthread]] != "" } {
+ unsupported "failed to compile test executable"
+ return
+}
+
+# Helper: assert that printing VAR_EXPR from the inferior gives EXPECTED.
+proc check_tls_var { var_expr expected test_name } {
+ gdb_test "print $var_expr" "= $expected" $test_name
+}
+
+clean_restart ${testfile}
+gdb_load_shlib ${binfile_lib}
+
+if {![runto_main]} {
+ return
+}
+
+# Set breakpoint at the labelled line in main.
+set bp_line [gdb_get_line_number "breakpoint here" \
+ "${srcdir}/${subdir}/${srcfile}"]
+gdb_breakpoint $bp_line
+gdb_continue_to_breakpoint "run to breakpoint"
+
+# local-exec testing
+# le_var1 and le_var2 are defined in the main executable. At this point
+# they have been set to 11 and 22 respectively.
+with_test_prefix "local-exec" {
+ check_tls_var le_var1 11 "le_var1 == 11"
+ check_tls_var le_var2 22 "le_var2 == 22"
+
+ gdb_test "info address le_var1" \
+ "Symbol \"le_var1\" is a thread-local variable.*" \
+ "info address le_var1"
+}
+
+# initial-exec / global-dynamic testing
+# gd_var1 and gd_var2 are defined in the shared library and accessed
+# from the main executable. Depending on the compiler's TLS dialect
+# the main exe will use either R_TLS_IE = initial-exec or R_TLS+R_TLSM
+# global-dynamic. Both must produce the correct values 33 and 44.
+with_test_prefix "gd-from-main" {
+ check_tls_var gd_var1 33 "gd_var1 == 33"
+ check_tls_var gd_var2 44 "gd_var2 == 44"
+
+ gdb_test "info address gd_var1" \
+ "Symbol \"gd_var1\" is a thread-local variable.*" \
+ "info address gd_var1"
+}
+
+# local-dynamic testing
+# ld_only_var is accessed solely from within the shared library using
+# local-dynamic model R_TLS_LD + R_TLSML. lib_set_tls_vars set it to
+# 55. Because ld_only_var is not referenced in the main executable there
+# is no per-variable TOC slot for it in the main exe's .loader section;
+# The fallback code must be used here which is rs6000_aix_compute_tls_address ()
+with_test_prefix "local-dynamic" {
+ check_tls_var ld_only_var 55 "ld_only_var == 55"
+
+ gdb_test "info address ld_only_var" \
+ "Symbol \"ld_only_var\" is a thread-local variable.*" \
+ "info address ld_only_var"
+}
+
+# Library-side local-dynamic - verify via library accessor
+# Cross-check by calling the library function that reads the same variable.
+with_test_prefix "lib-accessor" {
+ gdb_test "print lib_get_gd_var1()" "= 33" "lib_get_gd_var1"
+ gdb_test "print lib_get_gd_var2()" "= 44" "lib_get_gd_var2"
+ gdb_test "print lib_get_ld_only()" "= 55" "lib_get_ld_only"
+}
--
2.51.2