[PATCH v2] elf: Allow vDSO as a direct dependency (BZ 33335)

Adhemerval Zanella Netto adhemerval.zanella@linaro.org
Mon Mar 2 19:58:25 GMT 2026


Ping.

On 19/12/25 16:09, Adhemerval Zanella wrote:
> The vDSO is treated as a special link map, and using it as a direct
> dependency shows two issues:
> 
> 1. Binding uses the implicit vDSO provided by the kernel (even though a
>    library is explicitly provided);
> 2. And depending on how the binary is linked, it can cause loops that
>    trigger an inconsistency during process startup.
> 
> Both issues can be shown with the following example:
> 
>   $ cat > repro.c << EOF
>   #include <time.h>
>   extern int __vdso_clock_gettime (clockid_t clk_id, struct timespec *tp);
>   int main ()
>   {
>     __vdso_clock_gettime (CLOCK_REALTIME, &(struct timespec) {0});
>   }
>   EOF
>   $ cat > vdso.c << EOF
>   #include <stdio.h>
>   #include <time.h>
>   int __vdso_clock_gettime (clockid_t clk_id, struct timespec *tp)
>   {
>     puts ( __func__);
>   }
>   EOF
>   $ gcc -shared -fPIC vdso.c -o linux-vdso.so.1 -nodefaultlibs
>   $ gcc repro.c -L. -l:linux-vdso.so.1 -o test1 && ./test1
>   $ gcc repro.c -nodefaultlibs -L. -lc -l:linux-vdso.so.1 -o test2 && ./test2
>   Inconsistency detected by ld.so: rtld.c: 2013: dl_main: Assertion `GL(dl_rtld_map).l_prev->l_next == GL(dl_rtld_map).l_next' failed!
> 
> The first issue, always binding to implicit vDSO, is caused by the vDSO
> being present in the global link map list during _dl_lookup_map. The
> second issue, triggered by test2, is trickier to understand, but also
> due to the same behavior: after the program is loaded
> (_dl_map_object_deps), the main_map search list contains the loader in a
> position different than last one ({ "", "libc.so", "ld.so",
> "linux-vdso.so.1" }). This causes a cycle in the search list's order,
> triggering the assert.
> 
> To fix both issues, a new flag is added to link_map when the vDSO is
> loaded (through __RTLD_VDSO), and the vDSO link map is ignored on
> _dl_lookup_map. This allows programs to bind to a provided DSO, aiming
> to mimic Linux vDSO.
> 
> The test requires specific rules to build a program that places the vDSO
> at the end of the DT_NEEDED dynamic tags, and additional make rules to
> handle different vDSO names across architectures.
> 
> Checked on x86_64-linux-gnu, i686-linux-gnu, aarch64-linux-gnu,
> powerpc-linux-gnu, and powerpc64-linux-gnu.
> 
> Changes from v1:
> * Use a bit from l_type instead of adding a new member at link_map.
> * Move tests to sysdeps/unix/sysv/linux/.
> 
> ---
>  elf/Makefile                                  | 39 +++++++++++++++++++
>  elf/dl-load.c                                 |  8 +++-
>  elf/setup-vdso.h                              |  2 +-
>  include/link.h                                |  3 +-
>  sysdeps/unix/sysv/linux/aarch64/Makefile      |  2 +
>  sysdeps/unix/sysv/linux/arm/Makefile          |  2 +
>  sysdeps/unix/sysv/linux/i386/Makefile         |  2 +
>  sysdeps/unix/sysv/linux/loongarch/Makefile    |  2 +
>  sysdeps/unix/sysv/linux/mips/Makefile         |  2 +
>  .../sysv/linux/powerpc/powerpc32/Makefile     |  2 +
>  sysdeps/unix/sysv/linux/riscv/Makefile        |  2 +
>  sysdeps/unix/sysv/linux/s390/s390-32/Makefile |  2 +
>  sysdeps/unix/sysv/linux/s390/s390-64/Makefile |  2 +
>  sysdeps/unix/sysv/linux/tst-vdso-1.c          |  1 +
>  sysdeps/unix/sysv/linux/tst-vdso-2.c          |  1 +
>  sysdeps/unix/sysv/linux/tst-vdso-lib.c        | 24 ++++++++++++
>  sysdeps/unix/sysv/linux/tst-vdso.c            | 34 ++++++++++++++++
>  sysdeps/unix/sysv/linux/x86_64/Makefile       |  2 +
>  18 files changed, 128 insertions(+), 4 deletions(-)
>  create mode 100644 sysdeps/unix/sysv/linux/tst-vdso-1.c
>  create mode 100644 sysdeps/unix/sysv/linux/tst-vdso-2.c
>  create mode 100644 sysdeps/unix/sysv/linux/tst-vdso-lib.c
>  create mode 100644 sysdeps/unix/sysv/linux/tst-vdso.c
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index 07b456f4f5..648c17be63 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -1450,6 +1450,14 @@ ifeq ($(run-built-tests),yes)
>  tests-special += $(objpfx)tst-origin.out
>  endif
>  
> +ifndef $(vdso-name)
> +tests += tst-vdso-1
> +
> +ifeq ($(run-built-tests),yes)
> +tests-special += $(objpfx)tst-vdso-2.out
> +endif
> +endif
> +
>  include ../Rules
>  
>  ifeq (yes,$(build-shared))
> @@ -3534,3 +3542,34 @@ $(objpfx)tst-origin.out: tst-origin.sh $(objpfx)tst-origin
>  	$(evaluate-test)
>  
>  $(objpfx)tst-dlopen-sgid.out: $(objpfx)tst-dlopen-sgid-mod.so
> +
> +ifndef $(vdso-name)
> +CFLAGS-tst-vdso-lib.c += $(no-stack-protector)
> +$(objpfx)linux-vdso.os: ../sysdeps/unix/sysv/linux/tst-vdso-lib.c $(before-compile)
> +	$(compile-command.c) -UMODULE_NAME -DMODULE_NAME=testsuite
> +$(objpfx)$(vdso-name).so: $(objpfx)linux-vdso.os
> +	$(LINK.o) -nodefaultlibs  -Wl,--soname,$(vdso-name).so.1 -shared \
> +		  -o $@ -B$(csu-objpfx) $(LDFLAGS.so) $<
> +$(objpfx)$(vdso-name).so.1: $(objpfx)$(vdso-name).so
> +	$(make-link)
> +
> +# Link tst-vdso-1 with $(vdso-name).so, but without a full path.
> +LDFLAGS-tst-vdso-1 += -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link))
> +LDLIBS-tst-vdso-1 += -l:$(vdso-name).so
> +$(objpfx)tst-vdso-1: +nolink-deps += $(objpfx)$(vdso-name).so
> +$(objpfx)tst-vdso-1: $(objpfx)$(vdso-name).so.1
> +
> +# The tst-vdso-2 requires a special link rule to put the vDSO on the last
> +# tag in the dynamic section (to trigger the circular dependency as
> +# described by BZ 33335)
> +$(objpfx)tst-vdso-2: +nolink-deps += $(objpfx)$(vdso-name).so
> +$(objpfx)tst-vdso-2: $(objpfx)tst-vdso-2.o $(objpfx)$(vdso-name).so.1 $(libsupport)
> +	$(LINK.o) -nodefaultlibs -o $@ $< \
> +	  $(libsupport) $(static-gnulib) $(common-objpfx)libc_nonshared.a \
> +	  $(link-libc-rpath) \
> +	  -Wl,-rpath,\$$ORIGIN -L$(subst :, -L,$(rpath-link)) \
> +	  -lc -l:$(vdso-name).so \
> +	  -Wl,--dynamic-linker=$(objpfx)ld.so,--no-as-needed $(objpfx)ld.so
> +
> +$(objpfx)tst-vdso-2.out: $(objpfx)tst-vdso-2
> +endif
> diff --git a/elf/dl-load.c b/elf/dl-load.c
> index 0c57b86e3c..8305f63ce8 100644
> --- a/elf/dl-load.c
> +++ b/elf/dl-load.c
> @@ -1904,8 +1904,12 @@ _dl_lookup_map (Lmid_t nsid, const char *name)
>      {
>        /* If the requested name matches the soname of a loaded object,
>  	 use that object.  Elide this check for names that have not
> -	 yet been opened.  */
> -      if (__glibc_unlikely ((l->l_faked | l->l_removed) != 0))
> +	 yet been opened.
> +
> +	 Also, avoid matching the vDSO; if the DSO requires it, assume it is
> +	 provided by a real object (rather than the implicitly loaded one).  */
> +      if (__glibc_unlikely ((l->l_faked | l->l_removed) != 0
> +			    || l->l_type == lt_vdso))
>  	continue;
>        if (!_dl_name_match_p (name, l))
>  	{
> diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h
> index 6e974875e7..2e02c574a3 100644
> --- a/elf/setup-vdso.h
> +++ b/elf/setup-vdso.h
> @@ -29,7 +29,7 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
>       better be, since it's read-only and so we couldn't relocate it).
>       We just want our data structures to describe it as if we had just
>       mapped and relocated it normally.  */
> -  struct link_map *l = _dl_new_object ((char *) "", "", lt_library, NULL,
> +  struct link_map *l = _dl_new_object ((char *) "", "", lt_vdso, NULL,
>  				       __RTLD_VDSO, LM_ID_BASE);
>    bool l_addr_set = false;
>    if (__glibc_likely (l != NULL))
> diff --git a/include/link.h b/include/link.h
> index 78fa288b9d..e07f4d0875 100644
> --- a/include/link.h
> +++ b/include/link.h
> @@ -175,7 +175,8 @@ struct link_map
>        {
>  	lt_executable,		/* The main executable program.  */
>  	lt_library,		/* Library needed by main executable.  */
> -	lt_loaded		/* Extra run-time loaded shared object.  */
> +	lt_loaded,		/* Extra run-time loaded shared object.  */
> +	lt_vdso,		/* The vDSO object provided by the kernel.  */
>        } l_type:2;
>      unsigned int l_dt_relr_ref:1; /* Nonzero if GLIBC_ABI_DT_RELR is
>  				     referenced.  */
> diff --git a/sysdeps/unix/sysv/linux/aarch64/Makefile b/sysdeps/unix/sysv/linux/aarch64/Makefile
> index 6741f8ec00..569f840e1e 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/Makefile
> +++ b/sysdeps/unix/sysv/linux/aarch64/Makefile
> @@ -158,3 +158,5 @@ endif
>  
>  abi-lp64-condition := !defined __AARCH64EB__
>  abi-lp64_be-condition := defined __AARCH64EB__
> +
> +vdso-name := linux-vdso
> diff --git a/sysdeps/unix/sysv/linux/arm/Makefile b/sysdeps/unix/sysv/linux/arm/Makefile
> index e73ce4f811..bbb79f9ba4 100644
> --- a/sysdeps/unix/sysv/linux/arm/Makefile
> +++ b/sysdeps/unix/sysv/linux/arm/Makefile
> @@ -55,3 +55,5 @@ endif
>  abi-includes :=
>  abi-soft-condition := !defined __ARM_PCS_VFP
>  abi-hard-condition := defined __ARM_PCS_VFP
> +
> +vdso-name := linux-vdso
> diff --git a/sysdeps/unix/sysv/linux/i386/Makefile b/sysdeps/unix/sysv/linux/i386/Makefile
> index f1f8c3f44c..95e1f52b8e 100644
> --- a/sysdeps/unix/sysv/linux/i386/Makefile
> +++ b/sysdeps/unix/sysv/linux/i386/Makefile
> @@ -1,6 +1,8 @@
>  # The default ABI is 32.
>  default-abi := 32
>  
> +vdso-name := linux-gate
> +
>  ifeq ($(subdir),misc)
>  sysdep_routines += ioperm iopl vm86
>  
> diff --git a/sysdeps/unix/sysv/linux/loongarch/Makefile b/sysdeps/unix/sysv/linux/loongarch/Makefile
> index c84a1762ed..e96145aacf 100644
> --- a/sysdeps/unix/sysv/linux/loongarch/Makefile
> +++ b/sysdeps/unix/sysv/linux/loongarch/Makefile
> @@ -10,3 +10,5 @@ endif
>  
>  abi-lp64s-condition	:= __WORDSIZE == 64 && defined __loongarch_soft_float
>  abi-lp64d-condition	:= __WORDSIZE == 64 && defined __loongarch_double_float
> +
> +vdso-name := linux-vdso
> diff --git a/sysdeps/unix/sysv/linux/mips/Makefile b/sysdeps/unix/sysv/linux/mips/Makefile
> index 05ec9150b2..c61c03493d 100644
> --- a/sysdeps/unix/sysv/linux/mips/Makefile
> +++ b/sysdeps/unix/sysv/linux/mips/Makefile
> @@ -86,3 +86,5 @@ ASFLAGS-.os += -Wa,-execstack
>  ASFLAGS-.op += -Wa,-execstack
>  ASFLAGS-.oS += -Wa,-execstack
>  endif
> +
> +vdso-name := linux-vdso
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/Makefile b/sysdeps/unix/sysv/linux/powerpc/powerpc32/Makefile
> index b2d7c8f6cc..6a19b5eeb1 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/Makefile
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/Makefile
> @@ -13,3 +13,5 @@ ifeq ($(subdir),conform)
>  # For bug 21635.
>  conformtest-xfail-conds += powerpc32-linux
>  endif
> +
> +vdso-name := linux-vdso32
> diff --git a/sysdeps/unix/sysv/linux/riscv/Makefile b/sysdeps/unix/sysv/linux/riscv/Makefile
> index 04abf226ad..8336dd77b9 100644
> --- a/sysdeps/unix/sysv/linux/riscv/Makefile
> +++ b/sysdeps/unix/sysv/linux/riscv/Makefile
> @@ -25,3 +25,5 @@ abi-ilp32-condition   := __WORDSIZE == 32 && defined __riscv_float_abi_soft
>  abi-ilp32d-condition  := __WORDSIZE == 32 && defined __riscv_float_abi_double
>  abi-lp64-condition    := __WORDSIZE == 64 && defined __riscv_float_abi_soft
>  abi-lp64d-condition   := __WORDSIZE == 64 && defined __riscv_float_abi_double
> +
> +vdso-name := linux-vdso
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/Makefile b/sysdeps/unix/sysv/linux/s390/s390-32/Makefile
> index 2a5b4fbb6f..de206fa0bc 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/Makefile
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/Makefile
> @@ -29,3 +29,5 @@ shared-only-routines += divdi3
>  CPPFLAGS-divdi3.c = -Din_divdi3_c
>  endif
>  endif
> +
> +vdso-name := linux-vdso32
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/Makefile b/sysdeps/unix/sysv/linux/s390/s390-64/Makefile
> index 6795734747..75c66b217b 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/Makefile
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/Makefile
> @@ -16,3 +16,5 @@ endif
>  ifeq ($(subdir),stdlib)
>  sysdep_routines += __makecontext_ret
>  endif
> +
> +vdso-name := linux-vdso64
> diff --git a/sysdeps/unix/sysv/linux/tst-vdso-1.c b/sysdeps/unix/sysv/linux/tst-vdso-1.c
> new file mode 100644
> index 0000000000..2dd0855ea2
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-vdso-1.c
> @@ -0,0 +1 @@
> +#include "tst-vdso.c"
> diff --git a/sysdeps/unix/sysv/linux/tst-vdso-2.c b/sysdeps/unix/sysv/linux/tst-vdso-2.c
> new file mode 100644
> index 0000000000..2dd0855ea2
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-vdso-2.c
> @@ -0,0 +1 @@
> +#include "tst-vdso.c"
> diff --git a/sysdeps/unix/sysv/linux/tst-vdso-lib.c b/sysdeps/unix/sysv/linux/tst-vdso-lib.c
> new file mode 100644
> index 0000000000..abbd88e3fb
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-vdso-lib.c
> @@ -0,0 +1,24 @@
> +/* Check for explicit vDSO dependency (BZ 33335)
> +   Copyright (C) 2025 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 <time.h>
> +
> +int __kernel_clock_gettime (clockid_t clk_id, struct timespec *tp)
> +{
> +  return 42;
> +}
> diff --git a/sysdeps/unix/sysv/linux/tst-vdso.c b/sysdeps/unix/sysv/linux/tst-vdso.c
> new file mode 100644
> index 0000000000..03e92ab734
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-vdso.c
> @@ -0,0 +1,34 @@
> +/* Check for explicit vDSO dependency (BZ 33335)
> +   Copyright (C) 2025 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 <time.h>
> +#include <support/check.h>
> +
> +extern int __kernel_clock_gettime(clockid_t clk_id, struct timespec *tp);
> +
> +static int
> +do_test (void)
> +{
> +  TEST_COMPARE (__kernel_clock_gettime (CLOCK_REALTIME,
> +					&(struct timespec) { 0 }),
> +		42);
> +
> +  return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/sysdeps/unix/sysv/linux/x86_64/Makefile b/sysdeps/unix/sysv/linux/x86_64/Makefile
> index 6938382801..6c85e6b321 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/Makefile
> +++ b/sysdeps/unix/sysv/linux/x86_64/Makefile
> @@ -121,3 +121,5 @@ tests += tst-cet-setcontext-1
>  CFLAGS-tst-cet-setcontext-1.c += -mshstk
>  endif
>  endif
> +
> +vdso-name := linux-vdso



More information about the Libc-alpha mailing list