@@ -407,6 +407,7 @@ tests += \
tst-align2 \
tst-align3 \
tst-audit-symbind-dlsym \
+ tst-audit-symbind-flags \
tst-audit-tlsdesc \
tst-audit-tlsdesc-dlopen \
tst-audit-tlsdesc-dlopen2 \
@@ -913,6 +914,7 @@ modules-names += \
tst-array2dep \
tst-array5dep \
tst-audit-symbind-dlsym-mod \
+ tst-audit-symbind-flags-mod \
tst-audit-tlsdesc-mod1 \
tst-audit-tlsdesc-mod2 \
tst-audit11mod1 \
@@ -950,6 +952,8 @@ modules-names += \
tst-auditmanymod9 \
tst-auditmod-symbind-dlsym-a \
tst-auditmod-symbind-dlsym-b \
+ tst-auditmod-symbind-flags-a \
+ tst-auditmod-symbind-flags-b \
tst-auditmod-tlsdesc \
tst-auditmod-tlsdesc2 \
tst-auditmod1 \
@@ -2874,6 +2878,12 @@ $(objpfx)tst-audit-symbind-dlsym.out: \
tst-audit-symbind-dlsym-ENV = \
LD_AUDIT=$(objpfx)tst-auditmod-symbind-dlsym-a.so:$(objpfx)tst-auditmod-symbind-dlsym-b.so
+$(objpfx)tst-audit-symbind-flags: $(objpfx)tst-audit-symbind-flags-mod.so
+$(objpfx)tst-audit-symbind-flags.out: \
+ $(objpfx)tst-auditmod-symbind-flags-a.so \
+ $(objpfx)tst-auditmod-symbind-flags-b.so
+LDFLAGS-tst-audit-symbind-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 \
@@ -211,7 +211,7 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
const char *strtab2 = (const void *) D_PTR (result, l_info[DT_STRTAB]);
- unsigned int flags = 0;
+ unsigned int altvalue = 0;
struct audit_ifaces *afct = GLRO(dl_audit);
uintptr_t new_value = (uintptr_t) sym.st_value;
for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
@@ -222,17 +222,19 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
if ((l_state->bindflags & LA_FLG_BINDFROM) != 0
&& (result_state->bindflags & LA_FLG_BINDTO) != 0)
{
+ /* Only LA_SYMB_ALTVALUE is carried over from previous auditors. */
+ unsigned int flags = altvalue;
+ if (for_jmp_slot)
+ flags |= LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT;
if (afct->symbind != NULL)
{
- flags |= for_jmp_slot ? LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT
- : 0;
new_value = afct->symbind (&sym, boundndx,
&l_state->cookie,
&result_state->cookie, &flags,
strtab2 + defsym->st_name);
if (new_value != (uintptr_t) sym.st_value)
{
- flags |= LA_SYMB_ALTVALUE;
+ altvalue = LA_SYMB_ALTVALUE;
sym.st_value = for_jmp_slot
? DL_FIXUP_BINDNOW_ADDR_VALUE (new_value) : new_value;
}
@@ -255,10 +257,10 @@ _dl_audit_symbind (struct link_map *l, struct reloc_result *reloc_result,
if (!for_jmp_slot)
{
reloc_result->enterexit = enterexit;
- reloc_result->flags = flags;
+ reloc_result->flags = altvalue;
}
- if (flags & LA_SYMB_ALTVALUE)
+ if (altvalue != 0)
DL_FIXUP_BINDNOW_RELOC (l, reloc, value, new_value, sym.st_value, lazy);
}
@@ -288,7 +290,7 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
const char *symname = strtab + sym.st_name;
/* Keep track of overwritten addresses. */
- unsigned int flags = reloc_result->flags;
+ unsigned int altvalue = reloc_result->flags;
struct audit_ifaces *afct = GLRO(dl_audit);
for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
@@ -301,13 +303,18 @@ _dl_audit_pltenter (struct link_map *l, struct reloc_result *reloc_result,
struct auditstate *l_state = link_map_audit_state (l, cnt);
struct auditstate *bound_state
= link_map_audit_state (reloc_result->bound, cnt);
+ /* Each auditor only sees its own LA_SYMB_NOPLTENTER and
+ LA_SYMB_NOPLTEXIT. */
+ unsigned int flags = altvalue
+ | ((reloc_result->enterexit >> (2 * (cnt + 1)))
+ & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT));
uintptr_t new_value
= afct->ARCH_LA_PLTENTER (&sym, reloc_result->boundndx,
&l_state->cookie, &bound_state->cookie,
regs, &flags, symname, &new_framesize);
if (new_value != (uintptr_t) sym.st_value)
{
- flags |= LA_SYMB_ALTVALUE;
+ altvalue = LA_SYMB_ALTVALUE;
sym.st_value = new_value;
}
new file mode 100644
@@ -0,0 +1,29 @@
+/* Module for tst-audit-symbind-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_symbind_flags_func1 (void)
+{
+ return 1;
+}
+
+int
+tst_audit_symbind_flags_func2 (void)
+{
+ return 2;
+}
new file mode 100644
@@ -0,0 +1,137 @@
+/* Check that la_symbind and la_pltenter flags are 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 <getopt.h>
+#include <stdbool.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_symbind_flags_func1 (void);
+int tst_audit_symbind_flags_func2 (void);
+
+static int
+handle_restart (void)
+{
+ /* Call each function twice so la_pltenter is also exercised after
+ the lazy binding. */
+ TEST_COMPARE (tst_audit_symbind_flags_func1 (), 1);
+ TEST_COMPARE (tst_audit_symbind_flags_func1 (), 1);
+ TEST_COMPARE (tst_audit_symbind_flags_func2 (), 2);
+ TEST_COMPARE (tst_audit_symbind_flags_func2 (), 2);
+ 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 void
+run (char *program, bool bindnow)
+{
+ if (bindnow)
+ setenv ("LD_BIND_NOW", "1", 1);
+ else
+ unsetenv ("LD_BIND_NOW");
+
+ 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);
+ support_capture_subprocess_check (&result, "tst-audit-symbind-flags", 0,
+ sc_allow_stderr);
+ const char *err = result.err.buffer;
+
+ /* tst-auditmod-symbind-flags-a.so sets LA_SYMB_NOPLTENTER and
+ LA_SYMB_NOPLTEXIT for func1 in la_symbind, which must not be seen by
+ tst-auditmod-symbind-flags-b.so. ld.so sets both for bind-now. */
+ const char *expected1 = bindnow
+ ? "b: la_symbind: tst_audit_symbind_flags_func1 3"
+ : "b: la_symbind: tst_audit_symbind_flags_func1 0";
+ const char *expected2 = bindnow
+ ? "b: la_symbind: tst_audit_symbind_flags_func2 3"
+ : "b: la_symbind: tst_audit_symbind_flags_func2 0";
+ TEST_COMPARE (count_lines (err, expected1), 1);
+ TEST_COMPARE (count_lines (err, expected2), 1);
+
+ /* Not all architectures support la_pltenter. If they do, the first auditor
+ sets LA_SYMB_NOPLTENTER for func2 in its first la_pltenter, which must
+ not stop the calls for the second auditor. */
+ if (count_lines (err, "a: la_pltenter: tst_audit_symbind_flags_func2") > 0)
+ {
+ TEST_VERIFY (!bindnow);
+ TEST_COMPARE (count_lines (err,
+ "a: la_pltenter: tst_audit_symbind_flags_func2"), 1);
+ TEST_COMPARE (count_lines (err,
+ "b: la_pltenter: tst_audit_symbind_flags_func1"), 2);
+ TEST_COMPARE (count_lines (err,
+ "b: la_pltenter: tst_audit_symbind_flags_func2"), 2);
+ }
+ else if (!bindnow)
+ puts ("info: la_pltenter not called, skipping its checks");
+
+ if (support_record_failure_is_failed ())
+ printf ("info: auditor output (LD_BIND_NOW=%d):\n%s", bindnow, err);
+
+ support_capture_subprocess_free (&result);
+}
+
+static int
+do_test (void)
+{
+ if (restart)
+ return handle_restart ();
+
+ char *audit = xasprintf ("%s/elf/tst-auditmod-symbind-flags-a.so"
+ ":%s/elf/tst-auditmod-symbind-flags-b.so",
+ support_objdir_root, support_objdir_root);
+ setenv ("LD_AUDIT", audit, 1);
+ free (audit);
+
+ char *program = xasprintf ("%s/elf/tst-audit-symbind-flags",
+ support_objdir_root);
+ run (program, false);
+ run (program, true);
+ free (program);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,64 @@
+/* Auditor for tst-audit-symbind-flags that opts out of PLT tracing.
+ 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>
+
+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, "tst_audit_symbind_flags_func1") == 0)
+ *flags |= LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT;
+ 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 (strcmp (symname, "tst_audit_symbind_flags_func2") == 0)
+ {
+ fprintf (stderr, "a: la_pltenter: %s\n", symname);
+ *flags |= LA_SYMB_NOPLTENTER;
+ }
+ return sym->st_value;
+}
new file mode 100644
@@ -0,0 +1,64 @@
+/* Auditor for tst-audit-symbind-flags that logs the flags it gets.
+ 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>
+
+#define PREFIX "tst_audit_symbind_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 (strncmp (symname, PREFIX, strlen (PREFIX)) == 0)
+ fprintf (stderr, "b: la_symbind: %s %u\n", symname,
+ *flags & (LA_SYMB_NOPLTENTER | LA_SYMB_NOPLTEXIT));
+ 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, "b: la_pltenter: %s\n", symname);
+ return sym->st_value;
+}
@@ -35,6 +35,7 @@ test-xfail-tst-audit24c = yes
test-xfail-tst-audit24d = yes
test-xfail-tst-audit25a = yes
test-xfail-tst-audit25b = yes
+test-xfail-tst-audit-symbind-flags = yes
ifneq ($(o32-fpabi),)
tests += tst-abi-interlink