@@ -406,6 +406,7 @@ tests += \
tst-align \
tst-align2 \
tst-align3 \
+ tst-audit-plt-flags \
tst-audit-symbind-dlsym \
tst-audit-symbind-flags \
tst-audit-tlsdesc \
@@ -913,6 +914,7 @@ modules-names += \
tst-alignmod3 \
tst-array2dep \
tst-array5dep \
+ tst-audit-plt-flags-mod \
tst-audit-symbind-dlsym-mod \
tst-audit-symbind-flags-mod \
tst-audit-tlsdesc-mod1 \
@@ -950,6 +952,8 @@ modules-names += \
tst-auditmanymod7 \
tst-auditmanymod8 \
tst-auditmanymod9 \
+ tst-auditmod-plt-flags-a \
+ tst-auditmod-plt-flags-b \
tst-auditmod-symbind-dlsym-a \
tst-auditmod-symbind-dlsym-b \
tst-auditmod-symbind-flags-a \
@@ -2884,6 +2888,12 @@ $(objpfx)tst-audit-symbind-flags.out: \
$(objpfx)tst-auditmod-symbind-flags-b.so
LDFLAGS-tst-audit-symbind-flags = -Wl,-z,lazy
+$(objpfx)tst-audit-plt-flags: $(objpfx)tst-audit-plt-flags-mod.so
+$(objpfx)tst-audit-plt-flags.out: \
+ $(objpfx)tst-auditmod-plt-flags-a.so \
+ $(objpfx)tst-auditmod-plt-flags-b.so
+LDFLAGS-tst-audit-plt-flags = -Wl,-z,lazy
+
# tst-sonamemove links against an older implementation of the library.
LDFLAGS-tst-sonamemove-linkmod1.so = \
-Wl,--version-script=tst-sonamemove-linkmod1.map \
@@ -241,10 +241,11 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
}
/* Remember the results for every audit library and store a summary
- in the first two bits. */
- enterexit &= flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT);
- enterexit |= ((flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT))
- << ((cnt + 1) * 2));
+ in the first two bits, without clearing the bits of the previous
+ auditors. */
+ unsigned int noplt = flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT);
+ enterexit &= noplt | ~(LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT);
+ enterexit |= noplt << ((cnt + 1) * 2);
}
else
/* If the bind flags say this auditor is not interested, set the bits
@@ -375,7 +376,7 @@ _dl_audit_pltexit (struct link_map *l, ElfW(Word) reloc_arg,
{
if (afct->ARCH_LA_PLTEXIT != NULL
&& (reloc_result->enterexit
- & (LA_SYMB_NOPLTEXIT >> (2 * cnt))) == 0)
+ & (LA_SYMB_NOPLTEXIT << (2 * (cnt + 1)))) == 0)
{
struct auditstate *l_state = link_map_audit_state (l, cnt);
struct auditstate *bound_state
new file mode 100644
@@ -0,0 +1,35 @@
+/* Module for tst-audit-plt-flags.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+int
+tst_audit_plt_flags_func1 (void)
+{
+ return 1;
+}
+
+int
+tst_audit_plt_flags_func2 (void)
+{
+ return 2;
+}
+
+int
+tst_audit_plt_flags_func3 (void)
+{
+ return 3;
+}
new file mode 100644
@@ -0,0 +1,124 @@
+/* Check that LA_SYMB_NOPLTENTER and LA_SYMB_NOPLTEXIT are honored per auditor.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/subprocess.h>
+#include <support/support.h>
+
+static int restart;
+#define CMDLINE_OPTIONS \
+ { "restart", no_argument, &restart, 1 },
+
+int tst_audit_plt_flags_func1 (void);
+int tst_audit_plt_flags_func2 (void);
+int tst_audit_plt_flags_func3 (void);
+
+static int
+handle_restart (void)
+{
+ TEST_COMPARE (tst_audit_plt_flags_func1 (), 1);
+ TEST_COMPARE (tst_audit_plt_flags_func2 (), 2);
+ TEST_COMPARE (tst_audit_plt_flags_func3 (), 3);
+ return 0;
+}
+
+static int
+count_lines (const char *buf, const char *line)
+{
+ size_t len = strlen (line);
+ int n = 0;
+ for (const char *p = buf; p != NULL && *p != '\0'; )
+ {
+ if (strncmp (p, line, len) == 0 && p[len] == '\n')
+ n++;
+ p = strchr (p, '\n');
+ if (p != NULL)
+ p++;
+ }
+ return n;
+}
+
+static int
+do_test (void)
+{
+ if (restart)
+ return handle_restart ();
+
+ char *audit = xasprintf ("%s/elf/tst-auditmod-plt-flags-a.so"
+ ":%s/elf/tst-auditmod-plt-flags-b.so",
+ support_objdir_root, support_objdir_root);
+ setenv ("LD_AUDIT", audit, 1);
+ free (audit);
+
+ char *program = xasprintf ("%s/elf/tst-audit-plt-flags",
+ support_objdir_root);
+ char *args[] = { program, (char *) "--direct", (char *) "--restart", NULL };
+ struct support_spawn_wrapped *w
+ = support_spawn_wrap (program, args, NULL, 0);
+ struct support_capture_subprocess result
+ = support_capture_subprogram (w->path, w->argv, w->envp);
+ support_spawn_wrapped_free (w);
+ free (program);
+ support_capture_subprocess_check (&result, "tst-audit-plt-flags", 0,
+ sc_allow_stderr);
+ const char *err = result.err.buffer;
+
+ if (strstr (err, ": la_pltexit: ") == NULL)
+ FAIL_UNSUPPORTED ("la_pltexit not supported");
+
+ /* Both auditors request la_pltexit in la_pltenter. The first one
+ sets LA_SYMB_NOPLTEXIT for func1 and LA_SYMB_NOPLTENTER for func3,
+ the second one sets LA_SYMB_NOPLTEXIT for func2. */
+ static const struct
+ {
+ const char *line;
+ int count;
+ } expected[] =
+ {
+ { "a: la_pltenter: tst_audit_plt_flags_func1", 1 },
+ { "a: la_pltexit: tst_audit_plt_flags_func1", 0 },
+ { "b: la_pltenter: tst_audit_plt_flags_func1", 1 },
+ { "b: la_pltexit: tst_audit_plt_flags_func1", 1 },
+ { "a: la_pltenter: tst_audit_plt_flags_func2", 1 },
+ { "a: la_pltexit: tst_audit_plt_flags_func2", 1 },
+ { "b: la_pltenter: tst_audit_plt_flags_func2", 1 },
+ { "b: la_pltexit: tst_audit_plt_flags_func2", 0 },
+ { "a: la_pltenter: tst_audit_plt_flags_func3", 0 },
+ { "a: la_pltexit: tst_audit_plt_flags_func3", 1 },
+ { "b: la_pltenter: tst_audit_plt_flags_func3", 1 },
+ { "b: la_pltexit: tst_audit_plt_flags_func3", 1 },
+ };
+ for (size_t j = 0; j < array_length (expected); j++)
+ if (count_lines (err, expected[j].line) != expected[j].count)
+ FAIL ("expected %d \"%s\" lines", expected[j].count,
+ expected[j].line);
+
+ if (support_record_failure_is_failed ())
+ printf ("info: auditor output:\n%s", err);
+
+ support_capture_subprocess_free (&result);
+ return 0;
+}
+
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,85 @@
+/* Auditor for tst-audit-plt-flags.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <link.h>
+#include <stdio.h>
+#include <string.h>
+#include <tst-audit.h>
+
+#ifndef TAG
+# define TAG "a"
+/* The functions for which la_pltexit and la_pltenter are not wanted. */
+# define NOPLTEXIT_FUNC "tst_audit_plt_flags_func1"
+# define NOPLTENTER_FUNC "tst_audit_plt_flags_func3"
+#endif
+
+#define PREFIX "tst_audit_plt_flags_func"
+
+unsigned int
+la_version (unsigned int version)
+{
+ return LAV_CURRENT;
+}
+
+unsigned int
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
+{
+ return LA_FLG_BINDFROM | LA_FLG_BINDTO;
+}
+
+#if __ELF_NATIVE_CLASS == 64
+uintptr_t
+la_symbind64 (Elf64_Sym *sym, unsigned int ndx,
+ uintptr_t *refcook, uintptr_t *defcook,
+ unsigned int *flags, const char *symname)
+#else
+uintptr_t
+la_symbind32 (Elf32_Sym *sym, unsigned int ndx,
+ uintptr_t *refcook, uintptr_t *defcook,
+ unsigned int *flags, const char *symname)
+#endif
+{
+ if (strcmp (symname, NOPLTEXIT_FUNC) == 0)
+ *flags |= LA_SYMB_NOPLTEXIT;
+ if (strcmp (symname, NOPLTENTER_FUNC) == 0)
+ *flags |= LA_SYMB_NOPLTENTER;
+ return sym->st_value;
+}
+
+ElfW(Addr)
+pltenter (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, La_regs *regs, unsigned int *flags,
+ const char *symname, long int *framesizep)
+{
+ if (strncmp (symname, PREFIX, strlen (PREFIX)) == 0)
+ {
+ fprintf (stderr, TAG ": la_pltenter: %s\n", symname);
+ *framesizep = 1024;
+ }
+ return sym->st_value;
+}
+
+unsigned int
+pltexit (ElfW(Sym) *sym, unsigned int ndx, uintptr_t *refcook,
+ uintptr_t *defcook, const La_regs *inregs, La_retval *outregs,
+ const char *symname)
+{
+ if (strncmp (symname, PREFIX, strlen (PREFIX)) == 0)
+ fprintf (stderr, TAG ": la_pltexit: %s\n", symname);
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,22 @@
+/* Second auditor for tst-audit-plt-flags.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#define TAG "b"
+#define NOPLTEXIT_FUNC "tst_audit_plt_flags_func2"
+#define NOPLTENTER_FUNC ""
+#include "tst-auditmod-plt-flags-a.c"
@@ -29,6 +29,10 @@ $(objpfx)tst-audit27: $(objpfx)tst-audit27mod.so \
$(objpfx)tst-audit27mod.so: $(libsupport)
LDFLAGS-tst-audit27 += -Wl,-z,lazy
tst-audit27-ENV = LD_AUDIT=$(objpfx)tst-auditmod27.so
+
+ifeq (yes,$(have-test-bti))
+test-xfail-tst-audit-plt-flags = yes
+endif
endif
ifeq ($(subdir),elf)
@@ -69,6 +69,10 @@ CFLAGS-s_isnan.c += -fno-builtin-isnanf
test-xfail-test-float32x-float64-div = yes
endif
+ifeq ($(subdir),elf)
+test-xfail-tst-audit-plt-flags = yes
+endif
+
# Build everything with full IEEE math support, and with dynamic rounding;
# there are a number of math routines that are defined to work with the
# "current" rounding mode, and it's easiest to set this with all of them.
@@ -22,6 +22,8 @@ gen-as-const-headers += \
CFLAGS-tst-tunables-seal.c += -DTST_SEAL_TUNABLE_NAME=hwcaps
tst-tunables-seal-TUNABLES += glibc.cpu.hwcaps=-LASX
+
+test-xfail-tst-audit-plt-flags = yes
endif
ifeq ($(subdir),csu)
@@ -4,6 +4,8 @@ endif
ifeq ($(subdir),elf)
gen-as-const-headers += dl-link.sym
+
+test-xfail-tst-audit-plt-flags = yes
endif
# RISC-V's assembler also needs to know about PIC as it changes the definition