Compare commits

..
Author SHA1 Message Date
Yury Khrustalev 4189e8060e malloc: aarch64: Add MTE-aware implementations
Tag memory on systems that support MTE and when memory
tagging is enabled at runtime.

Also add tests that check logical and allocation tags.
2026-05-07 13:44:26 +01:00
Yury Khrustalev 43bd158d89 malloc: aarch64: Add MTE ifunc resolvers
We introduce the ARCH_INIT_MALLOC() macro that is defined in
the sysdep headers to allow doing target-specific initialisation
in __ptmalloc_init. On AArch64 we use this macro to modify
global variables in malloc.c when MTE is enabled based on the
runtime value calculated from the glibc.mem.aarch64_mte tunable.

We create AArch64-specific version of the 'malloc-size.h' header
to provide custom 'memsize()' and 'checked_request2size()'. This
is required to support MTE memory tagging without the need to
check its status every time.

We modify ifunc resolvers for AArch64 to return MTE-aware versions
if malloc functions when MTE is enabled. At this point these *_mte
functions don't actually do any memory tagging. This will be done
in a subsequent commit.

Finally, we add the glibc.mem.aarch64_mte tunable to all malloc
tests on AArch64. On systems without MTE this will have no effect
but on systems with MTE it will make sure that tests are executed
with memory tagging enabled in sync mode.
2026-05-07 13:43:10 +01:00
Yury Khrustalev fe1173fcf4 aarch64: add glibc.mem.aarch64_mte tunable
Add new tunable glibc.mem.aarch64_mte of string type with supported
values: none, sync, async, and auto. Use it to control prctl syscall
used to send the PR_SET_TAGGED_ADDR_CTRL command.

To avoid confusion, we also remove the glibc.mem.tagging tunable.

The new tunable defaults to 'none' and only works of MTE is supported.
2026-05-07 13:43:10 +01:00
Yury Khrustalev acce1ee5a0 malloc: Put malloc_chunk into separate header
Refactor malloc code to extract the definition of malloc_chunk
struct into a dedicated header  malloc-chunk.h along with basic
chunk-related macros.

This is a sysdep header.

The new header 'malloc-chunk.h' will include 'malloc-alignment.h'
that has all dependencies necessary for chunk declarations.

In turn, 'malloc-size.h' will now include 'malloc-chunk.h'. We
also move size-related definitions from malloc.c into this header.
2026-05-07 13:43:10 +01:00
Yury Khrustalev 23d806003d malloc: Put MALLOC_ALIGNMENT depends in one header
The 'malloc-alignment.h' header defines macro MALLOC_ALIGNMENT that
depends on SIZE_SZ which in turn depends on INTERNAL_SIZE_T.

These dependencies used to be defined in 'malloc-size.h'. We should
either merge these two headers or move the dependencies or change the
'malloc-alignment.h' header to make sure it is self-consistent.

This commit does the latter. In addition we fix the tst-mallocalign1.c
test that uses the MALLOC_ALIGN_MASK internal to malloc. Since it's
been moved to 'malloc-alignment.h' it makes sense to include this
header into the test source.
2026-05-07 13:43:10 +01:00
Yury Khrustalev 884f8f9325 malloc: Remove currently broken memory tagging
Remove AArch64-specific code, that is currently broken, from the
core malloc implementation.
2026-05-07 13:43:10 +01:00
Yury Khrustalev f33b8e0df5 malloc: Remove code conditional on USE_MTAG
Further malloc refactoring related to memory tagging.

Remove code that was only compiled when macro USE_MTAG was defined
except for the AArch64-specific assembly MTE code that is going to
be compiled unconditionally from now on.

As a result, we change 'mtag_mmap_flags' to 'extra_mmap_prot' that
is now always defined. Change of the name due to this being used
as part of PROT options in mmap syscalls rather than part of flags.

Remove 'mtag_enabled' that would become compile-time false. Also
remove any code that would never be compiled when 'mtag_enabled'
is false.
2026-05-07 13:43:10 +01:00
Yury Khrustalev d59e08f09b configure: remove --enable-memory-tagging option
Remove the '--enable-memory-tagging' configure option along with
all associated variables and macros.

This includes:
 - Removing the 'glibc.mem.tagging' tunable.
 - Removing parts of the configure script that processes the
   '--enable-memory-tagging' configure option.
 - Remove the 'memory-tagging' makefile variable.
 - Remove the USE_MTAG macro definition. Code that is conditionally
   compiled when this macro is defined is not removed at this stage
   and will be dealt with in the following commits.

Also, update documentation accordingly.
2026-05-07 13:43:10 +01:00
Yury Khrustalev 7ca145fabe malloc: introduce ifuncs for malloc functions
Introduce ifuncs and resolvers for functions pertinent to the
malloc interface.

In order to do this, we first rename all core implementations by
adding the '_core' suffix. These functions are supposed to be strictly
internal and must not be used beyond the malloc code. For example, the
__libc_malloc() function becomes __libc_malloc_core() and instead
__libc_malloc() becomes an ifunc with a corresponding resolver that
may return '__libc_malloc_core' as the implementation.

Each ifunc comes with a generic resolver that will be used unless a
target overrides the resolvers in its sysdeps directory. All resolvers
are supposed to be overridden together, and the logic of the generic
resolvers should be replicated by the target-specific ones.

This patch contains, as an example, aarch64-specific resolvers. At this
moment they are identical to the generic ones but in the future they can
be changed to support for features, e.g. to handle memory tagging.

Corresponding aliases are moved next to the ifuncs as well.

On targets that do not support ifuncs we alias externally visible
functions as well as the __libc_* functions to their respective
*_core symbols.
2026-05-07 13:43:10 +01:00
Yury Khrustalev 1b27e7732f support: add support_ptr_after_free
Some tests use pointers after the associated memory has been freed.
On targets that support memory tagging, using such pointers even
for test purposes might be impossible. To work around this, we add
new function that would allow to clear a pointer in a target-specific
way.

We modify 3 relevant malloc tests: tst-malloc-backtrace, tst-tcfree3,
and tst-safe-linking.
2026-05-07 13:43:07 +01:00
677 changed files with 6188 additions and 18750 deletions
+6
View File
@@ -158,6 +158,12 @@ passed to 'configure'. For example:
Don't build libraries with profiling information. You may want to
use this option if you don't plan to do profiling.
'--enable-static-nss'
Compile static versions of the NSS (Name Service Switch) libraries.
This is not recommended because it defeats the purpose of NSS; a
program linked statically with the NSS libraries cannot be
dynamically reconfigured to use a different name database.
'--enable-hardcoded-path-in-tests'
By default, dynamic tests are linked to run with the installed C
library. This option hardcodes the newly built C library path in
-28
View File
@@ -9,15 +9,6 @@ Version 2.44
Major new features:
* A new tunable, glibc.elf.thp, is added to map read-only segments with
Transparent Huge Pages (THP) if THP isn't disable in kernel. When
glibc.elf.thp is set to 1, malloc uses the actual kernel THP mode
instead of defaulting to madvise mode and madvise_thp will stop issuing
MADV_HUGEPAGE if kernel THP mode is always.
* THP page size in malloc is capped to MAX_THP_PAGESIZE. If THP page
size is above MAX_THP_PAGESIZE, THP in malloc is disabled.
* Additional optimized and correctly rounded mathematical functions have
been imported from the CORE-MATH project, in particular cosh, sinh, and
tanh.
@@ -27,16 +18,6 @@ Major new features:
* New locale added: hrx_BR (Hunsrik language spoken in Brazil).
* Static PIE is now supported for arm-*-linux-gnueabi. It requires toolchain
support to correctly set the expected linker options.
* On AArch64 targets that support Guarded Control Stack extension all GCS
operations (including status, write on shadow stack, and push to shadow
stack) are locked after enabling GCS with ENFORCED or OVERRIDE GCS policy.
When a GCS operation is locked, a program cannot change this operation
status via prctl syscall. This prevents disabling or corrupting GCS
shadow stack during runtime.
Deprecated and removed features, and other changes affecting compatibility:
* Although malloc and related functions currently return pointers
@@ -48,15 +29,6 @@ Deprecated and removed features, and other changes affecting compatibility:
* The s390-linux-gnu (31bit) configuration is no longer supported.
* The --enable-memory-tagging configure option has been removed.
The corresponding AArch64-specific functionality that was previously
activated by this flag has been removed as well.
* The --enable-static-nss configure option has been removed. It had no
effect on the build since the NSS reorganization in glibc 2.33; its only
remaining behavior was to suppress the link-time warnings on the NSS
interface functions in libc.a, which are now emitted unconditionally.
Changes to build and runtime requirements:
[Add changes to build and runtime requirements here]
+2 -4
View File
@@ -288,8 +288,7 @@ $(addprefix $(objpfx),$(binaries-malloc-check-tests)): %-malloc-check: %.o \
endif
ifneq "$(strip $(binaries-malloc-hugetlb1-tests))" ""
$(addprefix $(objpfx),$(binaries-malloc-hugetlb1-tests)): \
%-malloc-hugetlb1: %-malloc-hugetlb1.o \
$(addprefix $(objpfx),$(binaries-malloc-hugetlb1-tests)): %-malloc-hugetlb1: %.o \
$(link-extra-libs-tests) \
$(sort $(filter $(common-objpfx)lib%,$(link-libc))) \
$(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
@@ -297,8 +296,7 @@ $(addprefix $(objpfx),$(binaries-malloc-hugetlb1-tests)): \
endif
ifneq "$(strip $(binaries-malloc-hugetlb2-tests))" ""
$(addprefix $(objpfx),$(binaries-malloc-hugetlb2-tests)): \
%-malloc-hugetlb2: %-malloc-hugetlb2.o \
$(addprefix $(objpfx),$(binaries-malloc-hugetlb2-tests)): %-malloc-hugetlb2: %.o \
$(link-extra-libs-tests) \
$(sort $(filter $(common-objpfx)lib%,$(link-libc))) \
$(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
+7 -529
View File
@@ -113,7 +113,6 @@ gnulib:
stdlib/setenv.c
stdlib/strtoll.c
stdlib/strtoul.c
stdlib/tst-stdc_rotate_left.c
stdlib/tst-stdc_rotate_right.c
# Merged from gnulib 2014-6-26, needs merge back
string/memchr.c
@@ -152,7 +151,9 @@ gnulib:
time/strptime.c
time/timegm.c
# Synced with GNU gettext 1.0 (2026-05-18), through gettext commit 2ebbdd0e2.
# The last merge was 2014-12-11 and merged gettext 0.19.3 into glibc with a
# patch submitted to the gettext mailing list for changes that could be merged
# back.
#
# This commit was omitted from the merge as it does not appear to be compatible
# with how glibc expects things to work:
@@ -169,7 +170,6 @@ gettext:
intl/dcngettext.c
intl/dgettext.c
intl/dngettext.c
intl/eval-plural.h
intl/explodename.c
intl/finddomain.c
intl/gettext.c
@@ -211,9 +211,8 @@ unicode:
# The following files are shared with the upstream tzcode project and must be
# updated regularly to stay in sync with the upstream releases.
#
# Currently synced to TZDB 2026b, distributed at:
# https://github.com/eggert/tz/releases/tag/2026b
# https://data.iana.org/time-zones/releases/tzdb-2026b.tar.lz
# Currently synced to TZDB 2024b, announced and distributed here:
# https://github.com/eggert/tz/releases/tag/2024b
tzcode:
timezone/private.h
timezone/tzfile.h
@@ -258,11 +257,11 @@ tzdata:
# FLT_EVAL_METHOD equal to 2 (i386). Additionally, extra optimizations
# are applied to share the internal data table across different
# implementations.
#
#
# The project is distribute here:
# https://gitlab.inria.fr/core-math/core-math/
core-math:
# src/binary64/acosh/acosh.c, revision 887cab6f
# src/binary64/acosh/acosh.c, revision 1bd85b89
sysdeps/ieee754/dbl-64/e_acosh.c
# src/binary64/atanh/atanh.c, revision 532e37dc
sysdeps/ieee754/dbl-64/e_atanh.c
@@ -332,524 +331,3 @@ core-math:
sysdeps/ieee754/flt-32/s_tanhf.c
# src/binary32/tanpi/tanpif.c, revision 3bbf907
sysdeps/ieee754/flt-32/s_tanpif.c
# The following files are imported from the Arm Optimized-Routines project,
# with adjustments made to follow glibc code style and integrate with
# glibc-specific infrastructure where needed.
#
# The project is distributed here:
# https://github.com/ARM-software/optimized-routines/
arm-optimized-routines:
# Math routines
# math/math_config.h, revision 712aa21
sysdeps/ieee754/flt-32/math_config.h
# math/math_config.h, revision 712aa21
sysdeps/ieee754/dbl-64/math_config.h
# math/math_err.c, revision 189dfef
sysdeps/ieee754/dbl-64/math_err.c
# math/math_errf.c, revision 189dfef
sysdeps/ieee754/flt-32/math_errf.c
# math/cosf.c, revision aec783d
sysdeps/ieee754/flt-32/s_cosf.c
# math/exp2f.c, revision 712aa21
sysdeps/ieee754/flt-32/e_exp2f.c
# math/exp2f_data.c, revision 189dfef
sysdeps/ieee754/flt-32/e_exp2f_data.c
# math/expf.c, revision 712aa21
sysdeps/ieee754/flt-32/e_expf.c
# math/log2f.c, revision 712aa21
sysdeps/ieee754/flt-32/e_log2f.c
# math/log2f_data.c, revision 189dfef
sysdeps/ieee754/flt-32/e_log2f_data.c
# math/logf.c, revision 712aa21
sysdeps/ieee754/flt-32/e_logf.c
# math/logf_data.c, revision c0136f1
sysdeps/ieee754/flt-32/e_logf_data.c
# math/powf.c, revision 712aa21
sysdeps/ieee754/flt-32/e_powf.c
# math/powf_log2_data.c, revision 189dfef
sysdeps/ieee754/flt-32/e_powf_log2_data.c
# math/sincosf.c, revision af29d39
sysdeps/ieee754/flt-32/s_sincosf.c
# math/sincosf.h, revision aed553c
sysdeps/ieee754/flt-32/s_sincosf.h
# math/sincosf_data.c, revision 189dfef
sysdeps/ieee754/flt-32/s_sincosf_data.c
# math/sinf.c, revision aec783d
sysdeps/ieee754/flt-32/s_sinf.c
# math/exp.c, revision 712aa21
sysdeps/ieee754/dbl-64/e_exp.c
# math/exp10.c, revision 712aa21
sysdeps/ieee754/dbl-64/e_exp10.c
# math/exp2.c, revision 712aa21
sysdeps/ieee754/dbl-64/e_exp2.c
# math/exp_data.c, revision c013701
sysdeps/ieee754/dbl-64/e_exp_data.c
# math/log.c, revision 712aa21
sysdeps/ieee754/dbl-64/e_log.c
# math/log2.c, revision 712aa21
sysdeps/ieee754/dbl-64/e_log2.c
# math/log2_data.c, revision 189dfef
sysdeps/ieee754/dbl-64/e_log2_data.c
# math/log_data.c, revision 189dfef
sysdeps/ieee754/dbl-64/e_log_data.c
# math/pow.c, revision 712aa21
sysdeps/ieee754/dbl-64/e_pow.c
# math/pow_log_data.c, revision 189dfef
sysdeps/ieee754/dbl-64/e_pow_log_data.c
# Mathvec routines
# math/math_config.h, revision 712aa21
sysdeps/aarch64/fpu/vecmath_config.h
# math/aarch64/v_erf_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/erf_data.c
# math/aarch64/v_erfc_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/erfc_data.c
# math/aarch64/v_erfcf_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/erfcf_data.c
# math/aarch64/v_erff_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/erff_data.c
# math/aarch64/v_exp_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_exp_data.c
# math/aarch64/v_exp_tail_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_exp_tail_data.c
# math/aarch64/v_log10_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_log10_data.c
# math/aarch64/v_log2_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_log2_data.c
# math/aarch64/v_log_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_log_data.c
# math/aarch64/v_pow_exp_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_pow_exp_data.c
# math/aarch64/v_pow_log_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_pow_log_data.c
# math/aarch64/v_powf_data.c, revision 0a6ab6d
sysdeps/aarch64/fpu/v_powf_data.c
# math/aarch64/sve/acos.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/acos.c
# math/aarch64/sve/acosf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/acosf.c
# math/aarch64/sve/acosh.c, revision 8b665af
sysdeps/aarch64/fpu/sve/acosh.c
# math/aarch64/sve/acoshf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/acoshf.c
# math/aarch64/sve/acospi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/acospi.c
# math/aarch64/sve/acospif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/acospif.c
# math/aarch64/sve/asin.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/asin.c
# math/aarch64/sve/asinf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/asinf.c
# math/aarch64/sve/asinh.c, revision 9bf4cfc
sysdeps/aarch64/fpu/sve/asinh.c
# math/aarch64/sve/asinhf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/asinhf.c
# math/aarch64/sve/asinpi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/asinpi.c
# math/aarch64/sve/asinpif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/asinpif.c
# math/aarch64/sve/atan.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atan.c
# math/aarch64/sve/atan2.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atan2.c
# math/aarch64/sve/atan2f.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atan2f.c
# math/aarch64/sve/atan2pi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atan2pi.c
# math/aarch64/sve/atan2pif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atan2pif.c
# math/aarch64/sve/atanf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atanf.c
# math/aarch64/sve/atanh.c, revision 29c7342
sysdeps/aarch64/fpu/sve/atanh.c
# math/aarch64/sve/atanhf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atanhf.c
# math/aarch64/sve/atanpi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atanpi.c
# math/aarch64/sve/atanpif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/atanpif.c
# math/aarch64/sve/cbrt.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/cbrt.c
# math/aarch64/sve/cbrtf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/cbrtf.c
# math/aarch64/sve/cos.c, revision 99cbbad
sysdeps/aarch64/fpu/sve/cos.c
# math/aarch64/sve/cosf.c, revision 25aa012
sysdeps/aarch64/fpu/sve/cosf.c
# math/aarch64/sve/cosh.c, revision 242a017
sysdeps/aarch64/fpu/sve/cosh.c
# math/aarch64/sve/coshf.c, revision 2fff60c
sysdeps/aarch64/fpu/sve/coshf.c
# math/aarch64/sve/cospi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/cospi.c
# math/aarch64/sve/cospif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/cospif.c
# math/aarch64/sve/erf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/erf.c
# math/aarch64/sve/erfc.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/erfc.c
# math/aarch64/sve/erfcf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/erfcf.c
# math/aarch64/sve/erff.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/erff.c
# math/aarch64/sve/exp.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp.c
# math/aarch64/sve/exp10.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp10.c
# math/aarch64/sve/exp10f.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp10f.c
# math/aarch64/sve/exp10m1.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp10m1.c
# math/aarch64/sve/exp10m1f.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp10m1f.c
# math/aarch64/sve/exp2.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp2.c
# math/aarch64/sve/exp2f.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp2f.c
# math/aarch64/sve/exp2m1.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp2m1.c
# math/aarch64/sve/exp2m1f.c, revision d664b69
sysdeps/aarch64/fpu/sve/exp2m1f.c
# math/aarch64/sve/expf.c, revision 47044a5
sysdeps/aarch64/fpu/sve/expf.c
# math/aarch64/sve/expm1.c, revision d664b69
sysdeps/aarch64/fpu/sve/expm1.c
# math/aarch64/sve/expm1f.c, revision d664b69
sysdeps/aarch64/fpu/sve/expm1f.c
# math/aarch64/sve/hypot.c, revision 74d699d
sysdeps/aarch64/fpu/sve/hypot.c
# math/aarch64/sve/hypotf.c, revision 74d699d
sysdeps/aarch64/fpu/sve/hypotf.c
# math/aarch64/sve/log.c, revision 91b052e
sysdeps/aarch64/fpu/sve/log.c
# math/aarch64/sve/log10.c, revision 91b052e
sysdeps/aarch64/fpu/sve/log10.c
# math/aarch64/sve/log10f.c, revision 8ca4cc0
sysdeps/aarch64/fpu/sve/log10f.c
# math/aarch64/sve/log10p1.c, revision 166744d
sysdeps/aarch64/fpu/sve/log10p1.c
# math/aarch64/sve/log10p1f.c, revision 8124d30
sysdeps/aarch64/fpu/sve/log10p1f.c
# math/aarch64/sve/log1p.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/log1p.c
# math/aarch64/sve/log1pf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/log1pf.c
# math/aarch64/sve/log2.c, revision 91b052e
sysdeps/aarch64/fpu/sve/log2.c
# math/aarch64/sve/log2f.c, revision 8ca4cc0
sysdeps/aarch64/fpu/sve/log2f.c
# math/aarch64/sve/log2p1.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/log2p1.c
# math/aarch64/sve/log2p1f.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/log2p1f.c
# math/aarch64/sve/logf.c, revision 8ca4cc0
sysdeps/aarch64/fpu/sve/logf.c
# math/aarch64/sve/pow.c, revision 2c38e83
sysdeps/aarch64/fpu/sve/pow.c
# math/aarch64/sve/powf.c, revision 2c38e83
sysdeps/aarch64/fpu/sve/powf.c
# math/aarch64/sve/powr.c, revision 30da949
sysdeps/aarch64/fpu/sve/powr.c
# math/aarch64/sve/powrf.c, revision e2bca42
sysdeps/aarch64/fpu/sve/powrf.c
# math/aarch64/sve/rsqrt.c, revision 4fc7374
sysdeps/aarch64/fpu/sve/rsqrt.c
# math/aarch64/sve/rsqrtf.c, revision b1bc8ba
sysdeps/aarch64/fpu/sve/rsqrtf.c
# math/aarch64/sve/sin.c, revision 99cbbad
sysdeps/aarch64/fpu/sve/sin.c
# math/aarch64/sve/sinf.c, revision 25aa012
sysdeps/aarch64/fpu/sve/sinf.c
# math/aarch64/sve/sinh.c, revision 242a017
sysdeps/aarch64/fpu/sve/sinh.c
# math/aarch64/sve/sinhf.c, revision 2fff60c
sysdeps/aarch64/fpu/sve/sinhf.c
# math/aarch64/sve/sinpi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/sinpi.c
# math/aarch64/sve/sinpif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/sinpif.c
# math/aarch64/sve/tan.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/tan.c
# math/aarch64/sve/tanf.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/tanf.c
# math/aarch64/sve/tanh.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/tanh.c
# math/aarch64/sve/tanhf.c, revision 2fff60c
sysdeps/aarch64/fpu/sve/tanhf.c
# math/aarch64/sve/tanpi.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/tanpi.c
# math/aarch64/sve/tanpif.c, revision 1aad38d
sysdeps/aarch64/fpu/sve/tanpif.c
# math/aarch64/sve/sv_exp_special_inline.h, revision d664b69
sysdeps/aarch64/fpu/sve/sv_exp_special_inline.h
# math/aarch64/sve/sv_expf_inline.h, revision ba35b32
sysdeps/aarch64/fpu/sve/sv_expf_inline.h
# math/aarch64/sve/sv_expf_special_inline.h, revision d664b69
sysdeps/aarch64/fpu/sve/sv_expf_special_inline.h
# math/aarch64/sve/sv_expm1f_inline.h, revision 78cd003
sysdeps/aarch64/fpu/sve/sv_expm1f_inline.h
# math/aarch64/sve/sv_log1p_inline.h, revision 7d08237
sysdeps/aarch64/fpu/sve/sv_log1p_inline.h
# math/aarch64/sve/sv_log1pf_inline.h, revision a386525
sysdeps/aarch64/fpu/sve/sv_log1pf_inline.h
# math/aarch64/sve/sv_pow_inline.h, revision 30da949
sysdeps/aarch64/fpu/sve/sv_pow_inline.h
# math/aarch64/sve/sv_powf_inline.h, revision 30da949
sysdeps/aarch64/fpu/sve/sv_powf_inline.h
# math/aarch64/sve/sv_poly_f32.h, revision 5d5e6e6
sysdeps/aarch64/fpu/sve/poly_sve_f32.h
# math/aarch64/sve/sv_poly_f64.h, revision 5d5e6e6
sysdeps/aarch64/fpu/sve/poly_sve_f64.h
# math/aarch64/sve/sv_poly_generic.h, revision 5d5e6e6
sysdeps/aarch64/fpu/sve/poly_sve_generic.h
# math/aarch64/sve/sv_math.h, revision 7361ef6
sysdeps/aarch64/fpu/sve/sv_math.h
# math/aarch64/sve/sv_trig_fallback.h, revision 99cbbad
sysdeps/aarch64/fpu/sve/sv_trig_fallback.h
# math/aarch64/sve/sv_trigf_fallback.h, revision 25aa012
sysdeps/aarch64/fpu/sve/sv_trigf_fallback.h
# math/aarch64/advsimd/acos.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/acos.c
# math/aarch64/advsimd/acosf.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/acosf.c
# math/aarch64/advsimd/acosh.c, revision 188e75a
sysdeps/aarch64/fpu/advsimd/acosh.c
# math/aarch64/advsimd/acoshf.c, revision af3851e
sysdeps/aarch64/fpu/advsimd/acoshf.c
# math/aarch64/advsimd/acospi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/acospi.c
# math/aarch64/advsimd/acospif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/acospif.c
# math/aarch64/advsimd/asin.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/asin.c
# math/aarch64/advsimd/asinf.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/asinf.c
# math/aarch64/advsimd/asinh.c, revision bac1b4f
sysdeps/aarch64/fpu/advsimd/asinh.c
# math/aarch64/advsimd/asinhf.c, revision af3851e
sysdeps/aarch64/fpu/advsimd/asinhf.c
# math/aarch64/advsimd/asinpi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/asinpi.c
# math/aarch64/advsimd/asinpif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/asinpif.c
# math/aarch64/advsimd/atan.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atan.c
# math/aarch64/advsimd/atan2.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atan2.c
# math/aarch64/advsimd/atan2f.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atan2f.c
# math/aarch64/advsimd/atan2pi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atan2pi.c
# math/aarch64/advsimd/atan2pif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atan2pif.c
# math/aarch64/advsimd/atanf.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atanf.c
# math/aarch64/advsimd/atanh.c, revision 281cb3a
sysdeps/aarch64/fpu/advsimd/atanh.c
# math/aarch64/advsimd/atanhf.c, revision d852158
sysdeps/aarch64/fpu/advsimd/atanhf.c
# math/aarch64/advsimd/atanpi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atanpi.c
# math/aarch64/advsimd/atanpif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/atanpif.c
# math/aarch64/advsimd/cbrt.c, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/cbrt.c
# math/aarch64/advsimd/cbrtf.c, revision 6c74650
sysdeps/aarch64/fpu/advsimd/cbrtf.c
# math/aarch64/advsimd/cos.c, revision 677a12b
sysdeps/aarch64/fpu/advsimd/cos.c
# math/aarch64/advsimd/cosf.c, revision 15c35d9
sysdeps/aarch64/fpu/advsimd/cosf.c
# math/aarch64/advsimd/cosh.c, revision 447bbaa
sysdeps/aarch64/fpu/advsimd/cosh.c
# math/aarch64/advsimd/coshf.c, revision 45237ba
sysdeps/aarch64/fpu/advsimd/coshf.c
# math/aarch64/advsimd/cospi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/cospi.c
# math/aarch64/advsimd/cospif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/cospif.c
# math/aarch64/advsimd/erf.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/erf.c
# math/aarch64/advsimd/erfc.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/erfc.c
# math/aarch64/advsimd/erfcf.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/erfcf.c
# math/aarch64/advsimd/erff.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/erff.c
# math/aarch64/advsimd/exp.c, revision dce5dab
sysdeps/aarch64/fpu/advsimd/exp.c
# math/aarch64/advsimd/exp10.c, revision dce5dab
sysdeps/aarch64/fpu/advsimd/exp10.c
# math/aarch64/advsimd/exp10f.c, revision 69d38dd
sysdeps/aarch64/fpu/advsimd/exp10f.c
# math/aarch64/advsimd/exp10m1.c, revision dce5dab
sysdeps/aarch64/fpu/advsimd/exp10m1.c
# math/aarch64/advsimd/exp10m1f.c, revision a5092e8
sysdeps/aarch64/fpu/advsimd/exp10m1f.c
# math/aarch64/advsimd/exp2.c, revision dce5dab
sysdeps/aarch64/fpu/advsimd/exp2.c
# math/aarch64/advsimd/exp2f.c, revision 69d38dd
sysdeps/aarch64/fpu/advsimd/exp2f.c
# math/aarch64/advsimd/exp2m1.c, revision dce5dab
sysdeps/aarch64/fpu/advsimd/exp2m1.c
# math/aarch64/advsimd/exp2m1f.c, revision a5092e8
sysdeps/aarch64/fpu/advsimd/exp2m1f.c
# math/aarch64/advsimd/expf.c, revision 69d38dd
sysdeps/aarch64/fpu/advsimd/expf.c
# math/aarch64/advsimd/expm1.c, revision dce5dab
sysdeps/aarch64/fpu/advsimd/expm1.c
# math/aarch64/advsimd/expm1f.c, revision a5092e8
sysdeps/aarch64/fpu/advsimd/expm1f.c
# math/aarch64/advsimd/hypot.c, revision 09f8d05
sysdeps/aarch64/fpu/advsimd/hypot.c
# math/aarch64/advsimd/hypotf.c, revision 99dff4f
sysdeps/aarch64/fpu/advsimd/hypotf.c
# math/aarch64/advsimd/log.c, revision 0345857
sysdeps/aarch64/fpu/advsimd/log.c
# math/aarch64/advsimd/log10.c, revision 77aff35
sysdeps/aarch64/fpu/advsimd/log10.c
# math/aarch64/advsimd/log10f.c, revision ff1596f
sysdeps/aarch64/fpu/advsimd/log10f.c
# math/aarch64/advsimd/log10p1.c, revision 7fc8510
sysdeps/aarch64/fpu/advsimd/log10p1.c
# math/aarch64/advsimd/log10p1f.c, revision 58cd796
sysdeps/aarch64/fpu/advsimd/log10p1f.c
# math/aarch64/advsimd/log1p.c, revision 355a985
sysdeps/aarch64/fpu/advsimd/log1p.c
# math/aarch64/advsimd/log1pf.c, revision 5cf8408
sysdeps/aarch64/fpu/advsimd/log1pf.c
# math/aarch64/advsimd/log2.c, revision b8deb33
sysdeps/aarch64/fpu/advsimd/log2.c
# math/aarch64/advsimd/log2f.c, revision b98f80d
sysdeps/aarch64/fpu/advsimd/log2f.c
# math/aarch64/advsimd/log2p1.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/log2p1.c
# math/aarch64/advsimd/log2p1f.c, revision 58cd796
sysdeps/aarch64/fpu/advsimd/log2p1f.c
# math/aarch64/advsimd/logf.c, revision 1b15b12
sysdeps/aarch64/fpu/advsimd/logf.c
# math/aarch64/advsimd/finite_pow.h, revision 30da949
sysdeps/aarch64/fpu/advsimd/finite_pow.h
# math/pow_common.h, revision 30da949
sysdeps/aarch64/fpu/pow_common.h
# math/powf_common.h, revision 30da949
sysdeps/aarch64/fpu/powf_common.h
# math/aarch64/advsimd/pow.c, revision 30da949
sysdeps/aarch64/fpu/advsimd/pow.c
# math/aarch64/advsimd/powf.c, revision 30da949
sysdeps/aarch64/fpu/advsimd/powf.c
# math/aarch64/advsimd/powr.c, revision 30da949
sysdeps/aarch64/fpu/advsimd/powr.c
# math/aarch64/advsimd/powrf.c, revision e2bca42
sysdeps/aarch64/fpu/advsimd/powrf.c
# math/aarch64/advsimd/rsqrt.c, revision 0945eab
sysdeps/aarch64/fpu/advsimd/rsqrt.c
# math/aarch64/advsimd/rsqrtf.c, revision 88e9cc9
sysdeps/aarch64/fpu/advsimd/rsqrtf.c
# math/aarch64/advsimd/sin.c, revision 677a12b
sysdeps/aarch64/fpu/advsimd/sin.c
# math/aarch64/advsimd/sinf.c, revision 15c35d9
sysdeps/aarch64/fpu/advsimd/sinf.c
# math/aarch64/advsimd/sinh.c, revision 447bbaa
sysdeps/aarch64/fpu/advsimd/sinh.c
# math/aarch64/advsimd/sinhf.c, revision 45237ba
sysdeps/aarch64/fpu/advsimd/sinhf.c
# math/aarch64/advsimd/sinpi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/sinpi.c
# math/aarch64/advsimd/sinpif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/sinpif.c
# math/aarch64/advsimd/tan.c, revision a8bbb87
sysdeps/aarch64/fpu/advsimd/tan.c
# math/aarch64/advsimd/tanf.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/tanf.c
# math/aarch64/advsimd/tanh.c, revision 447bbaa
sysdeps/aarch64/fpu/advsimd/tanh.c
# math/aarch64/advsimd/tanhf.c, revision 45237ba
sysdeps/aarch64/fpu/advsimd/tanhf.c
# math/aarch64/advsimd/tanpi.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/tanpi.c
# math/aarch64/advsimd/tanpif.c, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/tanpif.c
# math/aarch64/advsimd/v_exp_special_case_inline.h, revision dce5dab
sysdeps/aarch64/fpu/advsimd/v_exp_special_case_inline.h
# math/aarch64/advsimd/v_expf_inline.h, revision 747e0e1
sysdeps/aarch64/fpu/advsimd/v_expf_inline.h
# math/aarch64/advsimd/v_expf_special_inline.h, revision a5092e8
sysdeps/aarch64/fpu/advsimd/v_expf_special_inline.h
# math/aarch64/advsimd/v_expm1_inline.h, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/v_expm1_inline.h
# math/aarch64/advsimd/v_expm1f_inline.h, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/v_expm1f_inline.h
# math/aarch64/advsimd/v_log1p_inline.h, revision 1aad38d
sysdeps/aarch64/fpu/advsimd/v_log1p_inline.h
# math/aarch64/advsimd/v_log1pf_inline.h, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/v_log1pf_inline.h
# math/aarch64/advsimd/v_pow_inline.h, revision efde0c7
sysdeps/aarch64/fpu/advsimd/v_pow_inline.h
# math/aarch64/advsimd/v_powf_inline.h, revision 5b06a16
sysdeps/aarch64/fpu/advsimd/v_powf_inline.h
# math/aarch64/advsimd/v_powrf_inline.h, revision 30da949
sysdeps/aarch64/fpu/advsimd/v_powrf_inline.h
# math/aarch64/advsimd/v_poly_f32.h, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/poly_advsimd_f32.h
# math/aarch64/advsimd/v_poly_f64.h, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/poly_advsimd_f64.h
# math/poly_generic.h, revision 0a6ab6d
sysdeps/aarch64/fpu/advsimd/poly_generic.h
# math/aarch64/advsimd/v_math.h, revision 1829557
sysdeps/aarch64/fpu/advsimd/v_math.h
# math/aarch64/advsimd/v_trig_fallback.h, revision 677a12b
sysdeps/aarch64/fpu/advsimd/v_trig_fallback.h
# math/aarch64/advsimd/v_trigf_fallback.h, revision 15c35d9
sysdeps/aarch64/fpu/advsimd/v_trigf_fallback.h
# String routines
# string/aarch64/__mtag_tag_region.S, revision 41e5ae5
sysdeps/aarch64/__mtag_tag_region.S
# string/aarch64/__mtag_tag_zero_region.S, revision 41e5ae5
sysdeps/aarch64/__mtag_tag_zero_region.S
# string/aarch64/memchr-mte.S, revision 41e5ae5
sysdeps/aarch64/memchr.S
# string/aarch64/memcmp.S, revision 41e5ae5
sysdeps/aarch64/memcmp.S
# string/aarch64/memcpy-advsimd.S, revision 41e5ae5
sysdeps/aarch64/memcpy.S
# string/aarch64/memcpy-mops.S, revision 41e5ae5
sysdeps/aarch64/multiarch/memcpy_mops.S
# string/aarch64/memcpy-sve.S, revision 71e3640
sysdeps/aarch64/multiarch/memcpy_sve.S
# string/aarch64/memmove-mops.S, revision 41e5ae5
sysdeps/aarch64/multiarch/memmove_mops.S
# string/aarch64/memrchr.S, revision 41e5ae5
sysdeps/aarch64/memrchr.S
# string/aarch64/memset-mops.S, revision 41e5ae5
sysdeps/aarch64/multiarch/memset_mops.S
# string/aarch64/memset-sve.S, revision 0f71101
sysdeps/aarch64/multiarch/memset_sve_zva64.S
# string/aarch64/memset.S, revision 098df8c
sysdeps/aarch64/memset.S
# string/aarch64/stpcpy.S, revision 189dfef
sysdeps/aarch64/stpcpy.S
# string/aarch64/strchr-mte.S, revision 41e5ae5
sysdeps/aarch64/strchr.S
# string/aarch64/strchrnul-mte.S, revision 41e5ae5
sysdeps/aarch64/strchrnul.S
# string/aarch64/strcmp.S, revision 41e5ae5
sysdeps/aarch64/strcmp.S
# string/aarch64/strcpy.S, revision 41e5ae5
sysdeps/aarch64/strcpy.S
# string/aarch64/strlen-mte.S, revision 41e5ae5
sysdeps/aarch64/strlen.S
# string/aarch64/strncmp.S, revision 41e5ae5
sysdeps/aarch64/strncmp.S
# string/aarch64/strnlen.S, revision 41e5ae5
sysdeps/aarch64/strnlen.S
# string/aarch64/strrchr-mte.S, revision 41e5ae5
sysdeps/aarch64/strrchr.S
# string/arm/memchr.S, revision 1eb5d7c
sysdeps/arm/armv6t2/memchr.S
# string/arm/memcpy.S, revision 1eb5d7c
sysdeps/arm/armv7/multiarch/memcpy_impl.S
# string/arm/strcmp.S, revision 1eb5d7c
sysdeps/arm/armv7/strcmp.S
# string/arm/strlen-armv6t2.S, revision 1eb5d7c
sysdeps/arm/armv6t2/strlen.S
+6 -6
View File
@@ -1,11 +1,11 @@
Buffer overread in ns_printrrf with corrupted RDATA field
The deprecated functions ns_printrrf, ns_printrr and fp_nquery in the
GNU C Library version 2.0.1 to version 2.43 fail to validate the RDATA
content against the RDATA length in a DNS response when processing A6,
CERT, LOC, TKEY or TSIG records, which may allow an attacker to craft a
DNS response, causing a target application to crash or read
uninitialized memory.
GNU C Library version 2.2 and newer fail to validate the RDATA content
against the RDATA length in a DNS response when processing LOC, CERT,
TKEY or TSIG records, which may allow an attacker to craft a DNS
response, causing a target application to crash or read uninitialized
memory.
These functions are for application debugging only and hence not in the
path of code executed by the DNS resolver. Further, they have been
@@ -15,4 +15,4 @@ interfaces since they may be removed in future versions.
CVE-Id: CVE-2026-6238
Public-Date: 2026-04-11
Vulnerable-Commit: ee188d555b8c32ad9704a7440cab400af967292f (1.90)
Vulnerable-Commit: b43b13ac2544b11f35be301d1589b51a8473e32b (2.2)
-20
View File
@@ -1,20 +0,0 @@
Potential stack-based buffer clash during tilde expansion in wordexp
Calling wordexp with a tilde (~) followed by an overly long username
in the GNU C Library version 2.2.3 to 2.43 may lead to a stack buffer
clash.
When expanding paths that begin with a tilde (~) followed by a username, the
internal parse_tilde function extracts the username to determine the user's
home directory. The implementation allocates memory for this username directly
on the stack using the strndupa macro. Because the size of this allocation
was determined by the length of the user-supplied input without any bounds
checks, passing an excessively long username e.g. thousands of characters,
forces the thread to exhaust its stack space. Thus if an application passes
untrusted, attacker-controlled input to the wordexp function, an attacker
can trigger a stack clash.
CVE-Id: CVE-2026-6791
Public-Date: 2026-06-22
Vulnerable-Commit: 344af000e1d6e9c7882b9bc48e71cb3f1b5fc03c (2.2.3-114)
Reported-by: storm
+3 -3
View File
@@ -32,6 +32,9 @@
/* Define if _Unwind_Find_FDE should be exported from glibc. */
#undef EXPORT_UNWIND_FIND_FDE
/* Define if static NSS modules are wanted. */
#undef DO_STATIC_NSS
/* Assume that the compiler supports __builtin_expect.
This macro is necessary for proper compilation of code
shared between GNU libc and GNU gettext projects. */
@@ -167,9 +170,6 @@
/* Mach specific: define if the `vm_set_size_limit' RPC is available. */
#undef HAVE_MACH_VM_SET_SIZE_LIMIT
/* Mach specific: define if the `task_max_priority' RPC is available. */
#undef HAVE_MACH_TASK_MAX_PRIORITY
/* Mach/i386 specific: define if the `i386_io_perm_*' RPCs are available. */
#undef HAVE_I386_IO_PERM_MODIFY
+1
View File
@@ -81,6 +81,7 @@ mach-interface-list = @mach_interface_list@
# Configuration options.
build-shared = @shared@
build-profile = @profile@
build-static-nss = @static_nss@
cross-compiling = @cross_compiling@
force-install = @force_install@
build-nscd = @build_nscd@
Vendored
+60
View File
@@ -616,6 +616,7 @@ RELEASE
VERSION
mach_interface_list
DEFINES
static_nss
profile
libc_cv_multidir
test_enable_cet
@@ -803,6 +804,7 @@ enable_hardcoded_path_in_tests
enable_hidden_plt
enable_bind_now
enable_stack_protector
enable_static_nss
enable_force_install
enable_maintainer_mode
enable_kernel
@@ -1475,6 +1477,7 @@ Optional Features:
--enable-stack-protector=[yes|no|all|strong]
Use -fstack-protector[-all|-strong] to detect glibc
buffer overflows
--enable-static-nss build static NSS modules [default=no]
--disable-force-install don't force installation of files from this package,
even if they are older than the installed files
--enable-maintainer-mode
@@ -4611,6 +4614,21 @@ all|yes|no|strong) ;;
*) as_fn_error $? "Not a valid argument for --enable-stack-protector: \"$enable_stack_protector\"" "$LINENO" 5;;
esac
# Check whether --enable-static-nss was given.
if test ${enable_static_nss+y}
then :
enableval=$enable_static_nss; static_nss=$enableval
else case e in #(
e) static_nss=no ;;
esac
fi
if test x"$static_nss" = xyes || test x"$shared" = xno; then
static_nss=yes
printf "%s\n" "#define DO_STATIC_NSS 1" >>confdefs.h
fi
# Check whether --enable-force-install was given.
if test ${enable_force_install+y}
then :
@@ -7395,6 +7413,47 @@ printf "%s\n" "$libc_cv_fpie" >&6; }
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for -Wno-discarded-qualifiers" >&5
printf %s "checking for -Wno-discarded-qualifiers... " >&6; }
if test ${libc_cv_cflags_wno_discarded_qualifiers+y}
then :
printf %s "(cached) " >&6
else case e in #(
e) if { ac_try='${CC-cc} -Werror -Wno-discarded-qualifiers -xc /dev/null -S -o /dev/null'
{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
(eval $ac_try) 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; }
then :
libc_cv_cflags_wno_discarded_qualifiers=-Wno-discarded-qualifiers
else case e in #(
e) libc_cv_cflags_wno_discarded_qualifiers= ;;
esac
fi
if test -z "$libc_cv_cflags_wno_discarded_qualifiers"; then
if { ac_try='${CC-cc} -Werror -Wno-incompatible-pointer-types-discards-qualifiers -xc /dev/null -S -o /dev/null'
{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
(eval $ac_try) 2>&5
ac_status=$?
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; }
then :
libc_cv_cflags_wno_incompatible_pointer_type_discards_qualifiers=-Wno-incompatible-pointer-types-discards-qualifiers
else case e in #(
e) libc_cv_cflags_wno_incompatible_pointer_type_discards_qualifiers= ;;
esac
fi
libc_cv_cflags_wno_discarded_qualifiers=$libc_cv_cflags_wno_incompatible_pointer_type_discards_qualifiers
fi
;;
esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_cflags_wno_discarded_qualifiers" >&5
printf "%s\n" "$libc_cv_cflags_wno_discarded_qualifiers" >&6; }
config_vars="$config_vars
config-cflags-wno-discarded-qualifiers = $libc_cv_cflags_wno_discarded_qualifiers"
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GLOB_DAT reloc" >&5
printf %s "checking for GLOB_DAT reloc... " >&6; }
if test ${libc_cv_has_glob_dat+y}
@@ -9577,6 +9636,7 @@ libc_cv_multidir=`${CC-cc} $CFLAGS $CPPFLAGS -print-multi-directory`
VERSION=`sed -n -e 's/^#define VERSION "\([^"]*\)"/\1/p' < $srcdir/version.h`
RELEASE=`sed -n -e 's/^#define RELEASE "\([^"]*\)"/\1/p' < $srcdir/version.h`
+30
View File
@@ -272,6 +272,19 @@ all|yes|no|strong) ;;
*) AC_MSG_ERROR([Not a valid argument for --enable-stack-protector: "$enable_stack_protector"]);;
esac
dnl On some platforms we cannot use dynamic loading. We must provide
dnl static NSS modules.
AC_ARG_ENABLE([static-nss],
AS_HELP_STRING([--enable-static-nss],
[build static NSS modules @<:@default=no@:>@]),
[static_nss=$enableval],
[static_nss=no])
dnl Enable static NSS also if we build no shared objects.
if test x"$static_nss" = xyes || test x"$shared" = xno; then
static_nss=yes
AC_DEFINE(DO_STATIC_NSS)
fi
AC_ARG_ENABLE([force-install],
AS_HELP_STRING([--disable-force-install],
[don't force installation of files from this package, even if they are older than the installed files]),
@@ -1343,6 +1356,22 @@ LIBC_TRY_CC_OPTION([-fpie], [libc_cv_fpie=yes], [libc_cv_fpie=no])
AC_SUBST(libc_cv_fpie)
dnl Check how to suppress C23-qualifier-generic const warnings. GCC has
dnl -Wno-discarded-qualifiers, while clang -Wno-incompatible-pointer-types-discards-qualifiers
AC_CACHE_CHECK(for -Wno-discarded-qualifiers, libc_cv_cflags_wno_discarded_qualifiers, [dnl
LIBC_TRY_CC_OPTION([-Werror -Wno-discarded-qualifiers],
[libc_cv_cflags_wno_discarded_qualifiers=-Wno-discarded-qualifiers],
[libc_cv_cflags_wno_discarded_qualifiers=])
if test -z "$libc_cv_cflags_wno_discarded_qualifiers"; then
LIBC_TRY_CC_OPTION([-Werror -Wno-incompatible-pointer-types-discards-qualifiers],
[libc_cv_cflags_wno_incompatible_pointer_type_discards_qualifiers=-Wno-incompatible-pointer-types-discards-qualifiers],
[libc_cv_cflags_wno_incompatible_pointer_type_discards_qualifiers=])
libc_cv_cflags_wno_discarded_qualifiers=$libc_cv_cflags_wno_incompatible_pointer_type_discards_qualifiers
fi
])
LIBC_CONFIG_VAR([config-cflags-wno-discarded-qualifiers],
[$libc_cv_cflags_wno_discarded_qualifiers])
AC_CACHE_CHECK(for GLOB_DAT reloc,
libc_cv_has_glob_dat, [dnl
cat > conftest.c <<EOF
@@ -2187,6 +2216,7 @@ libc_cv_multidir=`${CC-cc} $CFLAGS $CPPFLAGS -print-multi-directory`
AC_SUBST(libc_cv_multidir)
AC_SUBST(profile)
AC_SUBST(static_nss)
AC_SUBST(DEFINES)
+26 -38
View File
@@ -44,20 +44,21 @@ extern void __libc_init_first (int argc, char **argv, char **envp);
#include <tls.h>
#ifndef SHARED
# include <dl-osinfo.h>
# include <dl-reseed-random.h>
# include <dl-symbol-redir-ifunc.h>
# ifndef THREAD_SET_STACK_GUARD
/* Only exported for architectures that don't store the stack guard canary
in thread local area. */
uintptr_t __stack_chk_guard attribute_relro;
# endif
# ifndef THREAD_SET_POINTER_GUARD
/* Only exported for architectures that don't store the pointer guard
value in thread local area. */
uintptr_t __pointer_chk_guard_local attribute_relro attribute_hidden;
# endif
#endif
#ifndef SHARED
# include <link.h>
# include <dl-irel.h>
# include <dl-symbol-redir-ifunc.h>
# ifdef ELF_MACHINE_IRELA
# define IREL_T ElfW(Rela)
@@ -204,7 +205,6 @@ call_fini (void *unused)
#endif /* !SHARED */
#include <libc-start.h>
#include <dl-exec-post.h>
STATIC int LIBC_START_MAIN (int (*main) (int, char **, char **
MAIN_AUXVEC_DECL),
@@ -268,22 +268,23 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
ARCH_INIT_CPU_FEATURES ();
/* Do static-pie self relocation for the non-IRELATIVE part after tunables
and cpu features are set up. IFUNC entries are deferred until after the
TCB and the stack-protector canary are usable, so that an instrumented
resolver does not fault. Before this point relocations must be
avoided. */
/* Do static pie self relocation after tunables and cpu features
are setup for ifunc resolvers. Before this point relocations
must be avoided. */
_dl_relocate_static_pie ();
/* Set up the TCB so that the IFUNC pass below can fire resolvers
compiled with stack protection, and so that resolvers reading TLS
(errno, __thread variables, powerpc's hwcap / at_platform in the
TCB) observe an initialised slot. */
/* Perform IREL{,A} relocations. */
ARCH_SETUP_IREL ();
/* The stack guard goes into the TCB, so initialize it early. */
ARCH_SETUP_TLS ();
/* Set up the stack checker's canary. Must happen before any IFUNC resolver
runs so a resolver compiled with stack protection loads a defined
canary. */
/* In some architectures, IREL{,A} relocations happen after TLS setup in
order to let IFUNC resolvers benefit from TCB information, e.g. powerpc's
hwcap and platform fields available in the TCB. */
ARCH_APPLY_IREL ();
/* Set up the stack checker's canary. */
uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
# ifdef THREAD_SET_STACK_GUARD
THREAD_SET_STACK_GUARD (stack_chk_guard);
@@ -291,32 +292,19 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
__stack_chk_guard = stack_chk_guard;
# endif
/* Set up the pointer guard value. */
uintptr_t pointer_chk_guard = _dl_setup_pointer_guard (_dl_random,
stack_chk_guard);
__pointer_chk_guard_local = pointer_chk_guard;
/* We do not need the _dl_random value anymore. Scrub the AT_RANDOM
bytes and clear the pointer; on targets with an entropy source, refill
the bytes with fresh random data. */
_dl_reseed_random (&_dl_random);
/* Now that the TCB, canary, and pointer guard are in place, run the
deferred IFUNC relocations. For non-PIE static binaries this is
ARCH_SETUP_IREL (apply_irel); for static-pie it is the IRELATIVE
phase of _dl_relocate_static_pie above. */
_dl_relocate_static_pie_ifunc ();
ARCH_SETUP_IREL ();
/* This must run after the IFUNC relocations: _dl_executable_postprocess
may call IFUNC-resolved routines. */
struct link_map *main_map = _dl_get_dl_main_map ();
_dl_executable_postprocess (main_map, GL(dl_phdr), GL(dl_phnum));
/* Initialize libpthread if linked in. */
if (__pthread_initialize_minimal != NULL)
__pthread_initialize_minimal ();
/* Set up the pointer guard value. */
uintptr_t pointer_chk_guard = _dl_setup_pointer_guard (_dl_random,
stack_chk_guard);
# ifdef THREAD_SET_POINTER_GUARD
THREAD_SET_POINTER_GUARD (pointer_chk_guard);
# else
__pointer_chk_guard_local = pointer_chk_guard;
# endif
#endif /* !SHARED */
/* Register the destructor of the dynamic linker if there is any. */
-5
View File
@@ -23,9 +23,4 @@ void
_dl_relocate_static_pie (void)
{
}
void
_dl_relocate_static_pie_ifunc (void)
{
}
#endif
-4
View File
@@ -67,7 +67,6 @@ tests = \
glrefmain \
tst-dladdr \
tst-dlinfo \
tst-dlsym-ctor \
tst-rec-dlopen \
tstatexit \
tstcxaatexit \
@@ -91,7 +90,6 @@ modules-names = \
modcxaatexit \
moddummy1 \
moddummy2 \
tst-dlsym-ctormod \
# modules-names
failtestmod.so-no-z-defs = yes
@@ -199,5 +197,3 @@ $(objpfx)bug-dl-leaf.out: $(objpfx)bug-dl-leaf-lib-cb.so
$(objpfx)bug-dl-leaf-lib-cb.so: $(objpfx)bug-dl-leaf-lib.so
$(objpfx)tst-rec-dlopen.out: $(objpfx)moddummy1.so $(objpfx)moddummy2.so
$(objpfx)tst-dlsym-ctor.out: $(objpfx)tst-dlsym-ctormod.so
-36
View File
@@ -1,36 +0,0 @@
/* Test that a tail-called dlsym from a constructor works.
Copyright The GNU Toolchain Authors.
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 <dlfcn.h>
#include <support/check.h>
#include <support/xdlfcn.h>
static int
do_test (void)
{
/* Loading the module runs its constructor, which performs a dlsym
whose result is discarded. Under optimization the compiler lowers
that to a tail call, so dlsym sees a caller address inside the
dynamic linker itself. Before the fix that resolved to the ld.so
link map, which has no l_scope, and the lookup crashed. */
void *h = xdlopen ("tst-dlsym-ctormod.so", RTLD_NOW);
xdlclose (h);
return 0;
}
#include <support/test-driver.c>
-28
View File
@@ -1,28 +0,0 @@
/* Module for tst-dlsym-ctor.
Copyright The GNU Toolchain Authors.
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 <dlfcn.h>
/* The result is intentionally discarded so the compiler can lower the
dlsym call to a tail call. That is the trigger for the bug -- see
tst-dlsym-ctor.c. */
__attribute__ ((constructor)) static void
ctor (void)
{
(void) dlsym (RTLD_DEFAULT, "tst_dlsym_ctor_no_such_symbol");
}
+2 -153
View File
@@ -78,7 +78,6 @@ dl-routines = \
dl-reloc \
dl-runtime \
dl-scope \
dl-scratch-buffer \
dl-setup_hash \
dl-sort-maps \
dl-thread_gscope_wait \
@@ -270,7 +269,6 @@ tests-static-normal := \
tst-env-setuid-static \
tst-getauxval-static \
tst-linkall-static \
tst-ptrguard-static-dlopen \
tst-single_threaded-pthread-static \
tst-single_threaded-static \
tst-tls-allocation-failure-static \
@@ -279,7 +277,6 @@ tests-static-normal := \
# tests-static-normal
tests-static-internal := \
tst-atrandom-scrub-static \
tst-dl-printf-static \
tst-dl_find_object-static \
tst-env-setuid-tunables \
@@ -291,20 +288,10 @@ tests-static-internal := \
tst-tunables-enable_secure \
# tests-static-internal
ifeq (yes,$(have-gcc-ifunc))
tests-static-internal += \
tst-ifunc-resolver-protector-static \
tst-ifunc-resolver-protector-static-non-pie \
# tests-static-internal
endif
CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o
tst-tls1-static-non-pie-no-pie = yes
CRT-tst-ifunc-resolver-protector-static-non-pie := $(csu-objpfx)crt1.o
tst-ifunc-resolver-protector-static-non-pie-no-pie = yes
tests-container := \
tst-dl-cache-long-path \
tst-ldconfig-bad-aux-cache \
tst-ldconfig-ld_so_conf-update \
# tests-container
@@ -417,13 +404,9 @@ tests += \
tst-auxobj \
tst-auxobj-dlopen \
tst-big-note \
tst-bz26577 \
tst-bz26577-minstack \
tst-debug1 \
tst-deep1 \
tst-dl-is_dso \
tst-dl-llp-stack \
tst-dl-path-buf \
tst-dlclose-lazy \
tst-dlmodcount \
tst-dlmopen-dlerror \
@@ -446,7 +429,6 @@ tests += \
tst-dlopenfail-2 \
tst-dlopenrpath \
tst-dlsym-error \
tst-dst-needed-minstack \
tst-filterobj \
tst-filterobj-dlopen \
tst-glibc-hwcaps \
@@ -549,7 +531,6 @@ tests-internal += \
neededtest2 \
neededtest3 \
neededtest4 \
tst-atrandom-scrub \
tst-audit19a \
tst-create_format1 \
tst-dl-hwcaps_split \
@@ -579,7 +560,6 @@ tests-container += \
tst-dlopen-tlsmodid-container \
tst-pldd \
tst-preload-pthread-libc \
tst-ptrguard-static-dlopen \
tst-rootdir \
# tests-container
@@ -720,7 +700,6 @@ ifeq (yes,$(build-shared))
ifneq ($(PERL),no)
tests-special += \
$(objpfx)noload-mem.out \
$(objpfx)tst-dl-path-buf-mem.out \
$(objpfx)tst-leaks1-mem.out \
# tests-special
endif
@@ -757,19 +736,6 @@ test-extras += \
tst-tlsmod17a \
tst-tlsmod18a \
# test-extras
ifeq (yes,$(have-gcc-ifunc))
# The resolver helper needs <tls.h> for the TCB-canary STACK_CHK_GUARD
# macro, so it must be compiled with MODULE_NAME=testsuite_internal.
extra-test-objs += \
tst-ifunc-resolver-protector-static-mod.o \
tst-ifunc-resolver-protector-static-non-pie-mod.o \
# extra-test-objs
test-internal-extras += \
tst-ifunc-resolver-protector-static-mod \
tst-ifunc-resolver-protector-static-non-pie-mod \
# test-internal-extras
endif
modules-names += \
circlemod1 \
circlemod1a \
@@ -943,7 +909,6 @@ modules-names += \
tst-deep1mod1 \
tst-deep1mod2 \
tst-deep1mod3 \
tst-dl-path-buf-mod \
tst-dl_find_object-mod1 \
tst-dl_find_object-mod2 \
tst-dl_find_object-mod3 \
@@ -974,8 +939,6 @@ modules-names += \
tst-dlopenfailmod3 \
tst-dlopenfailnodelmod \
tst-dlopenrpathmod \
tst-dst-needed-leaf-mod \
tst-dst-needed-wrap-mod \
tst-filterobj-aux \
tst-filterobj-filtee \
tst-filterobj-flt \
@@ -1017,7 +980,6 @@ modules-names += \
tst-null-argv-lib \
tst-p_alignmod-base \
tst-p_alignmod3 \
tst-ptrguard-static-dlopen-mod \
tst-recursive-tlsmallocmod \
tst-recursive-tlsmod0 \
tst-recursive-tlsmod1 \
@@ -1261,7 +1223,6 @@ modules-names-nobuild += \
tst-audit24bmod1 \
tst-audit24bmod2 \
tst-big-note-lib \
tst-bz26577-mod \
tst-nodeps1-mod \
tst-nodeps2-mod \
tst-ro-dynamic-mod \
@@ -1283,11 +1244,7 @@ tests-ifuncstatic := \
ifuncmain7static \
# tests-ifuncstatic
ifeq (yes,$(have-gcc-ifunc))
tests-ifuncstatic += \
ifuncmain9picstatic \
ifuncmain9static \
tst-ifunc-bz28817 \
# tests-ifuncstatic
tests-ifuncstatic += ifuncmain9static ifuncmain9picstatic
endif
tests-static += $(tests-ifuncstatic)
tests-internal += $(tests-ifuncstatic)
@@ -1295,15 +1252,6 @@ ifeq (yes,$(build-shared))
tests += \
tst-ifunc-fault-bindnow \
tst-ifunc-fault-lazy \
tst-ifunc-plt \
tst-ifunc-plt-bindnow \
tst-ifunc-plt-dlopen \
tst-ifunc-plt-dlopen-bindnow \
tst-ifunc-resolver-protector \
tst-ifunc-tls-init \
tst-ifunc-tls-init-gd-ld \
tst-ifunc-tls-write \
tst-tls-tdata-reloc \
# tests
# Note: sysdeps/x86_64/ifuncmain8.c uses ifuncmain8.
tests-internal += \
@@ -1366,23 +1314,7 @@ modules-names += \
ifuncmod1 \
ifuncmod3 \
ifuncmod6 \
tst-ifunc-plt-dep \
tst-ifunc-plt-lib \
tst-ifunc-resolver-protector-mod \
tst-ifunc-tls-init-gd-global-lib \
tst-ifunc-tls-init-gd-lib \
tst-ifunc-tls-init-ld-lib \
tst-ifunc-tls-init-lib1 \
tst-ifunc-tls-init-lib2 \
tst-ifunc-tls-write-lib \
tst-tls-tdata-reloc-lib \
# modules-names
ifneq (no,$(have-test-mtls-descriptor))
tests += tst-ifunc-tls-init-tlsdesc
modules-names += tst-ifunc-tls-init-tlsdesc-lib
CFLAGS-tst-ifunc-tls-init-tlsdesc-lib.c += \
-mtls-dialect=$(have-test-mtls-descriptor)
endif
ifeq (no,$(with-lld))
modules-names += ifuncmod5
endif
@@ -1789,19 +1721,6 @@ $(objpfx)tst-nodelete-opened.out: $(objpfx)tst-nodelete-opened-lib.so
$(objpfx)tst-tlsalign-extern: $(objpfx)tst-tlsalign-vars.o
$(objpfx)tst-tlsalign-extern-static: $(objpfx)tst-tlsalign-vars.o
# The resolver translation unit must always be compiled with
# -fstack-protector-all so the canary code is emitted regardless of the
# default.
CFLAGS-tst-ifunc-resolver-protector-static-mod.c = -fstack-protector-all
CFLAGS-tst-ifunc-resolver-protector-static-non-pie-mod.c = -fstack-protector-all
CFLAGS-tst-ifunc-resolver-protector-mod.c = -fstack-protector-all
$(objpfx)tst-ifunc-resolver-protector-static: \
$(objpfx)tst-ifunc-resolver-protector-static-mod.o
$(objpfx)tst-ifunc-resolver-protector-static-non-pie: \
$(objpfx)tst-ifunc-resolver-protector-static-non-pie-mod.o
$(objpfx)tst-ifunc-resolver-protector: \
$(objpfx)tst-ifunc-resolver-protector-mod.so
tst-null-argv-ENV = LD_DEBUG=all LD_DEBUG_OUTPUT=$(objpfx)tst-null-argv.debug.out
LDFLAGS-nodel2mod3.so = -Wl,--no-as-needed
LDFLAGS-reldepmod5.so = -Wl,--no-as-needed
@@ -1874,7 +1793,6 @@ unload4mod1.so-no-z-defs = yes
ifuncmod1.so-no-z-defs = yes
ifuncmod5.so-no-z-defs = yes
ifuncmod6.so-no-z-defs = yes
tst-ifunc-plt-lib.so-no-z-defs = yes
tst-auditmod9a.so-no-z-defs = yes
tst-auditmod9b.so-no-z-defs = yes
tst-nodelete-uniquemod.so-no-z-defs = yes
@@ -2349,16 +2267,6 @@ CFLAGS-tst-dlopenrpath.c += -DPFX=\"$(objpfx)\"
LDFLAGS-tst-dlopenrpathmod.so += -Wl,-rpath,\$$ORIGIN/test-subdir
$(objpfx)tst-dlopenrpath.out: $(objpfx)firstobj.so
$(objpfx)tst-dl-path-buf: $(objpfx)tst-dl-path-buf-mod.so $(shared-thread-library)
LDFLAGS-tst-dl-path-buf += -Wl,-rpath,\$$ORIGIN/tst-dl-path-buf-subdir
tst-dl-path-buf-TUNABLES = glibc.mem.decorate_maps=1
tst-dl-path-buf-ENV = MALLOC_TRACE=$(objpfx)tst-dl-path-buf.mtrace \
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
$(objpfx)tst-dl-path-buf-mem.out: $(objpfx)tst-dl-path-buf.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-dl-path-buf.mtrace > $@; \
$(evaluate-test)
$(objpfx)tst-deep1mod2.so: $(objpfx)tst-deep1mod3.so
$(objpfx)tst-deep1: $(objpfx)tst-deep1mod1.so
$(objpfx)tst-deep1.out: $(objpfx)tst-deep1mod2.so
@@ -2422,9 +2330,6 @@ tst-ptrguard1-ARGS = --command "$(host-test-program-cmd) --child"
CFLAGS-tst-ptrguard1-static.c += -DPTRGUARD_LOCAL
tst-ptrguard1-static-ARGS = --command "$(objpfx)tst-ptrguard1-static --child"
# Likewise, the static pointer guard lives in __pointer_chk_guard_local.
CFLAGS-tst-atrandom-scrub-static.c += -DPTRGUARD_LOCAL
$(objpfx)tst-leaks1-mem.out: $(objpfx)tst-leaks1.out
$(common-objpfx)malloc/mtrace $(objpfx)tst-leaks1.mtrace > $@; \
$(evaluate-test)
@@ -2521,39 +2426,6 @@ $(objpfx)tst-ifunc-fault-bindnow.out: $(objpfx)tst-ifunc-fault-bindnow \
$(objpfx)ld.so
$(tst-ifunc-fault-script)
LDFLAGS-tst-ifunc-plt-lib.so = -Wl,-z,lazy
tst-ifunc-plt-bindnow-ENV = LD_BIND_NOW=1
tst-ifunc-plt-dlopen-bindnow-ENV = LD_BIND_NOW=1
$(objpfx)tst-ifunc-plt-lib.so: $(objpfx)tst-ifunc-plt-dep.so
$(objpfx)tst-ifunc-plt: $(objpfx)tst-ifunc-plt-lib.so
$(objpfx)tst-ifunc-plt-bindnow: $(objpfx)tst-ifunc-plt-lib.so
$(objpfx)tst-ifunc-plt-dlopen.out: \
$(objpfx)tst-ifunc-plt-lib.so $(objpfx)tst-ifunc-plt-dep.so
$(objpfx)tst-ifunc-plt-dlopen-bindnow.out: \
$(objpfx)tst-ifunc-plt-lib.so $(objpfx)tst-ifunc-plt-dep.so
$(objpfx)tst-ifunc-tls-init: $(objpfx)tst-ifunc-tls-init-lib1.so
$(objpfx)tst-ifunc-tls-init.out: \
$(objpfx)tst-ifunc-tls-init-lib2.so
$(objpfx)tst-ifunc-tls-write: $(objpfx)tst-ifunc-tls-write-lib.so
$(objpfx)tst-tls-tdata-reloc: $(objpfx)tst-tls-tdata-reloc-lib.so
$(objpfx)tst-ifunc-tls-init-gd-ld: $(shared-thread-library)
$(objpfx)tst-ifunc-tls-init-gd-ld.out: \
$(objpfx)tst-ifunc-tls-init-gd-lib.so \
$(objpfx)tst-ifunc-tls-init-gd-global-lib.so \
$(objpfx)tst-ifunc-tls-init-ld-lib.so
tst-ifunc-tls-init-gd-ld-TUNABLES = glibc.rtld.optional_static_tls=0
ifneq (no,$(have-test-mtls-descriptor))
$(objpfx)tst-ifunc-tls-init-tlsdesc: $(shared-thread-library)
$(objpfx)tst-ifunc-tls-init-tlsdesc.out: \
$(objpfx)tst-ifunc-tls-init-tlsdesc-lib.so
tst-ifunc-tls-init-tlsdesc-TUNABLES = glibc.rtld.optional_static_tls=0
endif # $(have-test-mtls-descriptor)
$(objpfx)tst-unique1.out: $(objpfx)tst-unique1mod1.so \
$(objpfx)tst-unique1mod2.so
@@ -2842,7 +2714,7 @@ $(objpfx)tst-ldconfig-soname.out : tst-ldconfig-soname.sh \
# Test static linking of all the libraries we can possibly link
# together. Note that in some configurations this may be less than the
# complete list of libraries we build but we try to maximize this list.
# complete list of libraries we build but we try to maxmimize this list.
$(objpfx)tst-linkall-static: \
$(common-objpfx)math/libm.a \
$(common-objpfx)resolv/libresolv.a \
@@ -2886,16 +2758,6 @@ $(objpfx)tst-big-note: $(objpfx)tst-big-note-lib.so
$(objpfx)tst-big-note-lib.so: $(objpfx)tst-big-note-lib.o
$(LINK.o) -shared -o $@ $(LDFLAGS.so) $(dt-relr-ldflag) $<
tst-bz26577-ARGS = -- $(host-test-program-cmd)
$(objpfx)tst-bz26577.out: $(objpfx)tst-bz26577-mod.so
$(objpfx)tst-bz26577-mod.so: gen-tst-bz26577-mod.py $(..)/scripts/glibcelf.py \
$(objpfx)ld.so
PYTHONPATH=$(..)scripts $(PYTHON) $< $@ $(objpfx)ld.so
generated += tst-bz26577-mod.so
$(objpfx)tst-bz26577-minstack: $(shared-thread-library)
$(objpfx)tst-bz26577-minstack.out: $(objpfx)tst-bz26577-mod.so
$(objpfx)tst-unwind-ctor: $(objpfx)tst-unwind-ctor-lib.so
LDLIBS-tst-unwind-ctor += $(libunwind)
LDFLAGS-tst-unwind-ctor-lib.so = -Wl,--unresolved-symbols=ignore-all
@@ -2977,16 +2839,6 @@ LDFLAGS-tst-dlopen-nodelete-reloc-mod17.so = -Wl,--no-as-needed
$(objpfx)tst-ldconfig-ld_so_conf-update.out: $(objpfx)tst-ldconfig-ld-mod.so
# Reuses the trivial module already built for tst-dl-path-buf.
$(objpfx)tst-dl-cache-long-path: $(shared-thread-library)
$(objpfx)tst-dl-cache-long-path.out: $(objpfx)tst-dl-path-buf-mod.so
LDFLAGS-tst-dst-needed-leaf-mod.so = \
-Wl,-soname,\$$ORIGIN/\$$ORIGIN/\$$ORIGIN/\$$ORIGIN/\$$ORIGIN/leaf.so
$(objpfx)tst-dst-needed-wrap-mod.so: $(objpfx)tst-dst-needed-leaf-mod.so
$(objpfx)tst-dst-needed-minstack: $(shared-thread-library)
$(objpfx)tst-dst-needed-minstack.out: $(objpfx)tst-dst-needed-wrap-mod.so
LDFLAGS-tst-filterobj-flt.so = -Wl,--filter=$(objpfx)tst-filterobj-filtee.so
$(objpfx)tst-filterobj: $(objpfx)tst-filterobj-flt.so
$(objpfx)tst-filterobj.out: $(objpfx)tst-filterobj-filtee.so
@@ -3209,9 +3061,6 @@ $(objpfx)tst-tls21mod.so: $(tst-tls-many-dynamic-modules:%=$(objpfx)%.so)
$(objpfx)tst-getauxval-static.out: $(objpfx)tst-auxvalmod.so
tst-getauxval-static-ENV = LD_LIBRARY_PATH=$(objpfx):$(common-objpfx)
$(objpfx)tst-ptrguard-static-dlopen.out: \
$(objpfx)tst-ptrguard-static-dlopen-mod.so
$(objpfx)tst-dlmopen-gethostbyname.out: $(objpfx)tst-dlmopen-gethostbyname-mod.so
$(objpfx)tst-ro-dynamic: $(objpfx)tst-ro-dynamic-mod.so
+5 -9
View File
@@ -21,7 +21,6 @@
#include <ldsodefs.h>
#include <sys/mman.h>
#include <dl-cache.h>
#include <dl-scratch-buffer.h>
#include <stdint.h>
#include <_itoa.h>
#include <dl-hwcaps.h>
@@ -491,15 +490,12 @@ _dl_load_cache_lookup (const char *name)
/* The double copy is *required* since malloc may be interposed
and call dlopen itself whose completion would unmap the data
we are accessing. Therefore we must make the copy of the
mapping data without using malloc. The DL_SCRATCH_NO_MALLOC
forces any spill to anonymous mmap rather than the malloc. */
struct dl_scratch_buffer scratch = dl_scratch_buffer_init ();
mapping data without using malloc. */
char *temp;
size_t best_len = strlen (best) + 1;
dl_scratch_buffer_allocate (&scratch, best_len, DL_SCRATCH_NO_MALLOC);
memcpy (scratch.data, best, best_len);
char *result = __strdup (scratch.data);
dl_scratch_buffer_free (&scratch);
return result;
temp = alloca (best_len);
memcpy (temp, best, best_len);
return __strdup (temp);
}
#ifndef MAP_COPY
+1 -1
View File
@@ -485,7 +485,7 @@ _dl_close_worker (struct link_map *map, bool force)
size_t tls_free_end;
tls_free_start = tls_free_end = NO_TLS_OFFSET;
/* Protects global and module specific TLS state. */
/* Protects global and module specitic TLS state. */
__rtld_lock_lock_recursive (GL(dl_load_tls_lock));
/* We modify the list of loaded objects. */
+47 -69
View File
@@ -30,7 +30,6 @@
#include <scratch_buffer.h>
#include <dl-dst.h>
#include <dl-scratch-buffer.h>
/* Whether an shared object references one or more auxiliary objects
is signaled by the AUXTAG entry in l_info. */
@@ -81,35 +80,47 @@ struct list
};
/* Expand the dynamic-string-tokens ($ORIGIN / $LIB / $PLATFORM) in INPUT
using L's context. Returns the expanded string -- a pointer either into
SCRATCH->data (when expansion was needed) or back at INPUT (when no DSTs
were present, so no allocation happened). Returns NULL when a DST was
present but could not be resolved.
SCRATCH must be an init'd dl_scratch_buffer the caller will release once
the returned string is no longer needed. This function never returns when
called for a SUID/SGID that contains DSTs: it raises a loader error. */
static const char *
expand_dst (struct link_map *l, const char *input,
struct dl_scratch_buffer *scratch)
{
size_t dst_cnt = _dl_dst_count (input);
if (dst_cnt == 0)
return input;
/* DST must not appear in SUID/SGID programs. */
if (__libc_enable_secure)
_dl_signal_error (0, input, NULL, N_("\
DST not allowed in SUID/SGID programs"));
size_t total = DL_DST_REQUIRED (l, input, strlen (input), dst_cnt);
dl_scratch_buffer_allocate (scratch, total + 1, 0);
const char *result = _dl_dst_substitute (l, input, scratch->data);
if (*result == '\0')
return NULL;
return result;
}
/* Macro to expand DST. It is an macro since we use `alloca'. */
#define expand_dst(l, str, fatal) \
({ \
const char *__str = (str); \
const char *__result = __str; \
size_t __dst_cnt = _dl_dst_count (__str); \
\
if (__dst_cnt != 0) \
{ \
char *__newp; \
\
/* DST must not appear in SUID/SGID programs. */ \
if (__libc_enable_secure) \
_dl_signal_error (0, __str, NULL, N_("\
DST not allowed in SUID/SGID programs")); \
\
__newp = (char *) alloca (DL_DST_REQUIRED (l, __str, strlen (__str), \
__dst_cnt)); \
\
__result = _dl_dst_substitute (l, __str, __newp); \
\
if (*__result == '\0') \
{ \
/* The replacement for the DST is not known. We can't \
processed. */ \
if (fatal) \
_dl_signal_error (0, __str, NULL, N_("\
empty dynamic string token substitution")); \
else \
{ \
/* This is for DT_AUXILIARY. */ \
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS)) \
_dl_debug_printf (N_("\
cannot load auxiliary `%s' because of empty dynamic string token " \
"substitution\n"), __str); \
continue; \
} \
} \
} \
\
__result; })
static void
preload (struct list *known, unsigned int *nlist, struct link_map *map)
@@ -213,26 +224,12 @@ _dl_map_object_deps (struct link_map *map,
/* Map in the needed object. */
struct link_map *dep;
/* Recognize DSTs. Empty substitution for DT_NEEDED is
non-fatal: log and skip this entry. */
struct dl_scratch_buffer scratch
= dl_scratch_buffer_init ();
name = expand_dst (l, strtab + d->d_un.d_val, &scratch);
if (__glibc_unlikely (name == NULL))
{
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
_dl_debug_printf (N_("\
cannot load auxiliary `%s' because of empty dynamic string token "
"substitution\n"),
strtab + d->d_un.d_val);
dl_scratch_buffer_free (&scratch);
continue;
}
/* Recognize DSTs. */
name = expand_dst (l, strtab + d->d_un.d_val, 0);
/* Store the tag in the argument structure. */
args.name = name;
int err = _dl_catch_exception (&exception, openaux, &args);
dl_scratch_buffer_free (&scratch);
if (__glibc_unlikely (exception.errstring != NULL))
{
if (err)
@@ -270,25 +267,9 @@ cannot load auxiliary `%s' because of empty dynamic string token "
{
struct list *newp;
/* Recognize DSTs. DT_AUXILIARY is fatal on unresolved
DST; DT_FILTER is non-fatal and is skipped. */
struct dl_scratch_buffer scratch
= dl_scratch_buffer_init ();
name = expand_dst (l, strtab + d->d_un.d_val, &scratch);
if (__glibc_unlikely (name == NULL))
{
dl_scratch_buffer_free (&scratch);
if (d->d_tag == DT_AUXILIARY)
_dl_signal_error (0, strtab + d->d_un.d_val, NULL,
N_("empty dynamic string token "
"substitution"));
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
_dl_debug_printf (N_("\
cannot load auxiliary `%s' because of empty dynamic string token "
"substitution\n"),
strtab + d->d_un.d_val);
continue;
}
/* Recognize DSTs. */
name = expand_dst (l, strtab + d->d_un.d_val,
d->d_tag == DT_AUXILIARY);
/* Store the tag in the argument structure. */
args.name = name;
@@ -304,9 +285,6 @@ cannot load auxiliary `%s' because of empty dynamic string token "
object is not available. For filter objects the dependency
must be available. */
int err = _dl_catch_exception (&exception, openaux, &args);
/* NAME is consumed by openaux above; release the DST
scratch buffer regardless of outcome. */
dl_scratch_buffer_free (&scratch);
if (__glibc_unlikely (exception.errstring != NULL))
{
if (d->d_tag == DT_AUXILIARY)
@@ -557,7 +535,7 @@ cannot load auxiliary `%s' because of empty dynamic string token "
/* Copy the binary into position 0. */
l_initfini[0] = map->l_searchlist.r_list[map_index];
/* Copy the filters. */
/* Copy the filtees. */
for (i = 0; i < map_index; ++i)
l_initfini[i+1] = map->l_searchlist.r_list[i];
+2 -2
View File
@@ -465,7 +465,7 @@ _dl_find_object (void *pc1, struct dl_find_object *result)
}
rtld_hidden_def (_dl_find_object)
/* Subroutine of _dlfo_process_initial to split out noncontiguous link
/* Subroutine of _dlfo_process_initial to split out noncontigous link
maps. NODELETE is the number of used _dlfo_nodelete_mappings
elements. It is incremented as needed, and the new NODELETE value
is returned. */
@@ -586,7 +586,7 @@ _dl_find_object_init (void)
{
/* Non-contiguous main maps are handled in
_dlfo_process_initial. Mark as initialized, but not
covering any valid PC. */
coverying any valid PC. */
_dlfo_main.map_start = -1;
_dlfo_main.map_end = -1;
}
+234 -277
View File
@@ -33,7 +33,31 @@
#include <sys/types.h>
#include <gnu/lib-names.h>
#include <dl-tunables.h>
#include <dl-scratch-buffer.h>
/* Type for the buffer we put the ELF header and hopefully the program
header. This buffer does not really have to be too large. In most
cases the program header follows the ELF header directly. If this
is not the case all bets are off and we can make the header
arbitrarily large and still won't get it read. This means the only
question is how large are the ELF and program header combined. The
ELF header 32-bit files is 52 bytes long and in 64-bit files is 64
bytes long. Each program header entry is again 32 and 56 bytes
long respectively. I.e., even with a file which has 10 program
header entries we only have to read 372B/624B respectively. Add to
this a bit of margin for program notes and reading 512B and 832B
for 32-bit and 64-bit files respectively is enough. If this
heuristic should really fail for some file the code in
`_dl_map_object_from_fd' knows how to recover. */
struct filebuf
{
ssize_t len;
#if __WORDSIZE == 32
# define FILEBUF_SIZE 512
#else
# define FILEBUF_SIZE 832
#endif
char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
};
#include "dynamic-link.h"
#include "get-dynamic-info.h"
@@ -97,9 +121,7 @@ is_trusted_path_normalize (const char *path, size_t len)
if (len == 0)
return false;
struct dl_scratch_buffer scratch = dl_scratch_buffer_init ();
dl_scratch_buffer_allocate (&scratch, len + 2, 0);
char *npath = scratch.data;
char *npath = (char *) alloca (len + 2);
char *wnp = npath;
while (*path != '\0')
{
@@ -134,24 +156,19 @@ is_trusted_path_normalize (const char *path, size_t len)
if (wnp == npath || wnp[-1] != '/')
*wnp++ = '/';
bool result = false;
const char *trun = system_dirs;
for (size_t idx = 0; idx < nsystem_dirs_len; ++idx)
{
if (wnp - npath >= system_dirs_len[idx]
&& memcmp (trun, npath, system_dirs_len[idx]) == 0)
{
/* Found it. */
result = true;
break;
}
/* Found it. */
return true;
trun += system_dirs_len[idx] + 1;
}
dl_scratch_buffer_free (&scratch);
return result;
return false;
}
/* Given a substring starting at INPUT, just after the DST '$' start
@@ -422,10 +439,7 @@ struct r_search_path_struct __rtld_search_dirs attribute_relro;
static size_t max_dirnamelen;
/* Tokenize RPATH (in place) and populate RESULT with one entry per non-empty
directory. Returns false if a per-entry allocation fails, leaving the
caller responsible for signaling any error. */
static bool
static struct r_search_path_elem **
fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
const char *what, const char *where, struct link_map *l)
{
@@ -493,10 +507,8 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
malloc (sizeof (*dirp) + ncapstr * sizeof (enum r_dir_status)
+ where_len + len + 1);
if (dirp == NULL)
{
free (to_free);
return false;
}
_dl_signal_error (ENOMEM, NULL, NULL,
N_("cannot create cache for search path"));
dirp->dirname = ((char *) dirp + sizeof (*dirp)
+ ncapstr * sizeof (enum r_dir_status));
@@ -533,7 +545,7 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
/* Terminate the array. */
result[nelems] = NULL;
return true;
return result;
}
@@ -614,13 +626,7 @@ decompose_rpath (struct r_search_path_struct *sps,
_dl_signal_error (ENOMEM, NULL, NULL, errstring);
}
if (!fillin_rpath (copy, result, ":", what, where, l))
{
free (copy);
free (result);
errstring = N_("cannot create cache for search path");
goto signal_error;
}
fillin_rpath (copy, result, ":", what, where, l);
/* Free the copied RPATH string. `fillin_rpath' make own copies if
necessary. */
@@ -793,13 +799,12 @@ _dl_init_paths (const char *llp, const char *source,
if (llp != NULL && *llp != '\0')
{
/* Count entries directly off the const LD_LIBRARY_PATH so the
search-path dirs array can be allocated before the scratch buffer is
live; that way an OOM on either of the two heap allocations the
loader controls (the dirs array or the per-entry malloc inside
fillin_rpath) is signalled after the scratch has been released. */
char *llp_tmp = strdupa (llp);
/* Decompose the LD_LIBRARY_PATH contents. First determine how many
elements it has. */
size_t nllp = 1;
for (const char *cp = llp; *cp != '\0'; ++cp)
for (const char *cp = llp_tmp; *cp != '\0'; ++cp)
if (*cp == ':' || *cp == ';')
++nllp;
@@ -811,25 +816,8 @@ _dl_init_paths (const char *llp, const char *source,
goto signal_error;
}
/* fillin_rpath needs a mutable copy because __strsep punches NULs
into it as it tokenizes. */
size_t llp_len = strlen (llp);
struct dl_scratch_buffer scratch = dl_scratch_buffer_init ();
dl_scratch_buffer_allocate (&scratch, llp_len + 1, 0);
char *llp_tmp = memcpy (scratch.data, llp, llp_len + 1);
bool ok = fillin_rpath (llp_tmp, __rtld_env_path_list.dirs, ":;",
source, NULL, l);
dl_scratch_buffer_free (&scratch);
if (!ok)
{
free (__rtld_env_path_list.dirs);
__rtld_env_path_list.dirs = NULL;
errstring = N_("cannot create cache for search path");
goto signal_error;
}
(void) fillin_rpath (llp_tmp, __rtld_env_path_list.dirs, ":;",
source, NULL, l);
if (__rtld_env_path_list.dirs[0] == NULL)
{
@@ -947,167 +935,6 @@ _dl_notify_new_object (int mode, Lmid_t nsid, struct link_map *l)
#endif
}
/* Initialize the PT_LOAD iterator IT for reading program headers from FD
at file offset PHOFF with PHNUM entries. FBP is used as scratch space
for batched program-header reads; if open_verify's initial read into
FBP->buf already covers the whole phdr table, the iterator runs
entirely from that buffer without any further pread. Zeros all
precomputed fields so the caller's scan loop can fill them in. */
static void
_dl_pt_load_iterator_init (struct dl_pt_load_iterator *it, int fd,
struct filebuf *fbp, ElfW(Off) phoff,
uint16_t phnum)
{
it->fd = fd;
it->fbp = fbp;
it->phoff = phoff;
it->phnum = phnum;
it->idx = 0;
it->pagesize = GLRO (dl_pagesize);
it->p_align_max = 0;
it->nloadcmds = 0;
it->first_mapstart = 0;
it->last_mapstart = 0;
it->last_allocend = 0;
it->cached = (phoff + (ElfW(Off)) phnum * sizeof (ElfW(Phdr))
<= (ElfW(Off)) fbp->len);
it->buf_base = 0;
it->buf_count = it->cached ? phnum : 0;
}
/* Scan all program headers from IT->fd, using the iterator's filebuf as a
scratch buffer for batched reads (skipped entirely if open_verify
already read the whole table). Fills in IT's precomputed PT_LOAD
metadata and collects segment attributes into L. Returns NULL on
success, or an error message string on failure; sets *ERRVALP to errno
for I/O errors, 0 otherwise. */
static const char *
_dl_map_object_scan_phdrs (struct dl_pt_load_iterator *it,
struct link_map *l, int mode,
unsigned int *stack_flagsp, bool *has_holesp,
bool *empty_dynamicp, int *errvalp)
{
ElfW(Addr) prev_mapend = 0;
struct dl_machine_phdr_info minfo;
elf_machine_phdr_info_init (&minfo);
for (ElfW(Half) i = 0; i < it->phnum; i++)
{
const ElfW(Phdr) *ph = _dl_pt_load_iterator_phdr_at (it, i);
if (__glibc_unlikely (ph == NULL))
{
*errvalp = errno;
return N_("cannot read file data");
}
elf_machine_phdr_collect (&minfo, ph);
switch (ph->p_type)
{
case PT_LOAD:
{
if (__glibc_unlikely (((ph->p_vaddr - ph->p_offset)
& (it->pagesize - 1)) != 0))
{
*errvalp = 0;
return N_("ELF load command address/offset not page-aligned");
}
ElfW(Addr) mapstart = ALIGN_DOWN (ph->p_vaddr, it->pagesize);
ElfW(Addr) mapend = ALIGN_UP (ph->p_vaddr + ph->p_filesz,
it->pagesize);
ElfW(Off) mapoff = ALIGN_DOWN (ph->p_offset, it->pagesize);
int prot = pf_to_prot (ph->p_flags);
if (powerof2 (ph->p_align) && ph->p_align > it->p_align_max)
it->p_align_max = ph->p_align;
it->p_align_max = _dl_map_segment_align (&(struct loadcmd) {
.mapstart = mapstart,
.mapend = mapend,
.mapoff = mapoff,
.prot = prot },
it->p_align_max);
if (it->nloadcmds > 0 && prev_mapend != mapstart)
*has_holesp = true;
prev_mapend = mapend;
if (it->nloadcmds == 0)
it->first_mapstart = mapstart;
it->last_mapstart = mapstart;
it->last_allocend = ph->p_vaddr + ph->p_memsz;
it->nloadcmds++;
}
break;
/* These entries tell us where to find things once the file's
segments are mapped in. We record the addresses it says
verbatim, and later correct for the run-time load address. */
case PT_DYNAMIC:
if (ph->p_filesz == 0)
*empty_dynamicp = true; /* Usually separate debuginfo. */
else
{
/* Debuginfo only files from "objcopy --only-keep-debug"
contain a PT_DYNAMIC segment with p_filesz == 0. Skip
such a segment to avoid a crash later. */
l->l_ld = (void *) ph->p_vaddr;
l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn));
l->l_ld_readonly = (ph->p_flags & PF_W) == 0;
}
break;
case PT_PHDR:
l->l_phdr = (void *) ph->p_vaddr;
break;
case PT_TLS:
if (ph->p_memsz == 0)
/* Nothing to do for an empty segment. */
break;
l->l_tls_blocksize = ph->p_memsz;
l->l_tls_align = ph->p_align;
if (ph->p_align == 0)
l->l_tls_firstbyte_offset = 0;
else
l->l_tls_firstbyte_offset = ph->p_vaddr & (ph->p_align - 1);
l->l_tls_initimage_size = ph->p_filesz;
/* Since we don't know the load address yet only store the
offset. We will adjust it later. */
l->l_tls_initimage = (void *) ph->p_vaddr;
/* l->l_tls_modid is assigned below, once there is no
possibility for failure. */
if (l->l_type != lt_library
&& GL(dl_tls_dtv_slotinfo_list) == NULL)
{
#ifdef SHARED
/* We are loading the executable itself when the dynamic
linker was executed directly. The setup will happen
later. */
assert (l->l_prev == NULL || (mode & __RTLD_AUDIT) != 0);
#else
assert (false && "TLS not initialized in static application");
#endif
}
break;
case PT_GNU_STACK:
*stack_flagsp = pf_to_prot (ph->p_flags);
break;
case PT_GNU_RELRO:
l->l_relro_addr = ph->p_vaddr;
l->l_relro_size = ph->p_memsz;
break;
}
}
if (__glibc_unlikely (elf_machine_reject_phdr_p (&minfo, l, it->fd)))
{
*errvalp = 0;
return N_("ELF file incompatible with this system");
}
return NULL;
}
/* Map in the shared object NAME, actually located in REALNAME, and already
opened on FD. */
@@ -1121,7 +948,8 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
const void *stack_endp, Lmid_t nsid)
{
struct link_map *l = NULL;
ElfW(Ehdr) header;
const ElfW(Ehdr) *header;
const ElfW(Phdr) *phdr;
const ElfW(Phdr) *ph;
size_t maplength;
int type;
@@ -1231,10 +1059,8 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_FILES))
_dl_debug_printf ("file=%s [%lu]; generating link map\n", name, nsid);
/* The ELF header is already validate in `open_verify', make a local copy
because _dl_map_object_scan_phdrs may overwrite fbp->buf when reading
phdrs via pread in the slow path. */
memcpy (&header, fbp->buf, sizeof header);
/* This is the ELF header. We read it in `open_verify'. */
header = (void *) fbp->buf;
/* Enter the new object in the list of loaded objects. */
l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
@@ -1246,9 +1072,26 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
errstring = N_("cannot create shared object descriptor");
goto lose_errno;
}
l->l_entry = header.e_entry;
type = header.e_type;
l->l_phnum = header.e_phnum;
/* Extract the remaining details we need from the ELF header
and then read in the program header table. */
l->l_entry = header->e_entry;
type = header->e_type;
l->l_phnum = header->e_phnum;
maplength = header->e_phnum * sizeof (ElfW(Phdr));
if (header->e_phoff + maplength <= (size_t) fbp->len)
phdr = (void *) (fbp->buf + header->e_phoff);
else
{
phdr = alloca (maplength);
if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
header->e_phoff) != maplength)
{
errstring = N_("cannot read file data");
goto lose_errno;
}
}
/* On most platforms presume that PT_GNU_STACK is absent and the stack is
* executable. Other platforms default to a nonexecutable stack and don't
@@ -1256,29 +1099,140 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
unsigned int stack_flags = DEFAULT_STACK_PROT_PERMS;
{
/* Single pass over the program header table: initialize the PT_LOAD
iterator (precomputing p_align_max, has_holes, and first/last segment
metadata) and collect all other segment attributes simultaneously.
Program headers are read in chunks into fbp->buf via pread so that
no large stack buffer is needed regardless of e_phnum. */
struct dl_pt_load_iterator it;
bool has_holes;
/* Scan the program header table, collecting its load commands. */
struct loadcmd loadcmds[l->l_phnum];
size_t nloadcmds = 0;
bool has_holes = false;
bool empty_dynamic = false;
ElfW(Addr) p_align_max = 0;
_dl_pt_load_iterator_init (&it, fd, fbp, header.e_phoff, l->l_phnum);
has_holes = false;
/* The struct is initialized to zero so this is not necessary:
l->l_ld = 0;
l->l_phdr = 0;
l->l_addr = 0; */
for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph)
switch (ph->p_type)
{
/* These entries tell us where to find things once the file's
segments are mapped in. We record the addresses it says
verbatim, and later correct for the run-time load address. */
case PT_DYNAMIC:
if (ph->p_filesz == 0)
empty_dynamic = true; /* Usually separate debuginfo. */
else
{
/* Debuginfo only files from "objcopy --only-keep-debug"
contain a PT_DYNAMIC segment with p_filesz == 0. Skip
such a segment to avoid a crash later. */
l->l_ld = (void *) ph->p_vaddr;
l->l_ldnum = ph->p_memsz / sizeof (ElfW(Dyn));
l->l_ld_readonly = (ph->p_flags & PF_W) == 0;
}
break;
errstring = _dl_map_object_scan_phdrs (&it, l, mode, &stack_flags,
&has_holes, &empty_dynamic, &errval);
if (__glibc_unlikely (errstring != NULL))
goto lose;
case PT_PHDR:
l->l_phdr = (void *) ph->p_vaddr;
break;
if (__glibc_unlikely (it.nloadcmds == 0))
case PT_LOAD:
/* A load command tells us to map in part of the file.
We record the load commands and process them all later. */
if (__glibc_unlikely (((ph->p_vaddr - ph->p_offset)
& (GLRO(dl_pagesize) - 1)) != 0))
{
errstring
= N_("ELF load command address/offset not page-aligned");
goto lose;
}
struct loadcmd *c = &loadcmds[nloadcmds++];
c->mapstart = ALIGN_DOWN (ph->p_vaddr, GLRO(dl_pagesize));
c->mapend = ALIGN_UP (ph->p_vaddr + ph->p_filesz, GLRO(dl_pagesize));
c->dataend = ph->p_vaddr + ph->p_filesz;
c->allocend = ph->p_vaddr + ph->p_memsz;
/* Remember the maximum p_align. */
if (powerof2 (ph->p_align) && ph->p_align > p_align_max)
p_align_max = ph->p_align;
c->mapoff = ALIGN_DOWN (ph->p_offset, GLRO(dl_pagesize));
DIAG_PUSH_NEEDS_COMMENT;
#if __GNUC_PREREQ (11, 0)
/* Suppress invalid GCC warning:
(((char *)loadcmds.113_68 + _933 + 16))[329406144173384849].mapend may be used uninitialized [-Wmaybe-uninitialized]
See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106008
*/
DIAG_IGNORE_NEEDS_COMMENT_GCC (11, "-Wmaybe-uninitialized");
#endif
/* Determine whether there is a gap between the last segment
and this one. */
if (nloadcmds > 1 && c[-1].mapend != c->mapstart)
has_holes = true;
DIAG_POP_NEEDS_COMMENT;
/* Optimize a common case. */
c->prot = pf_to_prot (ph->p_flags);
/* Architecture-specific adjustment of segment alignment. */
p_align_max = _dl_map_segment_align (c, p_align_max);
break;
case PT_TLS:
if (ph->p_memsz == 0)
/* Nothing to do for an empty segment. */
break;
l->l_tls_blocksize = ph->p_memsz;
l->l_tls_align = ph->p_align;
if (ph->p_align == 0)
l->l_tls_firstbyte_offset = 0;
else
l->l_tls_firstbyte_offset = ph->p_vaddr & (ph->p_align - 1);
l->l_tls_initimage_size = ph->p_filesz;
/* Since we don't know the load address yet only store the
offset. We will adjust it later. */
l->l_tls_initimage = (void *) ph->p_vaddr;
/* l->l_tls_modid is assigned below, once there is no
possibility for failure. */
if (l->l_type != lt_library
&& GL(dl_tls_dtv_slotinfo_list) == NULL)
{
#ifdef SHARED
/* We are loading the executable itself when the dynamic
linker was executed directly. The setup will happen
later. */
assert (l->l_prev == NULL || (mode & __RTLD_AUDIT) != 0);
#else
assert (false && "TLS not initialized in static application");
#endif
}
break;
case PT_GNU_STACK:
stack_flags = pf_to_prot (ph->p_flags);
break;
case PT_GNU_RELRO:
l->l_relro_addr = ph->p_vaddr;
l->l_relro_size = ph->p_memsz;
break;
}
if (__glibc_unlikely (nloadcmds == 0))
{
/* This only happens for a bogus object that will be caught with
another error below. But we don't want to go through the
calculations below using NLOADCMDS - 1. */
errstring = N_("object file has no loadable segments");
goto lose;
}
/* Align all PT_LOAD segments to the maximum p_align. */
for (size_t i = 0; i < nloadcmds; i++)
loadcmds[i].mapalign = p_align_max;
/* dlopen of an executable is not valid because it is not possible
to perform proper relocations, handle static TLS, or run the
ELF constructors. For PIE, the check needs the dynamic
@@ -1300,13 +1254,13 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
}
/* Length of the sections to be loaded. */
maplength = it.last_allocend - it.first_mapstart;
maplength = loadcmds[nloadcmds - 1].allocend - loadcmds[0].mapstart;
/* Now process the load commands and map segments into memory.
This is responsible for filling in:
l_map_start, l_map_end, l_addr, l_contiguous, l_phdr
*/
errstring = _dl_map_segments (l, fd, &header, type, &it,
errstring = _dl_map_segments (l, fd, header, type, loadcmds, nloadcmds,
maplength, has_holes, loader);
if (__glibc_unlikely (errstring != NULL))
{
@@ -1339,22 +1293,18 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
if (l->l_phdr == NULL)
{
/* The program header is not contained in any of the segments.
Allocate memory and read the program header table from the file. */
size_t phdr_size = (size_t) header.e_phnum * sizeof (ElfW(Phdr));
ElfW(Phdr) *newp = (ElfW(Phdr) *) malloc (phdr_size);
We have to allocate memory ourself and copy it over from out
temporary place. */
ElfW(Phdr) *newp = (ElfW(Phdr) *) malloc (header->e_phnum
* sizeof (ElfW(Phdr)));
if (newp == NULL)
{
errstring = N_("cannot allocate memory for program header");
goto lose_errno;
}
if ((size_t) __pread64_nocancel (fd, newp, phdr_size,
header.e_phoff) != phdr_size)
{
free (newp);
errstring = N_("cannot read file data");
goto lose_errno;
}
l->l_phdr = newp;
l->l_phdr = memcpy (newp, phdr,
(header->e_phnum * sizeof (ElfW(Phdr))));
l->l_phdr_allocated = 1;
}
else
@@ -1507,17 +1457,12 @@ cannot enable executable stack as shared object requires");
return l;
}
/* Print search path. BUF is a scratch buffer provided by the caller;
it must be large enough to hold the longest "<dirname><capstr>" plus
a trailing NUL byte -- i.e. at least
max_dirnamelen + max_capstrlen + 1 bytes. open_path's path buffer
(max_dirnamelen + max_capstrlen + namelen, namelen >= 1) is reused
here so that enabling LD_DEBUG=libs does not require an extra mmap
per call. */
/* Print search path. */
static void
print_search_path (struct r_search_path_elem **list,
const char *what, const char *name, char *buf)
const char *what, const char *name)
{
char buf[max_dirnamelen + max_capstrlen];
int first = 1;
_dl_debug_printf (" search path=");
@@ -1619,6 +1564,8 @@ open_verify (const char *name, int fd,
if (fd != -1)
{
ElfW(Ehdr) *ehdr;
ElfW(Phdr) *phdr;
size_t maplength;
/* We successfully opened the file. Now verify it is a file
we can use. */
@@ -1744,6 +1691,32 @@ open_verify (const char *name, int fd,
goto lose;
}
maplength = ehdr->e_phnum * sizeof (ElfW(Phdr));
if (ehdr->e_phoff + maplength <= (size_t) fbp->len)
phdr = (void *) (fbp->buf + ehdr->e_phoff);
else
{
phdr = alloca (maplength);
if ((size_t) __pread64_nocancel (fd, (void *) phdr, maplength,
ehdr->e_phoff) != maplength)
{
errval = errno;
errstring = N_("cannot read file data");
goto lose;
}
}
if (__glibc_unlikely (elf_machine_reject_phdr_p
(phdr, ehdr->e_phnum, fbp->buf, fbp->len,
loader, fd)))
{
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_LIBS))
_dl_debug_printf (" (incompatible ELF headers with the host)\n");
__close_nocancel (fd);
__set_errno (ENOENT);
return -1;
}
}
else
{
@@ -1767,6 +1740,7 @@ open_path (const char *name, size_t namelen, int mode,
bool *found_other_class)
{
struct r_search_path_elem **dirs = sps->dirs;
char *buf;
int fd = -1;
const char *current_what = NULL;
int any = 0;
@@ -1776,18 +1750,7 @@ open_path (const char *name, size_t namelen, int mode,
given on the command line when rtld is run directly. */
return -1;
/* The scratch buffer below is sized to satisfy both this function's
candidate-path construction (max_dirnamelen + max_capstrlen + namelen)
and print_search_path's buffer precondition
(max_dirnamelen + max_capstrlen + 1). An empty NAME would under-size the
buffer for the latter and would also produce a meaningless lookup (the
loader rejects empty names well before reaching here). */
assert (namelen >= 1);
size_t bufsize = max_dirnamelen + max_capstrlen + namelen;
struct dl_scratch_buffer scratch = dl_scratch_buffer_init ();
dl_scratch_buffer_allocate (&scratch, bufsize, 0);
char *buf = scratch.data;
buf = alloca (max_dirnamelen + max_capstrlen + namelen);
do
{
struct r_search_path_elem *this_dir = *dirs;
@@ -1802,7 +1765,7 @@ open_path (const char *name, size_t namelen, int mode,
&& current_what != this_dir->what)
{
current_what = this_dir->what;
print_search_path (dirs, current_what, this_dir->where, buf);
print_search_path (dirs, current_what, this_dir->where);
}
edp = (char *) __mempcpy (buf, this_dir->dirname, this_dir->dirnamelen);
@@ -1890,7 +1853,6 @@ open_path (const char *name, size_t namelen, int mode,
if (*realname != NULL)
{
memcpy (*realname, buf, buflen);
dl_scratch_buffer_free (&scratch);
return fd;
}
else
@@ -1898,7 +1860,6 @@ open_path (const char *name, size_t namelen, int mode,
/* No memory for the name, we certainly won't be able
to load and link it. */
__close_nocancel (fd);
dl_scratch_buffer_free (&scratch);
return -1;
}
}
@@ -1908,10 +1869,7 @@ open_path (const char *name, size_t namelen, int mode,
directory (for instance, if the component is a existing file meaning
essentially that the pathname is invalid - ENOTDIR). */
if (here_any && errno != ENOENT && errno != EACCES && errno != ENOTDIR)
{
dl_scratch_buffer_free (&scratch);
return -1;
}
return -1;
/* Remember whether we found anything. */
any |= here_any;
@@ -1932,7 +1890,6 @@ open_path (const char *name, size_t namelen, int mode,
sps->dirs = (void *) -1;
}
dl_scratch_buffer_free (&scratch);
return -1;
}
+2 -124
View File
@@ -21,39 +21,7 @@
#define _DL_LOAD_H 1
#include <link.h>
#include <stddef.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <libc-pointer-arith.h>
#include <stackinfo.h>
#include <not-cancel.h>
/* Type for the buffer we put the ELF header and hopefully the program
header. This buffer does not really have to be too large. In most
cases the program header follows the ELF header directly. If this
is not the case all bets are off and we can make the header
arbitrarily large and still won't get it read. This means the only
question is how large are the ELF and program header combined. The
ELF header for 32-bit files is 52 bytes long and for 64-bit files
64 bytes long. Each program header entry is 32 and 56 bytes long
respectively.
Size for at least 16 entries (with a little margin for program notes)
needs 52 + 16*32 = 564 bytes on 32-bit and 64 + 16*56 = 960 bytes on
64-bit; round up to 640 and 1024 respectively. If this heuristic
should still fail for some file the code in
`_dl_map_object_from_fd' knows how to recover. */
struct filebuf
{
ssize_t len;
#if __WORDSIZE == 32
# define FILEBUF_SIZE 640
#else
# define FILEBUF_SIZE 1024
#endif
char buf[FILEBUF_SIZE] __attribute__ ((aligned (__alignof (ElfW(Ehdr)))));
};
/* On some systems, no flag bits are given to specify file mapping. */
@@ -112,95 +80,6 @@ struct loadcmd
int prot; /* PROT_* bits. */
};
#include <dl-load-post.h>
/* Iterator for program header segments. Initialize with
_dl_pt_load_iterator_init, then either walk PT_LOAD segments via
_dl_pt_load_iterator_next or do random access via
_dl_pt_load_iterator_phdr_at. A scratch buffer (fbp->buf) is used to
batch-read program headers; if the entire program header table was
already loaded by open_verify's initial read no pread is issued. */
struct dl_pt_load_iterator
{
int fd; /* File descriptor for pread. */
struct filebuf *fbp; /* Scratch buffer for batched phdr reads. */
ElfW(Off) phoff; /* Program header table file offset. */
ElfW(Half) phnum; /* Total number of program headers. */
ElfW(Half) idx; /* Index of next header to read. */
ElfW(Half) buf_base; /* Index of phdr at start of fbp->buf
(chunked mode only). */
ElfW(Half) buf_count; /* Number of phdrs currently in fbp->buf. */
bool cached; /* True iff entire phdr table is already
resident in fbp->buf from open_verify. */
ElfW(Addr) p_align_max; /* Maximum p_align over all PT_LOAD segments. */
ElfW(Addr) pagesize; /* System page size (GLRO(dl_pagesize)). */
/* Fields below are precomputed by _dl_map_object_scan_phdrs and are
intended for use by _dl_map_segments. */
ElfW(Addr) first_mapstart; /* mapstart of the first PT_LOAD segment. */
ElfW(Addr) last_mapstart; /* mapstart of the last PT_LOAD segment. */
ElfW(Addr) last_allocend; /* allocend of the last PT_LOAD segment. */
size_t nloadcmds; /* Number of PT_LOAD segments found. */
};
/* Return a pointer to the program header at INDEX. If the entire phdr
table is already cached in fbp->buf (from open_verify), it is served
directly with no syscall; otherwise a batch of up to FILEBUF_SIZE /
sizeof(ElfW(Phdr)) entries is read into fbp->buf via a single pread.
Subsequent calls within the same batch hit the buffer. Returns NULL on
read failure (errno set by pread). */
static __always_inline const ElfW(Phdr) *
_dl_pt_load_iterator_phdr_at (struct dl_pt_load_iterator *it, ElfW(Half) idx)
{
if (__glibc_likely (it->cached))
return (const ElfW(Phdr) *) (it->fbp->buf + it->phoff) + idx;
if (idx < it->buf_base || idx >= it->buf_base + it->buf_count)
{
const ElfW(Half) phdrs_per_buf
= sizeof (it->fbp->buf) / sizeof (ElfW(Phdr));
ElfW(Half) batch = it->phnum - idx;
if (batch > phdrs_per_buf)
batch = phdrs_per_buf;
size_t bytes = (size_t) batch * sizeof (ElfW(Phdr));
ElfW(Off) off = it->phoff + (ElfW(Off)) idx * sizeof (ElfW(Phdr));
if (__pread64_nocancel (it->fd, it->fbp->buf, bytes, off)
!= (ssize_t) bytes)
return NULL;
it->buf_base = idx;
it->buf_count = batch;
}
return (const ElfW(Phdr) *) it->fbp->buf + (idx - it->buf_base);
}
/* Advance iterator IT to the next PT_LOAD segment and fill C with its
decoded load command. Returns true when a segment was found, false
when the end of the program header table has been reached or a read
error occurs. */
static __always_inline bool
_dl_pt_load_iterator_next (struct dl_pt_load_iterator *it, struct loadcmd *c)
{
while (it->idx < it->phnum)
{
const ElfW(Phdr) *ph = _dl_pt_load_iterator_phdr_at (it, it->idx);
it->idx++;
if (__glibc_unlikely (ph == NULL))
return false;
if (ph->p_type != PT_LOAD)
continue;
c->mapstart = ALIGN_DOWN (ph->p_vaddr, it->pagesize);
c->mapend = ALIGN_UP (ph->p_vaddr + ph->p_filesz, it->pagesize);
c->dataend = ph->p_vaddr + ph->p_filesz;
c->allocend = ph->p_vaddr + ph->p_memsz;
c->mapoff = ALIGN_DOWN (ph->p_offset, it->pagesize);
c->prot = pf_to_prot (ph->p_flags);
c->mapalign = it->p_align_max;
return true;
}
return false;
}
/* This is a subroutine of _dl_map_segments. It should be called for each
load command, some time after L->l_addr has been set correctly. It is
@@ -216,8 +95,6 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
/* Found the program header in this segment. */
l->l_phdr = (void *) (uintptr_t) (c->mapstart + header->e_phoff
- c->mapoff);
_dl_postprocess_loadcmd_extra (l, c);
}
@@ -236,7 +113,8 @@ _dl_postprocess_loadcmd (struct link_map *l, const ElfW(Ehdr) *header,
static const char *_dl_map_segments (struct link_map *l, int fd,
const ElfW(Ehdr) *header, int type,
struct dl_pt_load_iterator *it,
const struct loadcmd loadcmds[],
size_t nloadcmds,
const size_t maplength,
bool has_holes,
struct link_map *loader); */
+5 -29
View File
@@ -21,36 +21,12 @@
#include <stdbool.h>
/* Machine-specific data collected during the program-header scan for use
by elf_machine_reject_phdr_p. Ports that override elf_machine_reject_phdr_p
must define their own layout; this generic version carries no data. */
struct dl_machine_phdr_info
{
};
/* Initialize INFO before the program-header scan begins. */
static inline void
elf_machine_phdr_info_init (struct dl_machine_phdr_info *info
__attribute__ ((__unused__)))
{
}
/* Called once per ELF program header PH during the scan. Records any
machine-specific data from PH that elf_machine_reject_phdr_p needs. */
static inline void
elf_machine_phdr_collect (struct dl_machine_phdr_info *info
__attribute__ ((__unused__)),
const ElfW(Phdr) *ph __attribute__ ((__unused__)))
{
}
/* Return true iff the program-header data collected in INFO is incompatible
with the running host. */
/* Return true iff ELF program headers are incompatible with the running
host. */
static inline bool
elf_machine_reject_phdr_p (const struct dl_machine_phdr_info *info
__attribute__ ((__unused__)),
struct link_map *map __attribute__ ((__unused__)),
int fd __attribute__ ((__unused__)))
elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, unsigned int phnum,
const char *buf, size_t len, struct link_map *map,
int fd)
{
return false;
}
+28 -29
View File
@@ -75,14 +75,11 @@ _dl_map_segment (const struct loadcmd *c, ElfW(Addr) mappref,
static __always_inline const char *
_dl_map_segments (struct link_map *l, int fd,
const ElfW(Ehdr) *header, int type,
struct dl_pt_load_iterator *it,
const struct loadcmd loadcmds[], size_t nloadcmds,
const size_t maplength, bool has_holes,
struct link_map *loader)
{
/* Fetch the first PT_LOAD segment. _dl_pt_load_iterator_init already
verified nloadcmds > 0, so this call always succeeds. */
struct loadcmd c = { 0 };
_dl_pt_load_iterator_next (it, &c);
const struct loadcmd *c = loadcmds;
if (__glibc_likely (type == ET_DYN))
{
@@ -98,16 +95,16 @@ _dl_map_segments (struct link_map *l, int fd,
prefer to map such objects at; but this is only a preference,
the OS can do whatever it likes. */
ElfW(Addr) mappref
= (ELF_PREFERRED_ADDRESS (loader, maplength, c.mapstart)
= (ELF_PREFERRED_ADDRESS (loader, maplength, c->mapstart)
- MAP_BASE_ADDR (l));
/* Remember which part of the address space this object uses. */
l->l_map_start = _dl_map_segment (&c, mappref, maplength, fd);
l->l_map_start = _dl_map_segment (c, mappref, maplength, fd);
if (__glibc_unlikely ((void *) l->l_map_start == MAP_FAILED))
return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
l->l_map_end = l->l_map_start + maplength;
l->l_addr = l->l_map_start - c.mapstart;
l->l_addr = l->l_map_start - c->mapstart;
if (has_holes)
{
@@ -116,11 +113,12 @@ _dl_map_segments (struct link_map *l, int fd,
unallocated. Then jump into the normal segment-mapping loop to
handle the portion of the segment past the end of the file
mapping. */
if (__glibc_unlikely (it->last_mapstart < c.mapend))
return N_("ELF load command address/offset not page-aligned");
if (__glibc_unlikely (loadcmds[nloadcmds - 1].mapstart <
c->mapend))
return N_("ELF load command address/offset not page-aligned");
if (__glibc_unlikely
(__mprotect ((caddr_t) (l->l_addr + c.mapend),
it->last_mapstart - c.mapend,
(__mprotect ((caddr_t) (l->l_addr + c->mapend),
loadcmds[nloadcmds - 1].mapstart - c->mapend,
PROT_NONE) < 0))
return DL_MAP_SEGMENTS_ERROR_MPROTECT;
}
@@ -131,32 +129,32 @@ _dl_map_segments (struct link_map *l, int fd,
}
/* Remember which part of the address space this object uses. */
l->l_map_start = c.mapstart + l->l_addr;
l->l_map_start = c->mapstart + l->l_addr;
l->l_map_end = l->l_map_start + maplength;
l->l_contiguous = !has_holes;
do
while (c < &loadcmds[nloadcmds])
{
if (c.mapend > c.mapstart
if (c->mapend > c->mapstart
/* Map the segment contents from the file. */
&& (__mmap ((void *) (l->l_addr + c.mapstart),
c.mapend - c.mapstart, c.prot,
&& (__mmap ((void *) (l->l_addr + c->mapstart),
c->mapend - c->mapstart, c->prot,
MAP_FIXED|MAP_COPY|MAP_FILE,
fd, c.mapoff)
fd, c->mapoff)
== MAP_FAILED))
return DL_MAP_SEGMENTS_ERROR_MAP_SEGMENT;
postmap:
_dl_postprocess_loadcmd (l, header, &c);
_dl_postprocess_loadcmd (l, header, c);
if (c.allocend > c.dataend)
if (c->allocend > c->dataend)
{
/* Extra zero pages should appear at the end of this segment,
after the data mapped from the file. */
ElfW(Addr) zero, zeroend, zeropage;
zero = l->l_addr + c.dataend;
zeroend = l->l_addr + c.allocend;
zero = l->l_addr + c->dataend;
zeroend = l->l_addr + c->allocend;
zeropage = ((zero + GLRO(dl_pagesize) - 1)
& ~(GLRO(dl_pagesize) - 1));
@@ -168,18 +166,18 @@ _dl_map_segments (struct link_map *l, int fd,
if (zeropage > zero)
{
/* Zero the final part of the last page of the segment. */
if (__glibc_unlikely ((c.prot & PROT_WRITE) == 0))
if (__glibc_unlikely ((c->prot & PROT_WRITE) == 0))
{
/* Dag nab it. */
if (__mprotect ((caddr_t) (zero
& ~(GLRO(dl_pagesize) - 1)),
GLRO(dl_pagesize), c.prot|PROT_WRITE) < 0)
GLRO(dl_pagesize), c->prot|PROT_WRITE) < 0)
return DL_MAP_SEGMENTS_ERROR_MPROTECT;
}
memset ((void *) zero, '\0', zeropage - zero);
if (__glibc_unlikely ((c.prot & PROT_WRITE) == 0))
if (__glibc_unlikely ((c->prot & PROT_WRITE) == 0))
__mprotect ((caddr_t) (zero & ~(GLRO(dl_pagesize) - 1)),
GLRO(dl_pagesize), c.prot);
GLRO(dl_pagesize), c->prot);
}
if (zeroend > zeropage)
@@ -189,7 +187,7 @@ _dl_map_segments (struct link_map *l, int fd,
caddr_t mapat;
mapat = __mmap ((caddr_t) zeropage, zeroend - zeropage,
c.prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
c->prot, MAP_ANON|MAP_PRIVATE|MAP_FIXED,
-1, 0);
if (__glibc_unlikely (mapat == MAP_FAILED))
return DL_MAP_SEGMENTS_ERROR_MAP_ZERO_FILL;
@@ -222,12 +220,13 @@ _dl_map_segments (struct link_map *l, int fd,
}
}
}
++c;
}
while (_dl_pt_load_iterator_next (it, &c));
/* Notify ELF_PREFERRED_ADDRESS that we have to load this one
fixed. */
ELF_FIXED_ADDRESS (loader, it->last_mapstart);
ELF_FIXED_ADDRESS (loader, c->mapstart);
return NULL;
}
+27 -26
View File
@@ -658,31 +658,6 @@ dl_open_worker_begin (void *a)
bool relocation_in_progress = false;
/* This only performs the memory allocations. The actual update of
the scopes happens below, after failure is impossible. */
resize_scopes (new);
/* Increase the size of the GL (dl_tls_dtv_slotinfo_list) data
structure. */
bool any_tls = resize_tls_slotinfo (new);
/* Perform the necessary allocations for adding new global objects
to the global scope below. */
if (mode & RTLD_GLOBAL)
add_to_global_resize (new);
/* Install the new modules in the DTV slotinfo and initialise their
static TLS *before* relocation, so an IFUNC resolver firing during
the relocation loop below can reach its DSO's __thread storage via
__tls_get_addr / TLSDESC. Without this, the resolver's TLS access
for a just-loaded module would index into an unallocated DTV slot
and crash. If relocation later fails, the subsequent _dl_close_worker
cleans up these slotinfo entries via remove_slotinfo. */
if (any_tls)
/* FIXME: This calls _dl_update_slotinfo, which aborts the process
on memory allocation failure. See bug 16134. */
update_tls_slotinfo (new);
/* Perform relocation. This can trigger lazy binding in IFUNC
resolvers. For NODELETE mappings, these dependencies are not
recorded because the flag has not been applied to the newly
@@ -707,6 +682,19 @@ dl_open_worker_begin (void *a)
_dl_open_relocate_one_object (args, r, new->l_initfini[i], reloc_mode,
&relocation_in_progress);
/* This only performs the memory allocations. The actual update of
the scopes happens below, after failure is impossible. */
resize_scopes (new);
/* Increase the size of the GL (dl_tls_dtv_slotinfo_list) data
structure. */
bool any_tls = resize_tls_slotinfo (new);
/* Perform the necessary allocations for adding new global objects
to the global scope below. */
if (mode & RTLD_GLOBAL)
add_to_global_resize (new);
/* Demarcation point: After this, no recoverable errors are allowed.
All memory allocations for new objects must have happened
before. */
@@ -728,6 +716,19 @@ dl_open_worker_begin (void *a)
_dl_signal_error (ENOMEM, new->l_libname->name, NULL,
N_ ("cannot allocate address lookup data"));
/* FIXME: It is unclear whether the order here is correct.
Shouldn't new objects be made available for binding (and thus
execution) only after there TLS data has been set up fully?
Fixing bug 16134 will likely make this distinction less
important. */
/* Second stage after resize_tls_slotinfo: Update the slotinfo data
structures. */
if (any_tls)
/* FIXME: This calls _dl_update_slotinfo, which aborts the process
on memory allocation failure. See bug 16134. */
update_tls_slotinfo (new);
/* Notify the debugger all new objects have been relocated. */
if (relocation_in_progress)
LIBC_PROBE (reloc_complete, 3, args->nsid, r, new);
@@ -776,7 +777,7 @@ dl_open_worker (void *a)
#ifdef SHARED
if (was_not_consistent)
/* Avoid redundant/recursive signalling. */
/* Avoid redudant/recursive signalling. */
_dl_audit_activity_nsid (nsid, LA_ACT_CONSISTENT);
#endif
}
+6 -18
View File
@@ -31,10 +31,8 @@
#include "dynamic-link.h"
#include "get-dynamic-info.h"
/* Phase 1: relocate static PIE - non-IRELATIVE pass. IFUNC resolvers are
deferred to _dl_relocate_static_pie_ifunc so that csu/libc-start.c can
initialise the TCB (and write the stack-protector canary into it) between
the two passes. */
/* Relocate static executable with PIE. */
void
_dl_relocate_static_pie (void)
{
@@ -78,9 +76,10 @@ _dl_relocate_static_pie (void)
ELF_MACHINE_BEFORE_RTLD_RELOC (main_map, main_map->l_info);
# endif
/* Relocate ourselves so we can do normal function calls and data access
using the global offset table. IRELATIVE entries are deferred. */
ELF_DYNAMIC_RELOCATE_NOIFUNC (main_map, NULL, 0, 0);
/* Relocate ourselves so we can do normal function calls and
data access using the global offset table. */
ELF_DYNAMIC_RELOCATE (main_map, NULL, 0, 0, 0);
main_map->l_relocated = 1;
/* Initialize _r_debug_extended. */
struct r_debug *r = _dl_debug_initialize (0, LM_ID_BASE);
@@ -90,15 +89,4 @@ _dl_relocate_static_pie (void)
time. */
elf_setup_debug_entry (main_map, r);
}
/* Phase 2: run the deferred IRELATIVE entries for the static-pie main map.
Must be called after the TCB is set up and the stack-protector canary is
written, so that an instrumented IFUNC resolver does not fault. */
void
_dl_relocate_static_pie_ifunc (void)
{
struct link_map *main_map = _dl_get_dl_main_map ();
ELF_DYNAMIC_RELOCATE_IFUNC (main_map, NULL, 0, 0);
main_map->l_relocated = 1;
}
#endif
+16 -35
View File
@@ -107,21 +107,22 @@ _dl_try_allocate_static_tls (struct link_map *map, bool optional)
# error "Either TLS_TCB_AT_TP or TLS_DTV_AT_TP must be defined"
#endif
/* Initialise the static TLS region, the map may not yet be l_relocated (a
TLS reloc inside the relocation loop triggered the allocation), but
_dl_init_static_tls only writes .tdata into the static TLS slot, which is
independent of relocation state.
Doing this inline ensures any IFUNC resolver that fires later in the same
object's relocation pass sees an initialised TLS slot, and the
post-relocation TLS init loop in dl_open_worker_begin becomes a no-op for
this map. */
/* If the object is not yet relocated we cannot initialize the
static TLS region. Delay it. */
if (map->l_real->l_relocated)
{
#ifdef SHARED
/* Update the DTV of the current thread. Note: GL(dl_load_tls_lock)
is held here so normal load of the generation counter is valid. */
if (__glibc_unlikely (THREAD_DTV()[0].counter != GL(dl_tls_generation)))
_dl_update_slotinfo (map->l_tls_modid, GL(dl_tls_generation));
/* Update the DTV of the current thread. Note: GL(dl_load_tls_lock)
is held here so normal load of the generation counter is valid. */
if (__builtin_expect (THREAD_DTV()[0].counter != GL(dl_tls_generation),
0))
(void) _dl_update_slotinfo (map->l_tls_modid, GL(dl_tls_generation));
#endif
_dl_init_static_tls (map);
_dl_init_static_tls (map);
}
else
map->l_need_tls_init = 1;
return 0;
}
@@ -269,29 +270,9 @@ _dl_relocate_object_no_relro (struct link_map *l, struct r_scope_elem *scope[],
}
{
/* Do the actual relocation of the object's GOT and other data.
/* Do the actual relocation of the object's GOT and other data. */
Process the non-IRELATIVE pass first so .tdata is fully relocated
(including R_*_RELATIVE / R_*_64 fixups for TLS initialisers, e.g. a
file-scope thread-local initialised with the address of a function),
then refresh the static TLS slot before the IRELATIVE pass runs the
IFUNC resolvers. Without this, a resolver would see the unrelocated
initialiser bytes that were placed into the slot by the early
_dl_allocate_tls_init. */
ELF_DYNAMIC_RELOCATE_NOIFUNC (l, scope, lazy, consider_profiling);
#ifdef SHARED
/* Re-initialise the static TLS slot with the .tdata so the IRELATIVE
pass observes a fully-relocated initialiser image. Skipped for objects
without static TLS or before the main thread TCB has been set up. */
if (l->l_tls_blocksize != 0
&& __rtld_tls_init_tp_called
&& l->l_tls_offset != NO_TLS_OFFSET
&& l->l_tls_offset != FORCED_DYNAMIC_TLS_OFFSET)
_dl_init_static_tls (l);
#endif
ELF_DYNAMIC_RELOCATE_IFUNC (l, scope, lazy, skip_ifunc);
ELF_DYNAMIC_RELOCATE (l, scope, lazy, consider_profiling, skip_ifunc);
if ((consider_profiling || consider_symbind)
&& l->l_info[DT_PLTRELSZ] != NULL)
-90
View File
@@ -1,90 +0,0 @@
/* Loader-internal scratch buffer.
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 <dl-scratch-buffer.h>
#include <assert.h>
#include <errno.h>
#include <ldsodefs.h>
#include <libc-pointer-arith.h>
#include <libintl.h>
#include <setvmaname.h>
#include <stdlib.h>
#include <sys/mman.h>
void
_dl_scratch_buffer_allocate (struct dl_scratch_buffer *b, size_t size,
unsigned int flags)
{
/* Enforce the one-shot contract. */
assert (b->backend == DL_SCRATCH_INLINE);
bool use_malloc = !(flags & DL_SCRATCH_NO_MALLOC);
#ifdef SHARED
/* While __minimal_malloc is the active allocator, __minimal_free
only releases the most-recent block; route through mmap instead so
dl_scratch_buffer_free can symmetrically release the spill. */
if (!__rtld_malloc_is_complete ())
use_malloc = false;
#endif
if (use_malloc)
{
void *p = malloc (size);
if (__glibc_unlikely (p == NULL))
_dl_signal_error (ENOMEM, NULL, NULL,
N_("cannot allocate loader scratch buffer"));
b->data = p;
b->size = size;
b->backend = DL_SCRATCH_MALLOC;
return;
}
size_t map_size = ALIGN_UP (size, GLRO(dl_pagesize));
void *p = __mmap (NULL, map_size, PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
if (__glibc_unlikely (p == MAP_FAILED))
_dl_signal_error (ENOMEM, NULL, NULL,
N_("cannot allocate loader scratch buffer"));
__set_vma_name (p, map_size, " glibc: loader scratch");
b->data = p;
b->size = map_size;
b->backend = DL_SCRATCH_MMAP;
}
rtld_hidden_def (_dl_scratch_buffer_allocate)
void
_dl_scratch_buffer_free (struct dl_scratch_buffer *b)
{
switch (b->backend)
{
case DL_SCRATCH_MALLOC:
free (b->data);
break;
case DL_SCRATCH_MMAP:
__munmap (b->data, b->size);
break;
case DL_SCRATCH_INLINE:
/* Unreachable in normal use; guarded by the inline wrapper. */
break;
}
b->data = b->inline_data;
b->size = sizeof b->inline_data;
b->backend = DL_SCRATCH_INLINE;
}
rtld_hidden_def (_dl_scratch_buffer_free)
-145
View File
@@ -1,145 +0,0 @@
/* Loader-internal scratch buffer.
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/>. */
/* This is the loader-side analogue of <scratch_buffer.h>. It exists
because the loader has two constraints that <scratch_buffer.h> does
not address:
1. While the active allocator is __minimal_malloc (early startup,
before __rtld_malloc_init_real has switched in libc's malloc),
__minimal_free only releases the most-recent allocation -- a
malloc'd spill would silently leak.
2. Some loader code paths cannot route a spill through the
interposable malloc at all because the user malloc may
recursively re-enter the loader and invalidate state we are
copying from (the canonical example is _dl_load_cache_lookup
copying out of the file-backed ld.so.cache mapping).
The buffer starts in a stack-resident inline area; if the caller
needs more bytes, the spill is to anonymous mmap (always safe,
tagged for /proc/self/maps visibility) or to malloc (cheaper, only
chosen when both the active allocator is real malloc and the
caller does not pass DL_SCRATCH_NO_MALLOC).
Typical usage:
struct dl_scratch_buffer scratch = dl_scratch_buffer_init ();
dl_scratch_buffer_allocate (&scratch, needed, 0);
... use scratch.data ...
dl_scratch_buffer_free (&scratch);
The interface is one-shot: every consumer knows the required size
upfront and calls dl_scratch_buffer_allocate exactly once, so there
is no incremental-growth model. A second allocate without an
intervening free is a programming error and is checked by an
assertion in _dl_scratch_buffer_allocate.
On allocation failure dl_scratch_buffer_allocate does not return;
it raises a loader ENOMEM via _dl_signal_error. Callers may
therefore treat scratch.data as valid after a successful return. */
#ifndef _DL_SCRATCH_BUFFER_H
#define _DL_SCRATCH_BUFFER_H 1
#include <stdbool.h>
#include <stddef.h>
#include <sys/cdefs.h>
/* Size of the inline area. Tuned to cover typical ld.so.cache
entries (well under 256 bytes) so that the common case stays
entirely on-stack with no syscall and no malloc. */
enum { DL_SCRATCH_BUFFER_INLINE_SIZE = 256 };
enum dl_scratch_backend
{
DL_SCRATCH_INLINE,
DL_SCRATCH_MMAP,
DL_SCRATCH_MALLOC,
};
struct dl_scratch_buffer
{
void *data;
size_t size;
enum dl_scratch_backend backend;
char inline_data[DL_SCRATCH_BUFFER_INLINE_SIZE]
__attribute__ ((aligned (__alignof__ (max_align_t))));
};
enum
{
/* Forbid the malloc backend for spill allocations -- the spill must
come from anonymous mmap so that interposed user malloc cannot
recursively re-enter the loader and invalidate state the caller
is copying from. See _dl_load_cache_lookup. */
DL_SCRATCH_NO_MALLOC = 1 << 0,
};
/* Return a freshly-initialized scratch buffer suitable for use as a
stack-resident initializer. */
static __always_inline __attribute_warn_unused_result__
struct dl_scratch_buffer
dl_scratch_buffer_init (void)
{
return (struct dl_scratch_buffer) {
.data = NULL,
.size = sizeof ((struct dl_scratch_buffer *) 0)->inline_data,
.backend = DL_SCRATCH_INLINE,
};
}
extern void _dl_scratch_buffer_allocate (struct dl_scratch_buffer *b,
size_t size, unsigned int flags)
__nonnull ((1)) attribute_hidden;
rtld_hidden_proto (_dl_scratch_buffer_allocate)
extern void _dl_scratch_buffer_free (struct dl_scratch_buffer *b)
__nonnull ((1)) attribute_hidden;
rtld_hidden_proto (_dl_scratch_buffer_free)
/* Ensure B->data points to a buffer of at least SIZE bytes; updates
B->size and B->backend accordingly. Intended to be called exactly
once per buffer lifetime (callers know the required size upfront --
there is no incremental growth model). Raises a loader ENOMEM
error via _dl_signal_error on failure -- does not return NULL. */
static __always_inline __nonnull ((1)) void
dl_scratch_buffer_allocate (struct dl_scratch_buffer *b, size_t size,
unsigned int flags)
{
/* First call after dl_scratch_buffer_init: point .data at the
caller's inline area now that its address is in scope. */
if (__glibc_unlikely (b->data == NULL))
b->data = b->inline_data;
if (__glibc_likely (size <= b->size))
return;
_dl_scratch_buffer_allocate (b, size, flags);
}
/* Release any out-of-line allocation held by B and restore the
inline state. Safe to call multiple times (and on an already-freed
or freshly-initialized buffer). */
static __always_inline __nonnull ((1)) void
dl_scratch_buffer_free (struct dl_scratch_buffer *b)
{
if (__glibc_likely (b->backend == DL_SCRATCH_INLINE))
return;
_dl_scratch_buffer_free (b);
}
#endif /* dl-scratch-buffer.h */
-6
View File
@@ -179,12 +179,6 @@ int _dl_stack_cache_lock;
#endif
struct dl_scope_free_list *_dl_scope_free_list;
#ifdef HAVE_THP
enum dl_elf_thp_control_t _dl_elf_thp_control;
enum thp_mode_t _dl_thp_mode;
size_t _dl_elf_thp_pagesize;
#endif
#ifdef NEED_DL_SYSINFO
/* Needed for improved syscall handling on at least x86/Linux. NB: Don't
initialize it here to avoid RELATIVE relocation in static PIE. */
+5 -9
View File
@@ -22,16 +22,12 @@ static struct link_map *
_dl_sym_find_caller_link_map (ElfW(Addr) caller)
{
struct link_map *l = _dl_find_dso_for_object (caller);
/* A constructor that tail-calls dlsym makes the caller address point
into the dynamic linker itself. The ld.so link map has no l_scope
set, so using it for a symbol lookup would dereference NULL. Treat
that like an unknown caller. */
if (l != NULL && l->l_scope != NULL)
if (l != NULL)
return l;
/* The address does not belong to any loaded object (e.g. it is in
JIT-generated code or in the main program). Fall back to the main
program's link map. */
return GL(dl_ns)[LM_ID_BASE]._ns_loaded;
else
/* If the address is not recognized the call comes from the main
program (we hope). */
return GL(dl_ns)[LM_ID_BASE]._ns_loaded;
}
/* Translates RESULT, *REF, VALUE into a symbol address from the point
+3 -3
View File
@@ -339,7 +339,7 @@ _dl_determine_tlsoffset (void)
/* Extra TLS block for internal usage to append at the end of the TLS blocks
(in allocation order). The address at which the block is allocated must
be aligned to 'extra_tls_align'. The size of the block as returned by
'_dl_extra_tls_get_size ()' is always a multiple of the alignment.
'_dl_extra_tls_get_size ()' is always a multiple of the aligment.
On Linux systems this is where the rseq area will be allocated. On other
systems it is currently unused and both values will be '0'. */
@@ -419,7 +419,7 @@ _dl_determine_tlsoffset (void)
/* Extra TLS block for internal usage to append at the end of the TLS blocks
(in allocation order). The address at which the block is allocated must
be aligned to 'extra_tls_align'. The size of the block as returned by
'_dl_extra_tls_get_size ()' is always a multiple of the alignment.
'_dl_extra_tls_get_size ()' is always a multiple of the aligment.
On Linux systems this is where the rseq area will be allocated. On other
systems it is currently unused and both values will be '0'. */
@@ -699,7 +699,7 @@ _dl_allocate_tls_init (void *result, bool main_thread)
initialization because it would already be set by the
audit setup, which uses the dlopen code and already
clears l_need_tls_init. Calls with !main_thread from
pthread_create need to initialize TLS for the current
pthread_create need to initialze TLS for the current
thread regardless of namespace. */
if (map->l_ns != LM_ID_BASE && main_thread)
continue;
+96 -138
View File
@@ -23,9 +23,6 @@
#ifdef DO_RELA
# define elf_dynamic_do_Rel elf_dynamic_do_Rela
# define elf_dynamic_do_Rel_irelative elf_dynamic_do_Rela_irelative
# define elf_dynamic_is_Rel_irelative elf_dynamic_is_Rela_irelative
# define elf_dynamic_Rel_audit_symbind elf_dynamic_Rela_audit_symbind
# define Rel Rela
# define elf_machine_rel elf_machine_rela
# define elf_machine_rel_relative elf_machine_rela_relative
@@ -37,53 +34,16 @@
(void *) (l_addr + relative->r_offset))
#endif
static __always_inline bool
elf_dynamic_is_Rel_irelative (const ElfW(Rel) *reloc, const ElfW(Sym) *sym)
{
#ifdef ELF_MACHINE_IRELATIVE
const unsigned int r_type = ELFW (R_TYPE) (reloc->r_info);
return ((sym != NULL
&& ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC
&& sym->st_shndx != SHN_UNDEF)
|| r_type == ELF_MACHINE_IRELATIVE);
#else
return false;
#endif
}
static __always_inline void
elf_dynamic_Rel_audit_symbind (struct link_map *map,
struct r_scope_elem *scope[],
const ElfW(Rel) *reloc, const ElfW(Sym) *sym,
const struct r_found_version *rversion,
void *r_addr_arg)
{
#if defined SHARED
if (ELFW(R_TYPE) (reloc->r_info) == ELF_MACHINE_JMP_SLOT
&& GLRO(dl_naudit) > 0)
{
struct link_map *sym_map
= RESOLVE_MAP (map, scope, &sym, rversion, ELF_MACHINE_JMP_SLOT);
if (sym != NULL)
_dl_audit_symbind (map, NULL, reloc, sym, r_addr_arg, sym_map, false);
}
#endif
}
/* Perform the relocations in MAP on the running program image as specified
by RELTAG, SZTAG. If LAZY is nonzero, this is the first pass on PLT
relocations; they should be set up to call _dl_runtime_resolve, rather
than fully resolved now.
IRELATIVE entries are always skipped (non-bootstrap); they are handled
separately by elf_dynamic_do_Rel_irelative after all other relocations
for both .rel.dyn and .rel.plt have been processed. */
than fully resolved now. */
static inline void __attribute__ ((always_inline))
elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
ElfW(Addr) reladdr, ElfW(Addr) relsize,
__typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative,
int lazy)
int lazy, int skip_ifunc)
{
const ElfW(Rel) *relative = (const void *) reladdr;
const ElfW(Rel) *r = relative + nrelative;
@@ -105,9 +65,14 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
void *const r_addr_arg = (void *) (l_addr + r->r_offset);
const struct r_found_version *rversion = &map->l_versions[ndx];
elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, 0);
elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, skip_ifunc);
}
#else /* !RTLD_BOOTSTRAP */
# if defined ELF_MACHINE_IRELATIVE
const ElfW(Rel) *r2 = NULL;
const ElfW(Rel) *end2 = NULL;
# endif
#if !defined DO_RELA || !defined ELF_MACHINE_PLT_REL
/* We never bind lazily during ld.so bootstrap. Unfortunately gcc is
not clever enough to see through all the function calls to realize
@@ -116,12 +81,23 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
{
/* Doing lazy PLT relocations; they need very little info. */
for (; r < end; ++r)
{
const ElfW (Sym) *sym = &symtab[ELFW (R_SYM) (r->r_info)];
if (elf_dynamic_is_Rel_irelative (r, sym))
continue;
elf_machine_lazy_rel (map, scope, l_addr, r, 0);
}
# ifdef ELF_MACHINE_IRELATIVE
if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
{
if (r2 == NULL)
r2 = r;
end2 = r;
}
else
# endif
elf_machine_lazy_rel (map, scope, l_addr, r, skip_ifunc);
# ifdef ELF_MACHINE_IRELATIVE
if (r2 != NULL)
for (; r2 <= end2; ++r2)
if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE)
elf_machine_lazy_rel (map, scope, l_addr, r2, skip_ifunc);
# endif
}
else
#endif
@@ -149,13 +125,46 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
void *const r_addr_arg = (void *) (l_addr + r->r_offset);
const struct r_found_version *rversion = &map->l_versions[ndx];
#if defined ELF_MACHINE_IRELATIVE
if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
{
if (r2 == NULL)
r2 = r;
end2 = r;
continue;
}
#endif
if (elf_dynamic_is_Rel_irelative (r, sym))
continue;
elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, 0);
elf_dynamic_Rel_audit_symbind (map, scope, r, sym, rversion,
r_addr_arg);
elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg,
skip_ifunc);
#if defined SHARED
if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
&& GLRO(dl_naudit) > 0)
{
struct link_map *sym_map
= RESOLVE_MAP (map, scope, &sym, rversion,
ELF_MACHINE_JMP_SLOT);
if (sym != NULL)
_dl_audit_symbind (map, NULL, r, sym, r_addr_arg, sym_map,
false);
}
#endif
}
#if defined ELF_MACHINE_IRELATIVE
if (r2 != NULL)
for (; r2 <= end2; ++r2)
if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE)
{
ElfW(Half) ndx
= version[ELFW(R_SYM) (r2->r_info)] & 0x7fff;
elf_machine_rel (map, scope, r2,
&symtab[ELFW(R_SYM) (r2->r_info)],
&map->l_versions[ndx],
(void *) (l_addr + r2->r_offset),
skip_ifunc);
}
#endif
}
else
{
@@ -163,97 +172,46 @@ elf_dynamic_do_Rel (struct link_map *map, struct r_scope_elem *scope[],
{
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
void *const r_addr_arg = (void *) (l_addr + r->r_offset);
if (elf_dynamic_is_Rel_irelative (r, sym))
continue;
elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg, 0);
elf_dynamic_Rel_audit_symbind (map, scope, r, sym, NULL,
r_addr_arg);
# ifdef ELF_MACHINE_IRELATIVE
if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_IRELATIVE)
{
if (r2 == NULL)
r2 = r;
end2 = r;
continue;
}
# endif
elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg,
skip_ifunc);
# if defined SHARED
if (ELFW(R_TYPE) (r->r_info) == ELF_MACHINE_JMP_SLOT
&& GLRO(dl_naudit) > 0)
{
struct link_map *sym_map
= RESOLVE_MAP (map, scope, &sym,
(struct r_found_version *) NULL,
ELF_MACHINE_JMP_SLOT);
if (sym != NULL)
_dl_audit_symbind (map, NULL, r, sym,r_addr_arg, sym_map,
false);
}
# endif
}
# ifdef ELF_MACHINE_IRELATIVE
if (r2 != NULL)
for (; r2 <= end2; ++r2)
if (ELFW(R_TYPE) (r2->r_info) == ELF_MACHINE_IRELATIVE)
elf_machine_rel (map, scope, r2, &symtab[ELFW(R_SYM) (r2->r_info)],
NULL, (void *) (l_addr + r2->r_offset),
skip_ifunc);
# endif
}
}
#endif /* !RTLD_BOOTSTRAP */
}
/* Process only IRELATIVE entries (and other relocations targeting a defined
STT_GNU_IFUNC symbol) in the relocation range [reladdr, reladdr+relsize).
The first NRELATIVE entries are R_*_RELATIVE and are skipped without
inspection. When lazy is non-zero the PLT lazy-binding path
(elf_machine_lazy_rel) is used, otherwise the full non-lazy path
(elf_machine_rel) is used.
Called by _ELF_DYNAMIC_DO_RELOC after all non-IRELATIVE relocations have
been processed for both .rela.dyn and .rela.plt, so that IRELATIVE
resolvers may call PLT stubs safely regardless of which section the linker
placed R_*_IRELATIVE in. */
static __always_inline void
elf_dynamic_do_Rel_irelative (struct link_map *map,
struct r_scope_elem *scope[],
ElfW(Addr) reladdr, ElfW(Addr) relsize,
__typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative,
int lazy, int skip_ifunc)
{
# ifdef ELF_MACHINE_IRELATIVE
const ElfW(Rel) *r = (const ElfW(Rel) *) reladdr + nrelative;
const ElfW(Rel) *end = (const void *) (reladdr + relsize);
ElfW(Addr) l_addr = map->l_addr;
const ElfW(Sym) *const symtab = (const void *) D_PTR (map, l_info[DT_SYMTAB]);
if (lazy)
{
for (; r < end; ++r)
{
const ElfW (Sym) *sym = &symtab[ELFW (R_SYM) (r->r_info)];
if (!elf_dynamic_is_Rel_irelative (r, sym))
continue;
elf_machine_lazy_rel (map, scope, l_addr, r, skip_ifunc);
}
}
else
{
if (map->l_info[VERSYMIDX (DT_VERSYM)])
{
const ElfW(Half) *const version =
(const void *) D_PTR (map, l_info[VERSYMIDX (DT_VERSYM)]);
for (; r < end; ++r)
{
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
void *const r_addr_arg = (void *) (l_addr + r->r_offset);
if (!elf_dynamic_is_Rel_irelative (r, sym))
continue;
ElfW(Half) ndx = version[ELFW(R_SYM) (r->r_info)] & 0x7fff;
const struct r_found_version *rversion = &map->l_versions[ndx];
elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg,
skip_ifunc);
elf_dynamic_Rel_audit_symbind (map, scope, r, sym, rversion,
r_addr_arg);
}
}
else
{
for (; r < end; ++r)
{
const ElfW(Sym) *sym = &symtab[ELFW(R_SYM) (r->r_info)];
void *const r_addr_arg = (void *) (l_addr + r->r_offset);
if (!elf_dynamic_is_Rel_irelative (r, sym))
continue;
elf_machine_rel (map, scope, r, sym, NULL, r_addr_arg,
skip_ifunc);
elf_dynamic_Rel_audit_symbind (map, scope, r, sym, NULL,
r_addr_arg);
}
}
}
# endif
}
#undef elf_dynamic_do_Rel
#undef elf_dynamic_do_Rel_irelative
#undef elf_dynamic_is_Rel_irelative
#undef elf_dynamic_Rel_audit_symbind
#undef Rel
#undef elf_machine_rel
#undef elf_machine_rel_relative
+10 -84
View File
@@ -78,18 +78,7 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
consumes precisely the very end of the DT_REL*, or DT_JMPREL and DT_REL*
are completely separate and there is a gap between them. */
/* This controls which sub-passes _ELF_DYNAMIC_DO_RELOC runs. Used to
interleave TLS / stack-protector setup between the two passes so IFUNC
resolvers see a fully-initialised TCB. */
enum elf_dynamic_reloc_phase
{
DL_RELOC_BOTH = 0, /* Non-IRELATIVE pass then IRELATIVE pass. */
DL_RELOC_NOIFUNC = 1, /* Non-IRELATIVE pass only. */
DL_RELOC_IFUNC = 2, /* IRELATIVE pass only. */
};
# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, scope, do_lazy, skip_ifunc, \
test_rel, phase) \
# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, scope, do_lazy, skip_ifunc, test_rel) \
do { \
struct { ElfW(Addr) start, size; \
__typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative; int lazy; } \
@@ -129,36 +118,13 @@ enum elf_dynamic_reloc_phase
} \
} \
\
/* Defer all IRELATIVE relocations until after all non-IRELATIVE \
relocations (including PLT lazy-binding setup) have been processed \
for both sections. This ensures IRELATIVE resolvers can call PLT \
stubs safely regardless of which section R_*_IRELATIVE was placed in \
by the linker. */ \
if (!DO_RTLD_BOOTSTRAP) \
{ \
if ((phase) != DL_RELOC_IFUNC) \
for (int ranges_index = 0; ranges_index < 2; ++ranges_index) \
elf_dynamic_do_##reloc ((map), scope, \
ranges[ranges_index].start, \
ranges[ranges_index].size, \
ranges[ranges_index].nrelative, \
ranges[ranges_index].lazy); \
if ((phase) != DL_RELOC_NOIFUNC) \
for (int ranges_index = 0; ranges_index < 2; ++ranges_index) \
elf_dynamic_do_##reloc##_irelative ((map), scope, \
ranges[ranges_index].start, \
ranges[ranges_index].size, \
ranges[ranges_index].nrelative,\
ranges[ranges_index].lazy, \
skip_ifunc); \
} \
else \
for (int ranges_index = 0; ranges_index < 2; ++ranges_index) \
elf_dynamic_do_##reloc ((map), scope, \
ranges[ranges_index].start, \
ranges[ranges_index].size, \
ranges[ranges_index].nrelative, \
ranges[ranges_index].lazy); \
for (int ranges_index = 0; ranges_index < 2; ++ranges_index) \
elf_dynamic_do_##reloc ((map), scope, \
ranges[ranges_index].start, \
ranges[ranges_index].size, \
ranges[ranges_index].nrelative, \
ranges[ranges_index].lazy, \
skip_ifunc); \
} while (0)
# if ELF_MACHINE_NO_REL || ELF_MACHINE_NO_RELA
@@ -170,36 +136,18 @@ enum elf_dynamic_reloc_phase
# if ! ELF_MACHINE_NO_REL
# include "do-rel.h"
# define ELF_DYNAMIC_DO_REL(map, scope, lazy, skip_ifunc) \
_ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, skip_ifunc, \
_ELF_CHECK_REL, DL_RELOC_BOTH)
# define ELF_DYNAMIC_DO_REL_NOIFUNC(map, scope, lazy) \
_ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, 0, \
_ELF_CHECK_REL, DL_RELOC_NOIFUNC)
# define ELF_DYNAMIC_DO_REL_IFUNCONLY(map, scope, lazy, skip_ifunc) \
_ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, skip_ifunc, \
_ELF_CHECK_REL, DL_RELOC_IFUNC)
_ELF_DYNAMIC_DO_RELOC (REL, Rel, map, scope, lazy, skip_ifunc, _ELF_CHECK_REL)
# else
# define ELF_DYNAMIC_DO_REL(map, scope, lazy, skip_ifunc) /* Nothing to do. */
# define ELF_DYNAMIC_DO_REL_NOIFUNC(map, scope, lazy) /* Nothing to do. */
# define ELF_DYNAMIC_DO_REL_IFUNCONLY(map, scope, lazy, skip_ifunc) /* Nothing. */
# endif
# if ! ELF_MACHINE_NO_RELA
# define DO_RELA
# include "do-rel.h"
# define ELF_DYNAMIC_DO_RELA(map, scope, lazy, skip_ifunc) \
_ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, skip_ifunc, \
_ELF_CHECK_REL, DL_RELOC_BOTH)
# define ELF_DYNAMIC_DO_RELA_NOIFUNC(map, scope, lazy) \
_ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, 0, \
_ELF_CHECK_REL, DL_RELOC_NOIFUNC)
# define ELF_DYNAMIC_DO_RELA_IFUNCONLY(map, scope, lazy, skip_ifunc) \
_ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, skip_ifunc, \
_ELF_CHECK_REL, DL_RELOC_IFUNC)
_ELF_DYNAMIC_DO_RELOC (RELA, Rela, map, scope, lazy, skip_ifunc, _ELF_CHECK_REL)
# else
# define ELF_DYNAMIC_DO_RELA(map, scope, lazy, skip_ifunc) /* Nothing to do. */
# define ELF_DYNAMIC_DO_RELA_NOIFUNC(map, scope, lazy) /* Nothing to do. */
# define ELF_DYNAMIC_DO_RELA_IFUNCONLY(map, scope, lazy, skip_ifunc) /* Nothing. */
# endif
# define ELF_DYNAMIC_DO_RELR(map) \
@@ -251,26 +199,4 @@ enum elf_dynamic_reloc_phase
ELF_DYNAMIC_AFTER_RELOC ((map), (edr_lazy)); \
} while (0)
/* Like ELF_DYNAMIC_RELOCATE but only processes the non-IRELATIVE pass.
The IRELATIVE pass must be completed later via ELF_DYNAMIC_RELOCATE_IFUNC.
Used by the static-pie startup so the TCB and stack-protector canary can
be initialised between the two passes. */
# define ELF_DYNAMIC_RELOCATE_NOIFUNC(map, scope, lazy, consider_profile) \
do { \
int edr_lazy = elf_machine_runtime_setup ((map), (scope), (lazy), \
(consider_profile)); \
if (!is_rtld_link_map (map) || DO_RTLD_BOOTSTRAP) \
ELF_DYNAMIC_DO_RELR (map); \
ELF_DYNAMIC_DO_REL_NOIFUNC ((map), (scope), edr_lazy); \
ELF_DYNAMIC_DO_RELA_NOIFUNC ((map), (scope), edr_lazy); \
ELF_DYNAMIC_AFTER_RELOC ((map), (edr_lazy)); \
} while (0)
/* IRELATIVE-only companion to ELF_DYNAMIC_RELOCATE_NOIFUNC. */
# define ELF_DYNAMIC_RELOCATE_IFUNC(map, scope, lazy, skip_ifunc) \
do { \
ELF_DYNAMIC_DO_REL_IFUNCONLY ((map), (scope), (lazy), skip_ifunc); \
ELF_DYNAMIC_DO_RELA_IFUNCONLY ((map), (scope), (lazy), skip_ifunc); \
} while (0)
#endif
-153
View File
@@ -1,153 +0,0 @@
#!/usr/bin/python3
# Generate a crafted ELF with a large number of PT_NULL program headers
# for tst-bz26577.
# 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/>.
# The generated file is an ET_DYN ELF with EVIL_PHNUM (0x7FFF) program
# headers. The first header is a PT_LOAD covering the ELF header itself so
# the dynamic linker actually attempts to map the object and exercises the
# iterator path that replaced the old loadcmd VLA. The remaining headers
# are PT_NULL. The object has no PT_DYNAMIC, so dlopen and LD_PRELOAD will
# both fail gracefully after loading.
import os
import struct
import sys
import glibcelf
# Must match the definition in tst-bz26577.c.
EVIL_PHNUM = 0x7FFF
def main():
if len(sys.argv) != 3:
print('usage: gen-tst-bz26577-mod.py OUTPUT REF-ELF', file=sys.stderr)
sys.exit(1)
output_path = sys.argv[1]
ref_elf_path = sys.argv[2]
# Read EI_CLASS, EI_DATA and e_machine from a target ELF (ld.so) so
# the generated file matches the target ABI, not the host Python.
ref = glibcelf.Image.readfile(ref_elf_path)
ei_class = ref.ehdr.e_ident.ei_class # ElfClass.ELFCLASS32 or ELFCLASS64
ei_data = ref.ehdr.e_ident.ei_data # ElfData.ELFDATA2LSB or ELFDATA2MSB
e_machine = ref.ehdr.e_machine # Machine.*
endian = '<' if ei_data == glibcelf.ElfData.ELFDATA2LSB else '>'
is64 = (ei_class == glibcelf.ElfClass.ELFCLASS64)
ehdr_size = glibcelf.Ehdr.layouts[(ei_class, ei_data)].size
phdr_size = glibcelf.Phdr.layouts[(ei_class, ei_data)].size
# File must hold the ELF header plus the full program header table so
# that pread() in open_verify and _dl_map_object_from_fd can read all
# EVIL_PHNUM entries without a short read.
total = ehdr_size + EVIL_PHNUM * phdr_size
# Assume workable value, the binary should be reject by the loader anyway.
pagesize = 4096
total = (total + pagesize - 1) & ~(pagesize - 1)
buf = bytearray(total)
# ELF Header:
buf[0:4] = b'\x7fELF'
buf[4] = ei_class.value
buf[5] = ei_data.value
buf[6] = 1 # EV_CURRENT
buf[7] = 0 # ELFOSABI_SYSV
# bytes 8..15 remain zero (padding)
# Pack the ELF header fields that follow e_ident.
# ELF64 Ehdr layout (after e_ident[16]):
# e_type(H) e_machine(H) e_version(I)
# e_entry(Q) e_phoff(Q) e_shoff(Q)
# e_flags(I) e_ehsize(H) e_phentsize(H)
# e_phnum(H) e_shentsize(H) e_shnum(H) e_shstrndx(H)
# ELF32 Ehdr layout (after e_ident[16]):
# e_type(H) e_machine(H) e_version(I)
# e_entry(I) e_phoff(I) e_shoff(I)
# e_flags(I) e_ehsize(H) e_phentsize(H)
# e_phnum(H) e_shentsize(H) e_shnum(H) e_shstrndx(H)
if is64:
fmt = endian + '2HI3QI6H'
else:
fmt = endian + '2H5I6H'
phoff = ehdr_size # program header table immediately follows Ehdr
fields = (
glibcelf.Et.ET_DYN.value, # e_type
e_machine.value, # e_machine
1, # e_version (EV_CURRENT)
0, # e_entry
phoff, # e_phoff
0, # e_shoff
0, # e_flags
ehdr_size, # e_ehsize
phdr_size, # e_phentsize
EVIL_PHNUM, # e_phnum
0, # e_shentsize
0, # e_shnum
0, # e_shstrndx
)
struct.pack_into(fmt, buf, 16, *fields)
# Write the first program header as PT_LOAD covering the ELF header
# (p_offset=0, p_filesz=ehdr_size, p_memsz=ehdr_size, PF_R). This
# ensures the dynamic linker actually maps the segment and exercises
# the PT_LOAD iterator path rather than aborting early with
# "no loadable segments". The remaining EVIL_PHNUM-1 headers stay
# zero (PT_NULL).
#
# ELF64 Phdr field order (layout '2I6Q'):
# p_type(I) p_flags(I) p_offset(Q) p_vaddr(Q) p_paddr(Q)
# p_filesz(Q) p_memsz(Q) p_align(Q)
# ELF32 Phdr field order (layout '8I'):
# p_type(I) p_offset(I) p_vaddr(I) p_paddr(I)
# p_filesz(I) p_memsz(I) p_flags(I) p_align(I)
if is64:
phdr_fmt = endian + '2I6Q'
phdr_fields = (
glibcelf.Pt.PT_LOAD.value, # p_type
glibcelf.Pf.PF_R.value, # p_flags
0, # p_offset
0, # p_vaddr
0, # p_paddr
ehdr_size, # p_filesz
ehdr_size, # p_memsz
pagesize, # p_align
)
else:
phdr_fmt = endian + '8I'
phdr_fields = (
glibcelf.Pt.PT_LOAD.value, # p_type
0, # p_offset
0, # p_vaddr
0, # p_paddr
ehdr_size, # p_filesz
ehdr_size, # p_memsz
glibcelf.Pf.PF_R.value, # p_flags
pagesize, # p_align
)
struct.pack_into(phdr_fmt, buf, ehdr_size, *phdr_fields)
with open(output_path, 'wb') as f:
f.write(buf)
if __name__ == '__main__':
main()
+29 -32
View File
@@ -35,7 +35,6 @@
#include <unsecvars.h>
#include <dl-cache.h>
#include <dl-osinfo.h>
#include <dl-reseed-random.h>
#include <dl-prop.h>
#include <dl-vdso.h>
#include <dl-vdso-setup.h>
@@ -53,7 +52,6 @@
#include <dl-find_object.h>
#include <dl-audit-check.h>
#include <dl-call_tls_init_tp.h>
#include <dl-exec-post.h>
#include <assert.h>
@@ -168,8 +166,12 @@ rtld_hidden_data_def (_dl_argv)
uintptr_t __stack_chk_guard attribute_relro;
#endif
/* Only exported for architectures that don't store the pointer guard
value in thread local area. */
uintptr_t __pointer_chk_guard_local attribute_relro attribute_hidden;
#ifndef THREAD_SET_POINTER_GUARD
strong_alias (__pointer_chk_guard_local, __pointer_chk_guard)
#endif
/* Check that AT_SECURE=0, or that the passed name does not contain
directories and is not overly long. Reject empty names
@@ -825,12 +827,15 @@ security_init (void)
/* Set up the pointer guard as well, if necessary. */
uintptr_t pointer_chk_guard
= _dl_setup_pointer_guard (_dl_random, stack_chk_guard);
#ifdef THREAD_SET_POINTER_GUARD
THREAD_SET_POINTER_GUARD (pointer_chk_guard);
#endif
__pointer_chk_guard_local = pointer_chk_guard;
/* We do not need the _dl_random value anymore. Scrub the AT_RANDOM
bytes and clear the pointer; on targets with an entropy source, refill
the bytes with fresh random data. */
_dl_reseed_random (&_dl_random);
/* We do not need the _dl_random value anymore. The less
information we leave behind, the better, so clear the
variable. */
_dl_random = NULL;
}
#include <setup-vdso.h>
@@ -1204,8 +1209,14 @@ rtld_setup_main_map (struct link_map *main_map)
main_map->l_relro_size = ph->p_memsz;
break;
}
_dl_executable_postprocess (main_map, phdr, phnum);
/* Process program headers again, but scan them backwards since
PT_GNU_PROPERTY is close to the end of program headers. */
for (const ElfW(Phdr) *ph = &phdr[phnum]; ph != phdr; --ph)
if (ph[-1].p_type == PT_GNU_PROPERTY)
{
_dl_process_pt_gnu_property (main_map, -1, &ph[-1]);
break;
}
/* Adjust the address of the TLS initialization image in case
the executable is actually an ET_DYN object. */
@@ -1578,9 +1589,6 @@ dl_main (const ElfW(Phdr) *phdr,
{
RTLD_TIMING_VAR (start);
rtld_timer_start (&start);
#ifdef HAVE_THP
_dl_get_thp_config ();
#endif
_dl_map_object (NULL, rtld_progname, lt_executable, 0,
__RTLD_OPENEXEC, LM_ID_BASE);
rtld_timer_stop (&load_time, start);
@@ -2255,26 +2263,6 @@ dl_main (const ElfW(Phdr) *phdr,
rtld_timer_accum (&relocate_time, start);
}
/* Populate the DTV slotinfo and copy each TLS module's into thestatic TLS
block *before* the relocation loop. IFUNC resolvers fired during phase 2
of the per-object two-phase scheme therefore observe initialised TLS. */
if (__rtld_tls_init_tp_called)
{
unsigned int i = main_map->l_searchlist.r_nlist;
while (i-- > 0)
{
struct link_map *l = main_map->l_initfini[i];
if (l->l_tls_blocksize != 0)
_dl_add_to_slotinfo (l, true);
}
/* _dl_add_to_slotinfo records gen = dl_tls_generation + 1, and
_dl_allocate_tls_init asserts gen <= dl_tls_generation, so bump
the generation before init. */
if (GL(dl_tls_max_dtv_idx) > 0)
++GL(dl_tls_generation);
_dl_allocate_tls_init (tcbp, true);
}
RTLD_TIMING_VAR (start);
rtld_timer_start (&start);
{
@@ -2298,6 +2286,10 @@ dl_main (const ElfW(Phdr) *phdr,
_dl_relocate_object (l, l->l_scope, GLRO(dl_lazy) ? RTLD_LAZY : 0,
consider_profiling);
/* Add object to slot information data if necessary. */
if (l->l_tls_blocksize != 0 && __rtld_tls_init_tp_called)
_dl_add_to_slotinfo (l, true);
}
}
rtld_timer_stop (&relocate_time, start);
@@ -2318,7 +2310,12 @@ dl_main (const ElfW(Phdr) *phdr,
|| count_modids != _dl_count_modids ())
++GL(dl_tls_generation);
/* TLS .tdata copy moved before the relocation loop above. */
/* Now that we have completed relocation, the initializer data
for the TLS blocks has its final values and we can copy them
into the main thread's TLS area, which we allocated above.
Note: thread-local variables must only be accessed after completing
the next step. */
_dl_allocate_tls_init (tcbp, true);
/* And finally install it for the main thread. */
if (! __rtld_tls_init_tp_called)
-10
View File
@@ -81,15 +81,5 @@ __rtld_static_init (struct link_map *map)
dl->_dl_find_object = _dl_find_object;
dl->_dl_readonly_area = _dl_readonly_area;
extern uintptr_t __pointer_chk_guard_local attribute_hidden;
const ElfW(Sym) *guard_sym
= _dl_lookup_direct (map, "__pointer_chk_guard",
0x69f99cab, /* _dl_new_hash output. */
"GLIBC_PRIVATE",
0x0963cf85); /* _dl_elf_hash output. */
assert (guard_sym != NULL);
*(uintptr_t *) DL_SYMBOL_ADDRESS (map, guard_sym)
= __pointer_chk_guard_local;
__rtld_static_init_arch (map, dl);
}
+7 -30
View File
@@ -1,5 +1,6 @@
/* Test header that defines feature-marking macros used in some test
assembly files where sysdep.h cannot be included for some reason.
/* Test header that defines macros for GNU properties that need to be
used in some test assembly files where sysdep.h cannot be included
for some reason.
Copyright (C) 2024-2026 Free Software Foundation, Inc.
This file is part of the GNU C Library.
@@ -26,6 +27,7 @@
#define FEATURE_1_PAC 2
#define FEATURE_1_GCS 4
/* Add a NT_GNU_PROPERTY_TYPE_0 note. */
#define GNU_PROPERTY(type, value) \
.section .note.gnu.property, "a"; \
.p2align 3; \
@@ -39,32 +41,7 @@
.word 0; \
.text
#ifdef __ARM_BUILDATTR64_FV
/* Add AArch64 feature bits build attributes. */
# define FEATURE_1_AND_MARK(value) \
.aeabi_subsection aeabi_feature_and_bits, optional, ULEB128; \
.if ((value) & FEATURE_1_BTI); \
.aeabi_attribute Tag_Feature_BTI, 1; \
.else; \
.aeabi_attribute Tag_Feature_BTI, 0; \
.endif; \
.if ((value) & FEATURE_1_GCS); \
.aeabi_attribute Tag_Feature_GCS, 1; \
.else; \
.aeabi_attribute Tag_Feature_GCS, 0; \
.endif; \
.if ((value) & FEATURE_1_PAC); \
.aeabi_attribute Tag_Feature_PAC, 1; \
.else; \
.aeabi_attribute Tag_Feature_PAC, 0; \
.endif; \
.text
#else
/* Add a NT_GNU_PROPERTY_TYPE_0 note. */
# define FEATURE_1_AND_MARK(value) GNU_PROPERTY (FEATURE_1_AND, value)
#endif /* __ARM_BUILDATTR64_FV */
/* Add marking with the supported features to all asm code where this header
is included. */
FEATURE_1_AND_MARK (FEATURE_1_BTI | FEATURE_1_PAC | FEATURE_1_GCS)
/* Add GNU property note with the supported features to all asm code
where sysdep.h is included. */
GNU_PROPERTY (FEATURE_1_AND, FEATURE_1_BTI|FEATURE_1_PAC|FEATURE_1_GCS)
#endif
-1
View File
@@ -1 +0,0 @@
#include "tst-atrandom-scrub.c"
-89
View File
@@ -1,89 +0,0 @@
/* Verify the AT_RANDOM bytes do not reveal the guards after startup.
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/>. */
/* The loader (security_init) and the static startup code (__libc_start_main)
derive the stack and pointer guards from the AT_RANDOM bytes, scrub those
bytes, and refill them with fresh entropy unrelated to the guards. The
AT_RANDOM entry is kept, so getauxval (AT_RANDOM) keeps returning 16 random
bytes, but they no longer reveal the guards. Check that neither guard can
be reconstructed from AT_RANDOM and that no auxiliary vector entry holds a
guard value. */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/auxv.h>
#include <stackguard-macros.h>
#include <tls.h>
#include <support/check.h>
static int
do_test (void)
{
uintptr_t stack_guard = STACK_CHK_GUARD;
uintptr_t pointer_guard = POINTER_CHK_GUARD;
unsigned char *random = (unsigned char *) getauxval (AT_RANDOM);
if (random == NULL)
FAIL_UNSUPPORTED ("the kernel did not provide AT_RANDOM");
printf ("debug: stack guard = %0*jx\n",
(int) (2 * sizeof (uintptr_t)), (uintmax_t) stack_guard);
printf ("debug: pointer guard = %0*jx\n",
(int) (2 * sizeof (uintptr_t)), (uintmax_t) pointer_guard);
printf ("debug: AT_RANDOM = ");
for (int i = 0; i < 16; i++)
printf ("%02x", random[i]);
printf ("\n");
/* Sanity check: the guards and AT_RANDOM must all have been populated
(not all-zero), otherwise we cannot tell scrub-and-reseed apart from
"never set up". */
TEST_VERIFY (stack_guard != 0);
TEST_VERIFY (pointer_guard != 0);
bool random_all_zero = true;
for (int i = 0; i < 16; i++)
if (random[i] != 0)
{
random_all_zero = false;
break;
}
TEST_VERIFY (!random_all_zero);
/* Reconstruct the guards from the (reseeded) AT_RANDOM bytes the way the
loader does and check that they no longer match the live guards. */
uintptr_t recovered_stack;
memcpy (&recovered_stack, random, sizeof (recovered_stack));
#if __BYTE_ORDER == __LITTLE_ENDIAN
recovered_stack &= ~(uintptr_t) 0xff;
#else
recovered_stack &= ~((uintptr_t) 0xff << (8 * (sizeof (recovered_stack) - 1)));
#endif
TEST_VERIFY (recovered_stack != stack_guard);
uintptr_t recovered_pointer;
memcpy (&recovered_pointer, random + sizeof (uintptr_t),
sizeof (recovered_pointer));
TEST_VERIFY (recovered_pointer != pointer_guard);
return 0;
}
#include <support/test-driver.c>
-67
View File
@@ -1,67 +0,0 @@
/* Test that dlopen on a large-e_phnum DSO does not overflow the stack when
called from a thread with PTHREAD_STACK_MIN stack size (BZ #26577).
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/>. */
/* Before the fix for BZ #26577, _dl_map_object_from_fd and open_verify both
used alloca(e_phnum * sizeof(ElfW(Phdr))) when the program-header table did
not fit in the initial filebuf read. Calling dlopen from a thread created
with PTHREAD_STACK_MIN stack size (typically 16 KB on Linux) would overflow
the stack even for moderate e_phnum values, and catastrophically for the
0x7FFF-header DSO used here.
The crafted DSO (tst-bz26577-mod.so, generated by gen-tst-bz26577-mod.py)
has e_phnum == 0x7FFF: one PT_LOAD covering the ELF header and the rest
PT_NULL. dlopen on it must return NULL (no PT_DYNAMIC) without triggering
a stack overflow. */
#include <dlfcn.h>
#include <limits.h>
#include <stdlib.h>
#include <support/check.h>
#include <support/support.h>
#include <support/xthread.h>
static void *
dlopen_thread (void *arg)
{
/* Attempt to load the large-phnum module from a PTHREAD_STACK_MIN thread.
With the old alloca-based code this overflows the stack; with the fix the
load fails gracefully because the module has no PT_DYNAMIC. */
void *h = dlopen ((const char *) arg, RTLD_LAZY);
TEST_VERIFY (h == NULL);
return NULL;
}
static int
do_test (void)
{
char *path = xasprintf ("%s/elf/tst-bz26577-mod.so", support_objdir_root);
size_t stacksize = support_small_thread_stack_size (true);
pthread_attr_t attr;
xpthread_attr_init (&attr);
xpthread_attr_setstacksize (&attr, stacksize);
pthread_t thr = xpthread_create (&attr, dlopen_thread, path);
xpthread_join (thr);
xpthread_attr_destroy (&attr);
free (path);
return 0;
}
#include <support/test-driver.c>
-169
View File
@@ -1,169 +0,0 @@
/* Tests for BZ #26577: stack overflow when loading a crafted ELF with
large e_phnum in _dl_map_object_from_fd.
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/>. */
/* The crafted ELF is generated at build time by gen-tst-bz26577-mod.py
(elf/tst-bz26577-mod.so) with an ET_DYN with e_phnum = 0x7FFF, one PT_LOAD
segment covering the ELF header and the remaining headers PT_NULL.
This test exercises the loader against that crafted module via two
subtests, each run as a fresh exec with a reduced stack limit:
1. dlopen() subtest (--restart dlopen <path>): call dlopen on the
crafted .so.
2. LD_PRELOAD startup subtest (--restart, with LD_PRELOAD set): the
dynamic linker attempts to load the crafted .so at startup.
In both cases loader should fail to load the DSO, but without triggering
errors like SEGFAULT. */
#include <dlfcn.h>
#include <elf.h>
#include <getopt.h>
#include <link.h>
#include <signal.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <unistd.h>
#include <support/check.h>
#include <support/subprocess.h>
#include <support/support.h>
/* Number of program headers in the crafted ELF. Must match EVIL_PHNUM
in gen-tst-bz26577-mod.py. */
#define EVIL_PHNUM UINT16_C (0x7FFF)
static int restart;
#define CMDLINE_OPTIONS \
{ "restart", no_argument, &restart, 1 },
static const char *test_binary;
/* Reduced stack size used in subprocess tests. The 1 MB headroom should
cover the loader required call chain. */
static size_t
evil_stack_size (void)
{
return (size_t) EVIL_PHNUM * sizeof (ElfW(Phdr)) + 1024 * 1024;
}
struct subtest_args
{
const char *mod_path;
const char *subtest;
};
static void
run_with_limited_stack (void *closure)
{
const struct subtest_args *args = closure;
struct rlimit rl;
TEST_VERIFY_EXIT (getrlimit (RLIMIT_STACK, &rl) == 0);
rl.rlim_cur = evil_stack_size ();
TEST_VERIFY_EXIT (setrlimit (RLIMIT_STACK, &rl) == 0);
if (args->subtest == NULL)
setenv ("LD_PRELOAD", args->mod_path, 1);
char *spawn_argv[6];
int i = 0;
spawn_argv[i++] = (char *) test_binary;
spawn_argv[i++] = (char *) "--direct";
spawn_argv[i++] = (char *) "--restart";
if (args->subtest != NULL)
{
spawn_argv[i++] = (char *) args->subtest;
spawn_argv[i++] = (char *) args->mod_path;
}
spawn_argv[i] = NULL;
struct support_spawn_wrapped *w
= support_spawn_wrap (test_binary, spawn_argv, NULL, 0);
execve (w->path, (char *const *) w->argv, (char *const *) w->envp);
_exit (127);
}
/* Fork a child with a reduced stack limit and exec this binary to call
dlopen on MOD_PATH. */
static void
test_dlopen_large_phnum (const char *mod_path)
{
struct subtest_args args = { mod_path, "dlopen" };
struct support_subprocess proc
= support_subprocess (run_with_limited_stack, &args);
int status = support_process_wait (&proc);
if (WIFSIGNALED (status) && WTERMSIG (status) == SIGSEGV)
FAIL_EXIT1 ("dlopen test: child killed by SIGSEGV"
" (stack overflow from unfixed loadcmd VLA)");
}
/* Fork a child with a reduced stack limit and exec this binary with
LD_PRELOAD set to MOD_PATH. */
static void
test_startup_large_phnum (const char *mod_path)
{
struct subtest_args args = { mod_path, NULL };
struct support_subprocess proc
= support_subprocess (run_with_limited_stack, &args);
int status = support_process_wait (&proc);
if (WIFSIGNALED (status) && WTERMSIG (status) == SIGSEGV)
FAIL_EXIT1 ("startup test: child killed by SIGSEGV"
" (stack overflow from unfixed loadcmd VLA)");
}
static int
do_test (int argc, char *argv[])
{
if (restart)
{
/* dlopen subtest: argv[1] == "dlopen", argv[2] == module path. */
if (argc > 1 && strcmp (argv[1], "dlopen") == 0)
{
TEST_VERIFY (argc == 3);
void *h = dlopen (argv[2], RTLD_LAZY);
TEST_VERIFY (h == NULL);
}
/* LD_PRELOAD subtest: no extra args; loader already exercised the
code during startup before main() was reached. */
return 0;
}
/* We must have either one argument (hardcoded paths) or four arguments
(ld.so, --library-path, lib-path, binary) after the program name. */
TEST_VERIFY_EXIT (argc == 2 || argc == 5);
test_binary = argv[argc - 1];
char *mod_path = xasprintf ("%s/elf/tst-bz26577-mod.so",
support_objdir_root);
test_dlopen_large_phnum (mod_path);
test_startup_large_phnum (mod_path);
free (mod_path);
return 0;
}
#define TEST_FUNCTION_ARGV do_test
#include <support/test-driver.c>
+1 -1
View File
@@ -128,7 +128,7 @@ do_test_threads (bool set_guard)
{
pthread_attr_t attr;
xpthread_attr_init (&attr);
size_t stacksize = support_small_thread_stack_size (false);
size_t stacksize = support_small_thread_stack_size ();
void *stack = xmmap (0,
stacksize,
PROT_READ | PROT_WRITE,
-174
View File
@@ -1,174 +0,0 @@
/* Test dlopen through ld.so.cache with a cache entry longer than the
dl_scratch_buffer inline area.
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/>. */
/* This test populates the cache with a single library sitting in a directory
whose absolute path is far larger than the dl_scratch_buffer inline area
(256 bytes) and most of the way to PATH_MAX; the loader's cache lookup
therefore exercises the anonymous-mmap spill. The dlopen is also repeated
from a PTHREAD_STACK_MIN thread to demonstrate that the path no longer
consumes too much caller's stack. */
#include <dlfcn.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <support/capture_subprocess.h>
#include <support/check.h>
#include <support/support.h>
#include <support/temp_file.h>
#include <support/xdlfcn.h>
#include <support/xstdio.h>
#include <support/xthread.h>
#include <support/xunistd.h>
/* ldconfig only indexes filenames starting with "lib", so the module is
deployed under the lib-prefixed name (MOD_DEPLOYED) in the deep directory
and dlopened by that name. */
#define MOD_BUILT "tst-dl-path-buf-mod.so"
#define MOD_DEPLOYED "libtst-dl-path-buf-mod.so"
#define MOD_SYMBOL "tst_dl_path_buf_mod_value"
#define MOD_EXPECTED 0xaabbccddu
/* Final absolute path of the deep directory holding the module; filled in by
setup (). Kept around so dlopen_module can sanity-print it on failure. */
static char *deep_dir;
static void
run_ldconfig (void *x)
{
char *prog = xasprintf ("%s/ldconfig", support_install_rootsbindir);
char *args[] = { prog, NULL };
execv (args[0], args);
FAIL_EXIT1 ("execv (%s): %m", prog);
}
/* Build /tst-dl-cache-long-path/d.../d.../d... with several long components,
totalling well past dl_scratch_buffer's inline area
(DL_SCRATCH_BUFFER_INLINE_SIZE = 256 bytes) and close to PATH_MAX. */
static char *
build_deep_directory (void)
{
enum { component_len = 250, components = 15 };
/* 14 * (1 + 240) = 3374 bytes of nesting, plus the base. */
char component[component_len + 1];
memset (component, 'd', component_len);
component[component_len] = '\0';
const char *base = "/tst-dl-cache-long-path";
size_t cap = strlen (base) + components * (1 + component_len) + 1;
char *path = xmalloc (cap);
strcpy (path, base);
xmkdirp (path, 0777);
add_temp_file (path);
for (int i = 0; i < components; ++i)
{
strcat (path, "/");
strcat (path, component);
xmkdirp (path, 0777);
add_temp_file (path);
}
return path;
}
static void
do_prepare (int argc, char **argv)
{
deep_dir = build_deep_directory ();
TEST_VERIFY (strlen (deep_dir) > 256);
char *src = xasprintf ("%s/elf/" MOD_BUILT, support_objdir_root);
char *dst = xasprintf ("%s/" MOD_DEPLOYED, deep_dir);
support_copy_file (src, dst);
add_temp_file (dst);
free (src);
free (dst);
char *conf = xasprintf ("%s/ld.so.conf", support_sysconfdir_prefix);
FILE *fp = xfopen (conf, "w");
fprintf (fp, "%s\n", deep_dir);
xfclose (fp);
free (conf);
xmkdirp ("/var/cache/ldconfig", 0777);
struct support_capture_subprocess r
= support_capture_subprocess (run_ldconfig, NULL);
support_capture_subprocess_check (&r, "ldconfig", 0, sc_allow_none);
support_capture_subprocess_free (&r);
}
#define PREPARE do_prepare
static void
__attribute_noinline__
dlopen_via_cache (volatile char *pressure)
{
if (pressure != NULL)
(void) *pressure;
void *h = xdlopen (MOD_DEPLOYED, RTLD_NOW | RTLD_LOCAL);
unsigned int (*fn) (void) = xdlsym (h, MOD_SYMBOL);
TEST_COMPARE (fn (), MOD_EXPECTED);
xdlclose (h);
}
/* Reduce the stack budget available to the dlopen call chain by
STACK_PRESSURE bytes. */
enum { STACK_PRESSURE = 5 * 1024 };
static void
__attribute_noinline__
dlopen_via_cache_under_pressure (void)
{
char filler[STACK_PRESSURE];
dlopen_via_cache (&filler[0]);
}
static void *
minstack_thread (void *closure)
{
dlopen_via_cache_under_pressure ();
return NULL;
}
static int
do_test (void)
{
/* Sanity: from the main thread (no stack pressure needed). */
dlopen_via_cache (NULL);
/* The motivating scenario: from a PTHREAD_STACK_MIN thread. Before
_dl_load_cache_lookup was converted to dl_scratch_buffer this would
have alloca'd ~3 KB mid-dlopen and risked overflowing. */
size_t stacksize = support_small_thread_stack_size (true);
pthread_attr_t attr;
xpthread_attr_init (&attr);
xpthread_attr_setstacksize (&attr, stacksize);
pthread_t thr = xpthread_create (&attr, minstack_thread, NULL);
xpthread_join (thr);
xpthread_attr_destroy (&attr);
free (deep_dir);
return 0;
}
#include <support/test-driver.c>
-152
View File
@@ -1,152 +0,0 @@
/* Test that a long search path does not overflow loader startup stack.
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/>. */
/* This test reduces RLIMIT_STACK to a value that just covers regular loader
startup, then spawns a child whose dynamic-linker search path is
artificially long (16 pages of synthetic colon-separated entries).
On exec the kernel places argv+envp at the top of the initial stack and
rounds the reservation to page granularity, which would otherwise eat the
loader's entire budget on architectures with large pages (e.g. 64 KB-page
aarch64).
Some kernels enforce that exec's argv+envp does not exceed RLIMIT_STACK/4;
the constraints here (envp+envp_copy must exceed RLIMIT_STACK) mean the
scenario cannot be set up on such kernels, in which case posix_spawn
returns E2BIG and the test is marked UNSUPPORTED. */
#include <errno.h>
#include <getopt.h>
#include <signal.h>
#include <spawn.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/wait.h>
#include <unistd.h>
#include <support/check.h>
#include <support/subprocess.h>
#include <support/support.h>
#include <support/xunistd.h>
static int restart;
#define CMDLINE_OPTIONS \
{ "restart", no_argument, &restart, 1 },
enum { llp_entries = 16 };
/* Return a newly malloc'd string of the form "PREFIX[:/ddd...]{16}"
where each synthetic entry is ENTRY_LEN bytes long. PREFIX is the
ld.so --library-path value from support_spawn_wrap and supplies the
real build directories so the rtld can still resolve libc. */
static char *
build_long_library_path (const char *prefix, size_t entry_len)
{
size_t prefix_len = strlen (prefix);
size_t junk_len = (size_t) llp_entries * (1 + entry_len);
char *out = xmalloc (prefix_len + junk_len + 1);
char *p = stpcpy (out, prefix);
for (int i = 0; i < llp_entries; i++)
{
*p++ = ':';
*p++ = '/';
memset (p, 'd', entry_len - 1);
p += entry_len - 1;
}
*p = '\0';
return out;
}
static int
do_test (void)
{
if (restart)
return 0;
char *binary = xasprintf ("%s/elf/tst-dl-llp-stack", support_objdir_root);
char *child_argv_in[] = {
binary, (char *) "--direct", (char *) "--restart", NULL
};
struct support_spawn_wrapped *w
= support_spawn_wrap (binary, child_argv_in, NULL,
support_spawn_wrap_force);
/* Scale envp and stack rlimit with PAGE_SIZE to handle kernels with
different page sizes. */
long page_size = sysconf (_SC_PAGESIZE);
TEST_VERIFY_EXIT (page_size > 0);
size_t entry_len = (size_t) page_size - 1;
size_t stack_limit = (size_t) 24 * page_size;
/* Extend the wrapped --library-path value with the synthetic junk.
Build a fresh argv whose slot 2 points at our long-paths string;
all other slots alias into the wrapped argv, which keeps ownership
of those strings. */
TEST_VERIFY_EXIT (w->argv[0] != NULL && w->argv[1] != NULL
&& w->argv[2] != NULL
&& strcmp (w->argv[1], "--library-path") == 0);
char *long_paths = build_long_library_path (w->argv[2], entry_len);
size_t nargs;
for (nargs = 0; w->argv[nargs] != NULL; nargs++)
;
char **child_argv = xcalloc (nargs + 1, sizeof (*child_argv));
for (size_t i = 0; i < nargs; i++)
child_argv[i] = (i == 2) ? long_paths : (char *) w->argv[i];
child_argv[nargs] = NULL;
/* Reduce the stack rlimit; the posix_spawn'd child inherits it. */
struct rlimit rl_save, rl_small;
TEST_VERIFY_EXIT (getrlimit (RLIMIT_STACK, &rl_save) == 0);
rl_small.rlim_cur = (rlim_t) stack_limit;
rl_small.rlim_max = rl_save.rlim_max;
TEST_VERIFY_EXIT (setrlimit (RLIMIT_STACK, &rl_small) == 0);
pid_t pid;
int spawn_ret = posix_spawn (&pid, w->path, NULL, NULL, child_argv,
(char *const *) w->envp);
TEST_VERIFY_EXIT (setrlimit (RLIMIT_STACK, &rl_save) == 0);
if (spawn_ret == E2BIG)
FAIL_UNSUPPORTED ("posix_spawn returned E2BIG: this kernel enforces "
"argv+envp <= RLIMIT_STACK/4");
if (spawn_ret != 0)
{
errno = spawn_ret;
FAIL_EXIT1 ("posix_spawn: %m");
}
int status;
TEST_COMPARE (xwaitpid (pid, &status, 0), pid);
if (WIFSIGNALED (status))
FAIL_EXIT1 ("child killed by signal %d", WTERMSIG (status));
TEST_VERIFY_EXIT (WIFEXITED (status));
TEST_COMPARE (WEXITSTATUS (status), 0);
free (child_argv);
free (long_paths);
support_spawn_wrapped_free (w);
free (binary);
return 0;
}
#include <support/test-driver.c>
-23
View File
@@ -1,23 +0,0 @@
/* Trivial DSO used by tst-dl-path-buf.
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/>. */
unsigned int
tst_dl_path_buf_mod_value (void)
{
return 0xaabbccddu;
}
-220
View File
@@ -1,220 +0,0 @@
/* Exercise the mmap-backed path scratch buffer in elf/dl-load.c.
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/>. */
/* open_path()'s scratch buffer comes from dl_scratch_buffer, which uses
anonymous mmap while __minimal_malloc is active (during loader startup)
and libc malloc afterwards. Mappings from the mmap backend are tagged
with the VMA name " glibc: loader scratch". This test exercises the
relevant code paths -- search via DT_RPATH, open_path failure cleanup,
dlopen with an over-long name, dlopen from a minimal-stack thread,
and per-backend leak checks -- to verify each path properly
alloc/frees the scratch buffer. */
#include <dlfcn.h>
#include <errno.h>
#include <limits.h>
#include <mcheck.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <support/check.h>
#include <support/support.h>
#include <support/temp_file.h>
#include <support/xdlfcn.h>
#include <support/xstdio.h>
#include <support/xthread.h>
#include <support/xunistd.h>
#ifndef PATH_MAX
# define PATH_MAX 1024
#endif
/* Must match LDFLAGS-tst-dl-path-buf in elf/Makefile. The test binary's
DT_RPATH resolves to $ORIGIN of the binary plus this subdirectory. */
#define MOD_SUBDIR "tst-dl-path-buf-subdir"
#define MOD_NAME "tst-dl-path-buf-mod.so"
/* Tag installed by _dl_scratch_buffer_allocate via __set_vma_name. Mappings
in /proc/self/maps annotated with this string belong to a live scratch
buffer; after a successful dlopen/dlclose cycle there must be zero of
them. */
#define SCRATCH_VMA_TAG "[anon: glibc: loader scratch]"
/* Open MOD_NAME via DT_RPATH. Returns the handle; the caller closes it. */
static void
dlopen_module (void)
{
void *h = xdlopen (MOD_NAME, RTLD_NOW | RTLD_LOCAL);
unsigned int (*fn) (void) = xdlsym (h, "tst_dl_path_buf_mod_value");
TEST_COMPARE (fn (), 0xaabbccddu);
xdlclose (h);
}
/* Subtest 1: basic dlopen/dlclose via DT_RPATH search. */
static void
test_basic (void)
{
dlopen_module ();
}
/* Subtest 2: non-existent name: open_path is exercised on every search list
(DT_RPATH then cache then __rtld_search_dirs), failing each time. Each
failure path must release its scratch buffer. */
static void
test_nonexistent (void)
{
void *h = dlopen ("tst-dl-path-buf-does-not-exist.so",
RTLD_NOW | RTLD_LOCAL);
TEST_VERIFY (h == NULL);
}
/* Subtest 3: a name whose resolved length far exceeds PATH_MAX cannot refer
to a real file: open_path will allocate a large scratch buffer, build
candidate paths, and have every open() return ENAMETOOLONG. dlopen must
therefore fail cleanly (and without leaking the scratch buffer on the
failure paths). */
static void
test_overlong_name (void)
{
char *huge = xmalloc (PATH_MAX + 64);
memset (huge, 'a', PATH_MAX + 32);
memcpy (huge + PATH_MAX, ".so", 4);
void *h = dlopen (huge, RTLD_NOW | RTLD_LOCAL);
TEST_VERIFY (h == NULL);
free (huge);
}
/* Count anonymous mappings in /proc/self/maps annotated with SCRATCH_VMA_TAG.
Used by the mmap-backend leak subtest below. */
static unsigned int
count_scratch_mappings (void)
{
FILE *f = xfopen ("/proc/self/maps", "r");
unsigned int n = 0;
char *line = NULL;
size_t line_len = 0;
while (xgetline (&line, &line_len, f))
if (strstr (line, SCRATCH_VMA_TAG) != NULL)
++n;
free (line);
xfclose (f);
return n;
}
/* Subtest 4a (mmap backend). dl_scratch_buffer's mmap backend is used while
__rtld_malloc_is_complete returns false -- that window covers the entire
loader-startup phase, during which the loader resolves the test binary's
DT_NEEDED dependencies via open_path() (and so allocates and frees scratch
buffers). */
static void
test_no_leak_mmap (void)
{
if (!support_set_vma_name_supported ())
{
printf ("info: skipping mmap-backend leak subtest:"
" kernel does not support PR_SET_VMA_ANON_NAME\n");
return;
}
unsigned int residual = count_scratch_mappings ();
if (residual != 0)
FAIL_EXIT1 ("%u leaked loader scratch mapping(s) survived loader"
" startup -- _dl_scratch_buffer_free's mmap backend"
" is broken", residual);
}
/* Subtest 4b (malloc backend). Once libc malloc is active,
dl_scratch_buffer_allocate routes through malloc and the mapping VMA tag is
no longer used. Drive enough dlopen success+failure cycles to exercise
every path in dl_scratch_buffer_free. */
static void
test_no_leak_malloc (void)
{
mtrace ();
enum { iterations = 10 };
for (unsigned int i = 0; i < iterations; ++i)
{
dlopen_module ();
void *nh = dlopen ("tst-dl-path-buf-does-not-exist.so",
RTLD_NOW | RTLD_LOCAL);
TEST_VERIFY (nh == NULL);
}
}
/* Subtest 5. Run the success path from a PTHREAD_STACK_MIN thread. */
static void *
minstack_thread (void *closure)
{
dlopen_module ();
void *nh = dlopen ("tst-dl-path-buf-does-not-exist.so",
RTLD_NOW | RTLD_LOCAL);
TEST_VERIFY (nh == NULL);
return NULL;
}
static void
test_minstack (void)
{
size_t stacksize = support_small_thread_stack_size (true);
pthread_attr_t attr;
xpthread_attr_init (&attr);
xpthread_attr_setstacksize (&attr, stacksize);
pthread_t thr = xpthread_create (&attr, minstack_thread, NULL);
xpthread_join (thr);
xpthread_attr_destroy (&attr);
}
static int
do_test (void)
{
support_need_proc ("/proc/self/maps is read for the leak subtest.");
char *subdir = xasprintf ("%s/elf/" MOD_SUBDIR, support_objdir_root);
xmkdirp (subdir, 0777);
add_temp_file (subdir);
char *src = xasprintf ("%s/elf/" MOD_NAME, support_objdir_root);
char *dst = xasprintf ("%s/" MOD_NAME, subdir);
support_copy_file (src, dst);
add_temp_file (dst);
/* Check the mmap backend's startup behavior first, before any
subtest can perturb /proc/self/maps with its own allocations. */
test_no_leak_mmap ();
test_basic ();
test_nonexistent ();
test_overlong_name ();
test_no_leak_malloc ();
test_minstack ();
free (src);
free (dst);
free (subdir);
return 0;
}
#include <support/test-driver.c>
-20
View File
@@ -1,20 +0,0 @@
/* Leaf DSO whose SONAME contains several DST tokens. Used by
tst-dst-needed-minstack via tst-dst-needed-wrap-mod.so.
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_dst_needed_leaf_dummy;
-122
View File
@@ -1,122 +0,0 @@
/* Test that dlopen of a library whose DT_NEEDED string carries
several dynamic-string tokens does not overflow a
PTHREAD_STACK_MIN-sized thread.
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/>. */
/* The leaf library is linked with a SONAME containing five $DST tokens; the
wrapper library links against the leaf so its DT_NEEDED inherits that
string. This test deploys the wrapper in a ~3 KB deep directory (so the
wrapper's l_origin matches), then dlopens it from a PTHREAD_STACK_MIN
thread. The leaf is not actually reachable through the (impossible)
expanded path, so the dlopen is expected to fail -- the regression
assertion is that the failure occurs without a stack overflow. */
#include <dlfcn.h>
#include <limits.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <support/check.h>
#include <support/support.h>
#include <support/temp_file.h>
#include <support/xstdio.h>
#include <support/xthread.h>
#include <support/xunistd.h>
#define WRAP_MOD "tst-dst-needed-wrap-mod.so"
/* Set by do_prepare; the absolute path of the wrapper as deployed in the
deep directory. */
static char *deep_wrap_path;
/* Build <temp>/<long components>/ and return the final path; intermediates
are registered with add_temp_file so cleanup is automatic. */
static char *
build_deep_directory (void)
{
enum { component_len = 240, components = 14 };
char component[component_len + 1];
memset (component, 'd', component_len);
component[component_len] = '\0';
char *base = support_create_temp_directory ("tst-dst-needed-");
size_t cap = strlen (base) + components * (1 + component_len) + 1;
char *path = xmalloc (cap);
strcpy (path, base);
free (base);
for (int i = 0; i < components; ++i)
{
strcat (path, "/");
strcat (path, component);
xmkdirp (path, 0777);
add_temp_file (path);
}
return path;
}
static void
do_prepare (int argc, char **argv)
{
char *deep_dir = build_deep_directory ();
/* Deep enough that l_origin alone is well over PTHREAD_STACK_MIN. */
TEST_VERIFY (strlen (deep_dir) > 256);
char *src = xasprintf ("%s/elf/" WRAP_MOD, support_objdir_root);
deep_wrap_path = xasprintf ("%s/" WRAP_MOD, deep_dir);
support_copy_file (src, deep_wrap_path);
add_temp_file (deep_wrap_path);
free (src);
free (deep_dir);
}
#define PREPARE do_prepare
static void *
minstack_thread (void *closure)
{
/* Trigger DT_NEEDED expansion on the deep wrapper. The leaf's five-$DST
SONAME, expanded against the wrapper's deep l_origin, produces a buffer
of several KB inside _dl_map_object_deps. We do not care whether the
leaf is actually findable -- the test passes if and only if the dlopen
returns without a stack overflow. */
void *h = dlopen (deep_wrap_path, RTLD_NOW);
TEST_VERIFY_EXIT (h == NULL);
return NULL;
}
static int
do_test (void)
{
size_t stacksize = support_small_thread_stack_size (true);
pthread_attr_t attr;
xpthread_attr_init (&attr);
xpthread_attr_setstacksize (&attr, stacksize);
pthread_t thr = xpthread_create (&attr, minstack_thread, NULL);
xpthread_join (thr);
xpthread_attr_destroy (&attr);
free (deep_wrap_path);
return 0;
}
#include <support/test-driver.c>
-21
View File
@@ -1,21 +0,0 @@
/* Wrapper DSO whose DT_NEEDED string inherits the DST-laden SONAME
of tst-dst-needed-leaf-mod.so. Loading this from a
PTHREAD_STACK_MIN thread is what tst-dst-needed-minstack exercises.
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_dst_needed_wrap_dummy;
+1 -1
View File
@@ -46,7 +46,7 @@ do_test (void)
{
/* Install the default implementation of libmarkermod1.so. */
char *conf_path = xasprintf ("%s/ld.so.conf.d/hwcaps.conf", support_sysconfdir_prefix);
char *conf_path = xasprintf ("%s/ld.so.conf", support_sysconfdir_prefix);
xmkdirp (support_sysconfdir_prefix, 0777);
support_write_file_string (conf_path, "/glibc-test/lib\n");
free (conf_path);
-61
View File
@@ -1,61 +0,0 @@
/* BZ 28817: TLS read from a static-pie IFUNC resolver.
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 <stdio.h>
#include <stdlib.h>
#include <support/check.h>
__thread int bar;
extern __thread int bar_gd asm ("bar")
__attribute__ ((tls_model("global-dynamic")));
static int *bar_ptr;
int foo (void);
static void
init_foo (void)
{
bar_ptr = &bar_gd;
}
static int
my_foo (void)
{
return bar_ptr != NULL;
}
static __typeof (foo) *
inhibit_stack_protector
__attribute_used__
foo_ifunc (void)
{
init_foo ();
__typeof (foo) *res = my_foo;
return res;
};
__typeof (foo) foo __attribute__ ((ifunc ("foo" "_ifunc")));
static int
do_test (void)
{
TEST_VERIFY (foo ());
TEST_VERIFY (&bar == bar_ptr);
return 0;
}
#include <support/test-driver.c>
-24
View File
@@ -1,24 +0,0 @@
/* Test that IRELATIVE resolvers may call PLT functions with LD_BIND_NOW=1.
Copyright (C) 2019-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/>. */
/* Same as tst-ifunc-plt, but the test is run with LD_BIND_NOW=1 so the
library is processed eagerly. Under eager binding the JMP_SLOT entries
in .rela.plt are resolved by elf_machine_rel during relocation; this
exercises the eager path of the deferred IRELATIVE processing. */
#include "tst-ifunc-plt.c"
-23
View File
@@ -1,23 +0,0 @@
/* Dependency library for tst-ifunc-plt.
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
get_value (void)
{
return 42;
}
-45
View File
@@ -1,45 +0,0 @@
/* Test that IRELATIVE resolvers may call PLT functions via dlopen + RTLD_NOW.
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/>. */
/* Same as tst-ifunc-plt-dlopen, but uses RTLD_NOW to force eager
relocation of the loaded library. This exercises the dlopen entry
point along the eager-binding path. */
#include <support/xdlfcn.h>
#include <support/check.h>
typedef int (*fn_t) (void);
static int
do_test (void)
{
void *handle = xdlopen ("tst-ifunc-plt-lib.so", RTLD_NOW | RTLD_LOCAL);
fn_t compute_a = (fn_t) xdlsym (handle, "compute_a");
TEST_COMPARE (compute_a (), 1);
fn_t compute_b = (fn_t) xdlsym (handle, "compute_b");
TEST_COMPARE (compute_b (), 2);
xdlclose (handle);
return 0;
}
#include <support/test-driver.c>
-46
View File
@@ -1,46 +0,0 @@
/* Test that IRELATIVE resolvers may call PLT functions on a dlopen'd library.
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/>. */
/* dlopen tst-ifunc-plt-lib.so with RTLD_LAZY and verify that the IFUNC
resolver (which calls get_value() via PLT) ran successfully. This
exercises the same _dl_relocate_object code path as startup loading
but via the dlopen entry point. */
#include <support/xdlfcn.h>
#include <support/check.h>
typedef int (*fn_t) (void);
static int
do_test (void)
{
void *handle = xdlopen ("tst-ifunc-plt-lib.so", RTLD_LAZY | RTLD_LOCAL);
fn_t compute_a = (fn_t) xdlsym (handle, "compute_a");
TEST_COMPARE (compute_a (), 1);
fn_t compute_b = (fn_t) xdlsym (handle, "compute_b");
TEST_COMPARE (compute_b (), 2);
xdlclose (handle);
return 0;
}
#include <support/test-driver.c>
-59
View File
@@ -1,59 +0,0 @@
/* Shared library for tst-ifunc-plt.
Two static IFUNCs whose resolvers both call get_value() via PLT.
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/>. */
/* Both resolvers call get_value() via PLT (one JUMP_SLOT entry in
.rel{a}.plt). This verifies that every IRELATIVE entry is deferred
until after .rela.plt has been processed, not just the first one. */
#include <sys/cdefs.h>
#include <stddef.h>
extern int get_value (void);
static int
impl_a (void)
{
return 1;
}
static int
impl_b (void)
{
return 2;
}
static typeof (impl_a) *
__attribute_used__
resolve_a (void)
{
return get_value () == 42 ? impl_a : NULL;
}
static typeof (impl_b) *
__attribute_used__
resolve_b (void)
{
return get_value () == 42 ? impl_b : NULL;
}
/* The test is only built for $(have-ifunc), so we can assume HAVE_GCC_IFUNC
here. */
int compute_a (void) __attribute__ ((ifunc ("resolve_a")));
int compute_b (void) __attribute__ ((ifunc ("resolve_b")));
-38
View File
@@ -1,38 +0,0 @@
/* Test that IRELATIVE resolvers may call PLT functions during startup.
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/>. */
/* tst-ifunc-plt-multi-lib.so defines two static IFUNCs (compute_a and
compute_b), each producing an R_*_IRELATIVE in .rel{a}.dyn, with both
resolvers calling get_value() via PLT. The test verifies that both
IRELATIVEs are deferred until after .rel{a}.plt is processed. */
#include <support/check.h>
extern int compute_a (void);
extern int compute_b (void);
static int
do_test (void)
{
TEST_COMPARE (compute_a (), 1);
TEST_COMPARE (compute_b (), 2);
return 0;
}
#include <support/test-driver.c>
-64
View File
@@ -1,64 +0,0 @@
/* Stack-protector-instrumented IFUNC resolver in a shared library, used
by tst-ifunc-resolver-protector. BZ #27582.
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/>. */
/* Built with -fstack-protector-all so the resolver's prologue/epilogue
carries the full canary check. Reaching the return statement means the
canary load did not fault (TCB-canary ABIs) and the canary compare did not
call __stack_chk_fail. The dynamic linker calls security_init() before the
main relocation loop, so resolvers fired from startup or dlopen should
always observe an initialised canary; this test guards against any future
reordering that would break that invariant. */
#include <sys/cdefs.h>
#define SENTINEL 0x5A5A1234
static volatile int resolver_ran;
int
get_resolver_ran (void)
{
return resolver_ran;
}
static int
impl_ok (int x)
{
return x + SENTINEL;
}
typedef int (*fn_t) (int);
static fn_t
__attribute_used__
resolver (void)
{
/* Buffer + zero-fill force -fstack-protector-all canary code. */
volatile char buf[32];
for (unsigned i = 0; i < sizeof (buf); ++i)
buf[i] = 0;
resolver_ran = 1;
return impl_ok;
}
int compute (int) __attribute__ ((ifunc ("resolver")));
/* Address taken in DSO data to force an R_*_IRELATIVE in .rela.dyn (a
non-PLT relocation against the IFUNC). */
int (*fptr) (int) = compute;
@@ -1,69 +0,0 @@
/* Stack-protector-instrumented IFUNC resolver used by
tst-ifunc-resolver-protector-static. BZ #34164.
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/>. */
/* This translation unit is built with -fstack-protector-all so that the
compiler instruments the resolver below. The resolver's prologue loads
the canary from the TCB (TCB-canary ABIs) or from __stack_chk_guard
(global-var ABIs); the epilogue compares it against the on-stack copy.
Pre-fix the prologue load either faulted (TCB-canary) or loaded zero
(global-var).
The resolver also records the canary value via STACK_CHK_GUARD so the
test can verify it matches what main observes. */
#include <stackguard-macros.h>
#include <stdint.h>
#include <tls.h>
#define SENTINEL 0x5A5A1234
static volatile uintptr_t resolver_canary;
uintptr_t
get_resolver_canary (void)
{
return resolver_canary;
}
static int
impl_ok (int x)
{
return x + SENTINEL;
}
typedef int (*fn_t) (int);
static fn_t
__attribute_used__
resolver (void)
{
/* Buffer forces -fstack-protector-all to emit canary code even with
no other reason to. */
volatile char buf[32];
for (unsigned i = 0; i < sizeof (buf); ++i)
buf[i] = 0;
resolver_canary = STACK_CHK_GUARD;
return impl_ok;
}
int compute (int) __attribute__ ((ifunc ("resolver")));
int (*fptr) (int) = compute;
@@ -1,2 +0,0 @@
/* Companion module for tst-ifunc-resolver-protector-static-non-pie. */
#include "tst-ifunc-resolver-protector-static-mod.c"
@@ -1,5 +0,0 @@
/* Same coverage as tst-ifunc-resolver-protector-static, but linked as non-PIE
static. Exercises the apply_irel / __rela_iplt_start path instead of the
static-pie _dl_relocate_static_pie_ifunc path. */
#include "tst-ifunc-resolver-protector-static.c"
-61
View File
@@ -1,61 +0,0 @@
/* Check that a stack-protector-instrumented IFUNC resolver works in a
static binary. BZ #34164.
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/>. */
/* When a static binary's IFUNC resolver is compiled with stack protector, the
resolver prologue loads the canary from the TCB (TCB-canary ABIs: x86_64,
i386, powerpc, s390) or from the global __stack_chk_guard (other ABIs).
The test checks if TCB-canary or the global-var is properly initialized:
1. The resolver runs without SIGSEGV (TCB-canary) or SIGABRT (canary
mismatch).
2. The resolver records the canary value it observed via STACK_CHK_GUARD,
and main asserts it equals the value visible from user code -- catching
the "silent zero canary" variant on global-var ABIs. */
#include <stackguard-macros.h>
#include <stdint.h>
#include <tls.h>
#include <support/check.h>
#define SENTINEL 0x5A5A1234
extern int compute (int);
extern uintptr_t get_resolver_canary (void);
static int
do_test (void)
{
/* compute() returns its argument + SENTINEL iff the resolver picked
impl_ok, which it does whenever the canary check at the resolver
prologue / epilogue did not abort. */
TEST_COMPARE (compute (1), 1 + SENTINEL);
/* Silent-variant check: the canary the resolver loaded must match the
canary do_test reads. On global-var ABIs, it checks if the stack
protector cookie is properly initialised. */
uintptr_t resolver_canary = get_resolver_canary ();
uintptr_t main_canary = STACK_CHK_GUARD;
TEST_VERIFY (resolver_canary != 0);
TEST_COMPARE (resolver_canary, main_canary);
return 0;
}
#include <support/test-driver.c>
-42
View File
@@ -1,42 +0,0 @@
/* Check that a stack-protector-instrumented IFUNC resolver works in a
dynamically-linked binary. BZ #27582.
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/>. */
/* Dynamic counterpart to tst-ifunc-resolver-protector-static.
The dynamic linker calls security_init() before the main relocation
loop, so a stack-protected resolver fired from startup should observe
an initialised canary -- but having a regression test guards against
any future reordering that would break that invariant. */
#include <support/check.h>
#define SENTINEL 0x5A5A1234
extern int compute (int);
extern int get_resolver_ran (void);
static int
do_test (void)
{
TEST_COMPARE (compute (1), 1 + SENTINEL);
TEST_VERIFY (get_resolver_ran () != 0);
return 0;
}
#include <support/test-driver.c>
-55
View File
@@ -1,55 +0,0 @@
/* Shared library for tst-ifunc-tls-init-dlopen.
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/>. */
/* Same as tst-ifunc-tls-init-lib.c, but exercises the dlopen path. */
#define SENTINEL 0x5A5A1234
static volatile __thread int sentinel
__attribute__ ((tls_model ("initial-exec"))) = SENTINEL;
static volatile int last_seen_sentinel;
static int
impl_ok (void)
{
return SENTINEL;
}
static int
impl_bad (void)
{
return 0;
}
int
get_last_seen_sentinel (void)
{
return last_seen_sentinel;
}
static int (*resolver (void)) (void)
{
int s = sentinel;
last_seen_sentinel = s;
return s == SENTINEL ? impl_ok : impl_bad;
}
int ifunc_tls (void) __attribute__ ((ifunc ("resolver")));
/* Force a non-PLT relocation against the IFUNC symbol. */
int (*fptr) (void) = ifunc_tls;
-3
View File
@@ -1,3 +0,0 @@
#define TLS_MODEL "global-dynamic"
#define SENTINEL_STORAGE /* empty */
#include "tst-ifunc-tls-init-gd-ld-lib-skeleton.c"
@@ -1,73 +0,0 @@
/* Shared-library skeleton for tst-ifunc-tls-init-gd-ld.
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/>. */
/* Unlike tst-ifunc-tls-init-lib-skeleton.c, which uses initial-exec and
resolves via a single TP-relative load, this skeleton's TLS_MODEL is
either "global-dynamic" or "local-dynamic" so the resolver's read of
'sentinel' must traverse __tls_get_addr (or the architecture's
TLSDESC equivalent). That dynamic-TLS access can lazily allocate a
per-module TLS block, which is the path being exercised. */
#include <sys/cdefs.h>
#ifndef TLS_MODEL
# error "tst-ifunc-tls-init-gd-ld-lib-skeleton.c needs TLS_MODEL defined"
#endif
/* Without static the relocation against the SENTINEL goes through the regular
global-symbol lookup path; combined with TLS_MODEL="global-dynamic" this
exercises the "global GD" variant rather than the file-local one. */
#ifndef SENTINEL_STORAGE
# define SENTINEL_STORAGE static
#endif
#define SENTINEL 0x5A5A1234
SENTINEL_STORAGE volatile __thread int sentinel
__attribute__ ((tls_model (TLS_MODEL))) = SENTINEL;
static volatile int last_seen_sentinel;
static int
impl_ok (void)
{
return SENTINEL;
}
static int
impl_bad (void)
{
return 0;
}
int
get_last_seen_sentinel (void)
{
return last_seen_sentinel;
}
static typeof (impl_ok) *
__attribute_used__
resolver (void)
{
int s = sentinel;
last_seen_sentinel = s;
return s == SENTINEL ? impl_ok : impl_bad;
}
int ifunc_tls (void) __attribute__ ((ifunc ("resolver")));
int (*fptr) (void) = ifunc_tls;
-89
View File
@@ -1,89 +0,0 @@
/* Check if dynamic-TLS variables (global-dynamic, local-dynamic) are
correctly initialised in IFUNC resolvers reached via dlopen.
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/>. */
/* This test dlopens three modules covering the dynamic-TLS access variants:
gd-lib file-local 'sentinel' + tls_model("global-dynamic")
ld-lib file-local 'sentinel' + tls_model("local-dynamic")
gd-global-lib external 'sentinel' + tls_model("global-dynamic")
In each case the IFUNC resolver's read of 'sentinel' must traverse the
dynamic-TLS resolution path at the moment the resolver fires during
dlopen-time relocation.
Each variant additionally calls the resolved IFUNC from a thread spawned
after dlopen, to verify per-thread DTV propagation for the newly-loaded
module. */
#include <support/check.h>
#include <support/xdlfcn.h>
#include <support/xthread.h>
#define SENTINEL 0x5A5A1234
struct ifunc_handles
{
int (*get_last_seen_sentinel) (void);
int (**fptr) (void);
int (*ifunc_tls) (void);
};
static void *
ifunc_caller (void *arg)
{
struct ifunc_handles *h = arg;
TEST_COMPARE ((*h->fptr) (), SENTINEL);
TEST_COMPARE (h->ifunc_tls (), SENTINEL);
return NULL;
}
static void
test_lib (const char *soname)
{
void *handle = xdlopen (soname, RTLD_LAZY | RTLD_LOCAL);
struct ifunc_handles h;
h.get_last_seen_sentinel = xdlsym (handle, "get_last_seen_sentinel");
h.fptr = xdlsym (handle, "fptr");
h.ifunc_tls = xdlsym (handle, "ifunc_tls");
TEST_COMPARE (h.get_last_seen_sentinel (), SENTINEL);
TEST_VERIFY (*h.fptr != NULL);
TEST_COMPARE ((*h.fptr) (), SENTINEL);
TEST_COMPARE (h.ifunc_tls (), SENTINEL);
/* From a thread spawned *after* the dlopen, which exercises DTV propagation
for the new module into a fresh TCB. */
pthread_t consumer = xpthread_create (NULL, ifunc_caller, &h);
xpthread_join (consumer);
xdlclose (handle);
}
static int
do_test (void)
{
test_lib ("tst-ifunc-tls-init-gd-lib.so");
test_lib ("tst-ifunc-tls-init-ld-lib.so");
test_lib ("tst-ifunc-tls-init-gd-global-lib.so");
return 0;
}
#include <support/test-driver.c>
-2
View File
@@ -1,2 +0,0 @@
#define TLS_MODEL "global-dynamic"
#include "tst-ifunc-tls-init-gd-ld-lib-skeleton.c"
-2
View File
@@ -1,2 +0,0 @@
#define TLS_MODEL "local-dynamic"
#include "tst-ifunc-tls-init-gd-ld-lib-skeleton.c"
-61
View File
@@ -1,61 +0,0 @@
/* Test that static-TLS initialisation works correctly with IFUNC resolvers.
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/>. */
/* The initial-exec TLS model keeps the access path to a single TP-relative
load, so the test is sensitive to whether the static TLS block has been
populated rather than to any __tls_get_addr / DTV-update timing. */
#include <sys/cdefs.h>
#define SENTINEL 0x5A5A1234
/* The 'volatile' avoids constant fold optimization in impl_ok. */
static volatile __thread int sentinel
__attribute__ ((tls_model ("initial-exec"))) = SENTINEL;
static volatile int last_seen_sentinel;
static int
impl_ok (void)
{
return SENTINEL;
}
static int
impl_bad (void)
{
return 0;
}
int
get_last_seen_sentinel (void)
{
return last_seen_sentinel;
}
static typeof (impl_ok) *
__attribute_used__
resolver (void)
{
int s = sentinel;
last_seen_sentinel = s;
return s == SENTINEL ? impl_ok : impl_bad;
}
int ifunc_tls (void) __attribute__ ((ifunc ("resolver")));
/* Force a non-PLT relocation against the IFUNC symbol. */
int (*fptr) (void) = ifunc_tls;
-1
View File
@@ -1 +0,0 @@
#include "tst-ifunc-tls-init-lib-skeleton.c"
-1
View File
@@ -1 +0,0 @@
#include "tst-ifunc-tls-init-lib-skeleton.c"
-3
View File
@@ -1,3 +0,0 @@
#define TLS_MODEL "global-dynamic"
#define SENTINEL_STORAGE /* empty */
#include "tst-ifunc-tls-init-gd-ld-lib-skeleton.c"
-69
View File
@@ -1,69 +0,0 @@
/* Check that an IFUNC resolver in a dlopen'd DSO can read .tdata-initialised
__thread storage when the TLS access is compiled as a TLSDESC sequence.
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/>. */
/* Sibling of tst-ifunc-tls-init-gd-ld, but the TLS access in the loaded
DSO is compiled with the TLSDESC dialect (x86 gnu2, aarch64 desc). */
#include <support/check.h>
#include <support/xdlfcn.h>
#include <support/xthread.h>
#define SENTINEL 0x5A5A1234
struct ifunc_handles
{
int (*get_last_seen_sentinel) (void);
int (**fptr) (void);
int (*ifunc_tls) (void);
};
static void *
ifunc_caller (void *arg)
{
struct ifunc_handles *h = arg;
TEST_COMPARE ((*h->fptr) (), SENTINEL);
TEST_COMPARE (h->ifunc_tls (), SENTINEL);
return NULL;
}
static int
do_test (void)
{
void *handle = xdlopen ("tst-ifunc-tls-init-tlsdesc-lib.so",
RTLD_LAZY | RTLD_LOCAL);
struct ifunc_handles h;
h.get_last_seen_sentinel = xdlsym (handle, "get_last_seen_sentinel");
h.fptr = xdlsym (handle, "fptr");
h.ifunc_tls = xdlsym (handle, "ifunc_tls");
TEST_COMPARE (h.get_last_seen_sentinel (), SENTINEL);
TEST_VERIFY (*h.fptr != NULL);
TEST_COMPARE ((*h.fptr) (), SENTINEL);
TEST_COMPARE (h.ifunc_tls (), SENTINEL);
pthread_t consumer = xpthread_create (NULL, ifunc_caller, &h);
xpthread_join (consumer);
xdlclose (handle);
return 0;
}
#include <support/test-driver.c>
-85
View File
@@ -1,85 +0,0 @@
/* Check if static-TLS variables are correctly intialized in IFUNC resolvers.
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/>. */
/* Checks if a IFUNC resolver sees if a TLS blocker is properly initialized.
The tst-ifunc-tls-init-lib.so carries:
- a thread-local 'sentinel' initialised to SENTINEL.
- an IFUNC 'ifunc_tls' whose resolver picks impl_ok when sentinel reads
as SENTINEL and impl_bad (returning 0) otherwise.
- an IFUNC-backed function-pointer global 'fptr' whose initialiser
produces a non-PLT relocation. */
#include <support/check.h>
#include <support/xdlfcn.h>
#define SENTINEL 0x5A5A1234
extern int ifunc_tls (void);
extern int (*fptr) (void);
extern int get_last_seen_sentinel (void);
static void
test_tls_ifunc (int (*check_sentinel)(void),
int (*check_fptr)(void),
int (*check_ifunc_tls)(void))
{
/* Force the IFUNC to be resolved before inspecting what the resolver
observed. In a non-PIE executable the references to the IFUNC are
satisfied through a canonical IPLT entry in the executable, which under
lazy binding is resolved on first use rather than during startup
relocation. Calling fptr/ifunc_tls here guarantees the resolver has run
regardless of PIE-ness or binding mode. */
TEST_VERIFY (check_fptr != NULL);
TEST_COMPARE (check_fptr (), SENTINEL);
/* Issue the ifunc directly as well. */
TEST_COMPARE (check_ifunc_tls (), SENTINEL);
/* Primary check: 'get_last_seen_sentinel' returns the value of the DSO's
thread-local 'sentinel' as observed by the resolver at the moment it ran
for the IFUNC reloc that initialised fptr. The getter is a regular
PLT-resolved function in the DSO, so the read of the diagnostic global
does NOT go through a COPY relocation that could overwrite the resolver's
write. */
TEST_COMPARE (check_sentinel (), SENTINEL);
}
static int
do_test (void)
{
test_tls_ifunc (get_last_seen_sentinel, fptr, ifunc_tls);
/* Same as before, but check the dlopen path. */
void *handle = xdlopen ("tst-ifunc-tls-init-lib2.so",
RTLD_LAZY | RTLD_LOCAL);
int (*get_last_seen_sentinel_dlopen) (void)
= xdlsym (handle, "get_last_seen_sentinel");
int (**fptr_dlopen) (void) = xdlsym (handle, "fptr");
int (*ifunc_tls_dlopen) (void) = xdlsym (handle, "ifunc_tls");
test_tls_ifunc (get_last_seen_sentinel_dlopen, *fptr_dlopen,
ifunc_tls_dlopen);
xdlclose (handle);
return 0;
}
#include <support/test-driver.c>
-58
View File
@@ -1,58 +0,0 @@
/* Shared library for tst-ifunc-tls-write.
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/>. */
/* The DSO defines an initial-exec thread-local 'counter' initialised to
SENTINEL, the IFUNC resolver (via the non-PLT IFUNC reloc on 'fptr')
overwrites counter with MARKER, and the test then reads counter back
through a getter. */
#include <sys/cdefs.h>
#define SENTINEL 0x5A5A1234u
#define MARKER 0x32125A5Au
/* The 'volatile' avoids constant fold optimization in impl_ok. */
static volatile __thread unsigned int counter
__attribute__ ((tls_model ("initial-exec"))) = SENTINEL;
static unsigned int
impl (void)
{
return 0;
}
static typeof (impl) *
__attribute_used__
resolver (void)
{
counter = MARKER;
return impl;
}
unsigned int ifunc_write (void) __attribute__ ((ifunc ("resolver")));
/* Force the resolver rather than lazy bind on first call, which would
re-write counter after the test reads it). Using a COPY'd fptr also lets
the test verify the resolver ran without making a PLT call that would
itself fire the resolver again. */
unsigned int (*fptr) (void) = ifunc_write;
unsigned int
get_counter (void)
{
return counter;
}
-29
View File
@@ -1,29 +0,0 @@
/* Shared object for the static-dlopen pointer guard test.
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 <setjmp.h>
static jmp_buf jb;
void (*do_longjmp) (jmp_buf);
void
foo (void)
{
if (setjmp (jb) == 0)
do_longjmp (jb);
}
-51
View File
@@ -1,51 +0,0 @@
/* Test that the pointer guard is propagated to ld.so via static dlopen.
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/>. */
/* A statically linked program dlopens a shared object; the object's setjmp
uses the just-mapped libc.so's pointer guard while the longjmp below uses
the program's guard. Unless __rtld_static_init propagates the guard to
the loaded loader the two differ, and the setjmp/longjmp round-trip jumps
to a corrupt address and crashes. */
#include <setjmp.h>
#include <support/check.h>
#include <support/xdlfcn.h>
static void
call_longjmp (jmp_buf jb)
{
longjmp (jb, 1);
}
static int
do_test (void)
{
void *h = xdlopen ("tst-ptrguard-static-dlopen-mod.so", RTLD_NOW);
void (*foo) (void) = xdlsym (h, "foo");
void (**do_longjmp) (jmp_buf) = xdlsym (h, "do_longjmp");
*do_longjmp = call_longjmp;
/* foo () sets the jump buffer and calls back into call_longjmp; a
mismatched guard makes the return jump fault. */
foo ();
xdlclose (h);
return 0;
}
#include <support/test-driver.c>
@@ -1 +0,0 @@
cp $B/elf/tst-ptrguard-static-dlopen-mod.so $L/tst-ptrguard-static-dlopen-mod.so
-32
View File
@@ -1,32 +0,0 @@
/* Check that .tdata relocations are applied to the static TLS slot.
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 <support/check.h>
#define EXPECTED 0x12345678
extern int call_through_tls (void);
static int
do_test (void)
{
TEST_COMPARE (call_through_tls (), EXPECTED);
return 0;
}
#include <support/test-driver.c>
-6
View File
@@ -1,6 +0,0 @@
# defines target $(gen-gconv-modules) that ensures gconv-modules are available
gen-gconv-modules := $(common-objpfx)iconvdata/gconv-modules
$(gen-gconv-modules):
$(MAKE) -C ../iconvdata subdir=iconvdata $@
+2 -2
View File
@@ -39,8 +39,8 @@ struct protoent_data {};
#include <nss/nss_files/files-parse.c>
LINE_PARSER
("#",
STRING_FIELD (result->p_name, isspace, true);
INT_FIELD (result->p_proto, isspace, true, 10,);
STRING_FIELD (result->p_name, isspace, 1);
INT_FIELD (result->p_proto, isspace, 1, 10,);
)
enum nss_status
+3 -3
View File
@@ -39,9 +39,9 @@ struct servent_data {};
#define ISSC_OR_SPACE(c) ((c) == ';' || isspace (c))
LINE_PARSER
("#",
STRING_FIELD (result->s_name, ISSC_OR_SPACE, true);
STRING_FIELD (result->s_proto, ISSC_OR_SPACE, true);
INT_FIELD (result->s_port, ISSC_OR_SPACE, false, 10, htons);
STRING_FIELD (result->s_name, ISSC_OR_SPACE, 1);
STRING_FIELD (result->s_proto, ISSC_OR_SPACE, 1);
INT_FIELD (result->s_port, ISSC_OR_SPACE, 10, 0, htons);
)
enum nss_status
+1 -2
View File
@@ -139,8 +139,7 @@ $(objpfx)test-iconvconfig.out: $(objpfx)iconvconfig
rm -f $$tmp) > $@; \
$(evaluate-test)
$(objpfx)tst-iconv_prog.out: tst-iconv_prog.sh $(objpfx)iconv_prog \
$(gen-locales)
$(objpfx)tst-iconv_prog.out: tst-iconv_prog.sh $(objpfx)iconv_prog
$(BASH) $< $(common-objdir) '$(test-wrapper-env)' \
'$(run-program-env)' '$(rpath-link)' > $@; \
$(evaluate-test)
+2 -4
View File
@@ -144,10 +144,8 @@
if (irreversible == NULL) \
{ \
/* This means we are in call from __gconv_transliterate. In this \
case we are not doing any error recovery ourselves. Do not create \
a persistent error state. If __gconv_transliterate exhausts all \
alternatives, it will call __gconv_mark_illegal_input itself. */ \
result = __GCONV_ILLEGAL_INPUT; \
case we are not doing any error recovery ourselves. */ \
result = __gconv_mark_illegal_input (step_data); \
break; \
} \
\
+10 -20
View File
@@ -29,10 +29,10 @@ LIBPATH=$codir:$codir/iconvdata
# How the start the iconv(1) program. $from is not defined/expanded yet.
ICONV='
$test_wrapper_env $run_program_env
$codir/elf/ld.so --library-path $library_path:$LIBPATH --inhibit-rpath ${from}.so
$codir/iconv/iconv_prog
'
ICONV="$test_wrapper_env $run_program_env $ICONV"
TIMEOUTFACTOR=${TIMEOUTFACTOR:-1}
@@ -220,7 +220,6 @@ testarray=(
"\x00\x00;;INVALID;UTF-8;1"
"\x00\x00;;UTF-8;INVALID;1"
"\xc3\xa9;;UTF-8;ASCII//TRANSLIT;0"
"X\xc2\xbdY;;UTF-8;ASCII//TRANSLIT;0"
)
# Requires $twobyte input, $c flag, $from, and $to to be set; sets $ret
@@ -281,21 +280,12 @@ check_errtest_result ()
fi
}
run_test_array ()
{
for testcommand in "${testarray[@]}"; do
twobyte="$(echo "$testcommand" | cut -d";" -f 1)"
c="$(echo "$testcommand" | cut -d";" -f 2)"
from="$(echo "$testcommand" | cut -d";" -f 3)"
to="$(echo "$testcommand" | cut -d";" -f 4)"
eret="$(echo "$testcommand" | cut -d";" -f 5)"
execute_test
check_errtest_result
done
}
echo "info: testing C locale"
run_test_array
echo "info: testing en_US.UTF-8 locale"
run_program_env="$run_program_env LC_ALL=en_US.UTF-8"
run_test_array
for testcommand in "${testarray[@]}"; do
twobyte="$(echo "$testcommand" | cut -d";" -f 1)"
c="$(echo "$testcommand" | cut -d";" -f 2)"
from="$(echo "$testcommand" | cut -d";" -f 3)"
to="$(echo "$testcommand" | cut -d";" -f 4)"
eret="$(echo "$testcommand" | cut -d";" -f 5)"
execute_test
check_errtest_result
done
+1 -2
View File
@@ -113,7 +113,6 @@
#define HAVE_LIBINTL_H 1
#define HAVE_WCTYPE_H 1
#define HAVE_ISWCTYPE 1
#define HAVE_MEMPCPY 1
#define ENABLE_NLS 1
/* The symbols in all the user (non-_) macros are C symbols. */
@@ -671,7 +670,7 @@ for linking")
/* Helper / base macros for indirect function symbols. */
#define __ifunc_resolver(type_name, name, expr, init, classifier, ...) \
classifier \
classifier inhibit_stack_protector \
__typeof (type_name) *name##_ifunc (__VA_ARGS__) \
{ \
init (); \
-5
View File
@@ -70,11 +70,6 @@ libc_hidden_proto (__libc_res_nameinquery)
extern __typeof (__res_queriesmatch) __libc_res_queriesmatch;
libc_hidden_proto (__libc_res_queriesmatch)
extern const struct res_sym __p_class_syms[];
libresolv_hidden_proto (__p_class_syms)
extern const struct res_sym __p_type_syms[];
libresolv_hidden_proto (__p_type_syms)
/* Variant of res_hnok which operates on binary (but uncompressed) names. */
bool __res_binary_hnok (const unsigned char *dn) attribute_hidden;
+4 -65
View File
@@ -5,73 +5,12 @@
#ifndef _ISOMAC
# include <stdint.h>
# if __glibc_has_builtin (__builtin_stdc_rotate_left)
# define stdc_rotate_left(__x, __n) (__builtin_stdc_rotate_left (__x, __n))
# define stdc_rotate_left_uc(__x, __n) (stdc_rotate_left (__x, __n))
# define stdc_rotate_left_us(__x, __n) (stdc_rotate_left (__x, __n))
# define stdc_rotate_left_ui(__x, __n) (stdc_rotate_left (__x, __n))
# define stdc_rotate_left_ul(__x, __n) (stdc_rotate_left (__x, __n))
# define stdc_rotate_left_ull(__x, __n) (stdc_rotate_left (__x, __n))
# else
# if __WORDSIZE == 64
# define __ROL_UL_GENERIC __rol64_inline
# else
# define __ROL_UL_GENERIC __rol32_inline
# endif
# define stdc_rotate_left(__x, __n) \
_Generic((__x), \
unsigned char: __rol8_inline, \
unsigned short: __rol16_inline, \
unsigned int: __rol32_inline, \
unsigned long: __ROL_UL_GENERIC, \
unsigned long long: __rol64_inline \
)(__x, __n)
# define __rol_generic(__v, __n) \
((__v << (__n & (sizeof __v * 8 - 1))) \
| (__v >> ((-__n) & (sizeof __v * 8 - 1))))
static __always_inline uint8_t
__rol8_inline (uint8_t __x, unsigned int __n)
{
return __rol_generic (__x, __n);
}
static __always_inline uint16_t
__rol16_inline (uint16_t __x, unsigned int __n)
{
return __rol_generic (__x, __n);
}
static __always_inline uint32_t
__rol32_inline (uint32_t __x, unsigned int __n)
{
return __rol_generic (__x, __n);
}
static __always_inline uint64_t
__rol64_inline (uint64_t __x, unsigned int __n)
{
return __rol_generic (__x, __n);
}
# define stdc_rotate_left_uc(__x, __n) (__rol8_inline (__x, __n))
# define stdc_rotate_left_us(__x, __n) (__rol16_inline (__x, __n))
# define stdc_rotate_left_ui(__x, __n) (__rol32_inline (__x, __n))
# if __WORDSIZE == 64
# define stdc_rotate_left_ul(__x, __n) (__rol64_inline (__x, __n))
# else
# define stdc_rotate_left_ul(__x, __n) (__rol32_inline (__x, __n))
# endif
# define stdc_rotate_left_ull(__x, __n) (__rol64_inline (__x, __n))
# endif /* __glibc_has_builtin (__builtin_stdc_rotate_left) */
# if __glibc_has_builtin (__builtin_stdc_rotate_right)
# define stdc_rotate_right(__x, __n) (__builtin_stdc_rotate_right (__x, __n))
# if __glibc_has_builtin (___builtin_stdc_rotate_right)
# define stdc_rotate_right(__x, __n) (___builtin_stdc_rotate_right (__x, __n))
# define stdc_rotate_right_uc(__x, __n) (stdc_rotate_right (__x, __n))
# define stdc_rotate_right_us(__x, __n) (stdc_rotate_right (__x, __n))
# define stdc_rotate_right_ui(__x, __n) (stdc_rotate_right (__x, __n))
# define stdc_rotate_right_ul(__x, __n) (stdc_rotate_right (__x, __n))
# define stdc_rotate_right_ull(__x, __n) (stdc_rotate_right (__x, __n))
# else
# if __WORDSIZE == 64
# define __ROR_UL_GENERIC __ror64_inline
@@ -109,7 +48,7 @@ __ror32_inline (uint32_t __x, unsigned int __n)
return __ror_generic (__x, __n);
}
static __always_inline uint64_t
static __always_inline uint64_t
__ror64_inline (uint64_t __x, unsigned int __n)
{
return __ror_generic (__x, __n);
@@ -123,7 +62,7 @@ __ror64_inline (uint64_t __x, unsigned int __n)
# define stdc_rotate_right_ul(__x, __n) (__ror32_inline (__x, __n))
# endif
# define stdc_rotate_right_ull(__x, __n) (__ror64_inline (__x, __n))
# endif /* __glibc_has_builtin (__builtin_stdc_rotate_right) */
# endif /* __glibc_has_builtin (___builtin_stdc_rotate_right) */
# endif /* _ISOMAC */
+4
View File
@@ -134,6 +134,10 @@ endif
$(inst_sysconfdir)/rpc: etc.rpc $(+force)
$(do-install)
ifeq ($(build-static-nss),yes)
CFLAGS += -DSTATIC_NSS
endif
ifeq ($(have-test-clang),yes)
CFLAGS-tst-deadline.c += -Wno-ignored-attributes
endif
+6 -26
View File
@@ -22,21 +22,13 @@ subdir = intl
include ../Makeconfig
headers = libintl.h
routines = bindtextdom dcgettext dgettext gettext \
routines = bindtextdom dcgettext dgettext gettext \
dcigettext dcngettext dngettext ngettext \
finddomain loadmsgcat localealias textdomain
aux = l10nflist explodename plural plural-exp hash-string
aux = l10nflist explodename plural plural-exp hash-string
multithread-test-srcs := tst-gettext4 tst-gettext5 tst-gettext6
test-srcs := \
tst-gettext \
tst-translit \
tst-gettext2 \
tst-codeset \
tst-gettext3 \
tst-plural-eval
# test-srcs
test-srcs := tst-gettext tst-translit tst-gettext2 tst-codeset tst-gettext3
ifeq ($(have-thread-library),yes)
test-srcs += $(multithread-test-srcs)
endif
@@ -61,15 +53,9 @@ $(objpfx)plural.o: $(objpfx)plural.c
ifeq ($(run-built-tests),yes)
ifeq (yes,$(build-shared))
ifneq ($(strip $(MSGFMT)),:)
tests-special += \
$(objpfx)tst-translit.out \
$(objpfx)tst-gettext.out \
$(objpfx)tst-gettext2.out \
$(objpfx)tst-codeset.out \
$(objpfx)tst-gettext3.out \
$(objpfx)tst-plural-eval.out
# tests-special
tests-special += $(objpfx)tst-translit.out $(objpfx)tst-gettext.out \
$(objpfx)tst-gettext2.out $(objpfx)tst-codeset.out \
$(objpfx)tst-gettext3.out
ifeq ($(have-thread-library),yes)
tests-special += $(objpfx)tst-gettext4.out $(objpfx)tst-gettext5.out \
$(objpfx)tst-gettext6.out
@@ -117,10 +103,6 @@ $(objpfx)tst-gettext4.out: tst-gettext4.sh $(objpfx)tst-gettext4
$(objpfx)tst-gettext6.out: tst-gettext6.sh $(objpfx)tst-gettext6
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)' $(common-objpfx)intl/; \
$(evaluate-test)
$(objpfx)tst-plural-eval.out: tst-plural-eval.sh $(objpfx)tst-plural-eval
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)' \
$(common-objpfx)intl/; \
$(evaluate-test)
$(objpfx)tst-codeset.out: $(codeset_mo)
$(objpfx)tst-gettext3.out: $(codeset_mo)
@@ -158,7 +140,6 @@ CFLAGS-tst-gettext3.c += -DOBJPFX=\"$(objpfx)\"
CFLAGS-tst-gettext4.c += -DOBJPFX=\"$(objpfx)\"
CFLAGS-tst-gettext5.c += -DOBJPFX=\"$(objpfx)\"
CFLAGS-tst-gettext6.c += -DOBJPFX=\"$(objpfx)\"
CFLAGS-tst-plural-eval.c += -DOBJPFX=\"$(objpfx)\"
ifeq ($(have-thread-library),yes)
ifeq (yes,$(build-shared))
@@ -175,7 +156,6 @@ $(objpfx)tst-gettext3.out: $(objpfx)tst-gettext.out
$(objpfx)tst-gettext4.out: $(objpfx)tst-gettext.out
$(objpfx)tst-gettext5.out: $(objpfx)tst-gettext.out
$(objpfx)tst-gettext6.out: $(objpfx)tst-gettext.out
$(objpfx)tst-plural-eval.out: $(objpfx)tst-gettext.out
CPPFLAGS += -D'LOCALEDIR="$(localedir)"' \
-D'LOCALE_ALIAS_PATH="$(localedir)"' \
+130 -28
View File
@@ -68,6 +68,20 @@ extern int errno;
#include <locale.h>
#ifdef _LIBC
/* Guess whether integer division by zero raises signal SIGFPE.
Set to 1 only if you know for sure. In case of doubt, set to 0. */
# if defined __alpha__ || defined __arm__ || defined __i386__ \
|| defined __m68k__ || defined __s390x__
# define INTDIV0_RAISES_SIGFPE 1
# else
# define INTDIV0_RAISES_SIGFPE 0
# endif
#endif
#if !INTDIV0_RAISES_SIGFPE
# include <signal.h>
#endif
#if defined HAVE_SYS_PARAM_H || defined _LIBC
# include <sys/param.h>
#endif
@@ -81,6 +95,9 @@ extern int errno;
#ifdef _LIBC
# include <libintl.h>
#else
# ifdef IN_LIBGLOCALE
# include <libintl.h>
# endif
# include "libgnuintl.h"
#endif
#include "hash-string.h"
@@ -211,7 +228,7 @@ static void *mempcpy (void *dest, const void *src, size_t n);
#endif
/* Whether to support different locales in different threads. */
#if defined _LIBC || HAVE_USELOCALE
#if defined _LIBC || HAVE_USELOCALE || defined IN_LIBGLOCALE
# define HAVE_PER_THREAD_LOCALE
#endif
@@ -230,6 +247,11 @@ struct known_translation_t
const char *localename;
#endif
#ifdef IN_LIBGLOCALE
/* The character encoding. */
const char *encoding;
#endif
/* State of the catalog counter at the point the string was found. */
int counter;
@@ -277,6 +299,10 @@ transcmp (const void *p1, const void *p2)
if (result == 0)
#endif
{
#ifdef IN_LIBGLOCALE
result = strcmp (s1->encoding, s2->encoding);
if (result == 0)
#endif
/* We compare the category last (though this is the cheapest
operation) since it is hopefully always the same (namely
LC_MESSAGES). */
@@ -292,9 +318,11 @@ transcmp (const void *p1, const void *p2)
textdomain(3). The default value for this is "messages". */
const char _nl_default_default_domain[] attribute_hidden = "messages";
#ifndef IN_LIBGLOCALE
/* Value used as the default domain for gettext(3). */
const char *_nl_current_default_domain attribute_hidden
= _nl_default_default_domain;
#endif
/* Contains the default location of the message catalogs. */
#if defined __EMX__
@@ -310,17 +338,25 @@ libc_hidden_data_def (_nl_default_dirname)
# endif
#endif
#ifndef IN_LIBGLOCALE
/* List with bindings of specific domains created by bindtextdomain()
calls. */
struct binding *_nl_domain_bindings;
#endif
/* Prototypes for local functions. */
static char *plural_lookup (struct loaded_l10nfile *domain,
unsigned long int n,
const char *translation, size_t translation_len);
#ifdef IN_LIBGLOCALE
static const char *guess_category_value (int category,
const char *categoryname,
const char *localename);
#else
static const char *guess_category_value (int category,
const char *categoryname);
#endif
#ifdef _LIBC
# include "../locale/localeinfo.h"
@@ -328,7 +364,7 @@ static const char *guess_category_value (int category,
#else
static const char *category_to_name (int category);
#endif
#if (defined _LIBC || HAVE_ICONV)
#if (defined _LIBC || HAVE_ICONV) && !defined IN_LIBGLOCALE
static const char *get_output_charset (struct binding *domainbinding);
#endif
@@ -407,9 +443,18 @@ static int enable_secure;
/* Look up MSGID in the DOMAINNAME message catalog for the current
CATEGORY locale and, if PLURAL is nonzero, search over string
depending on the plural form determined by N. */
#ifdef IN_LIBGLOCALE
char *
gl_dcigettext (const char *domainname,
const char *msgid1, const char *msgid2,
int plural, unsigned long int n,
int category,
const char *localename, const char *encoding)
#else
char *
DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
int plural, unsigned long int n, int category)
#endif
{
struct loaded_l10nfile *domain;
struct binding *binding;
@@ -424,7 +469,7 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
int saved_errno;
struct known_translation_t search;
struct known_translation_t **foundp = NULL;
#if defined HAVE_PER_THREAD_LOCALE
#if defined HAVE_PER_THREAD_LOCALE && !defined IN_LIBGLOCALE
const char *localename;
#endif
size_t domainname_len;
@@ -471,16 +516,21 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
search.domainname = domainname;
search.category = category;
#ifdef HAVE_PER_THREAD_LOCALE
# ifdef _LIBC
# ifndef IN_LIBGLOCALE
# ifdef _LIBC
localename = __current_locale_name (category);
# else
# else
categoryname = category_to_name (category);
# define CATEGORYNAME_INITIALIZED
# define CATEGORYNAME_INITIALIZED
localename = _nl_locale_name_thread_unsafe (category, categoryname);
if (localename == NULL)
localename = "";
# endif
# endif
search.localename = localename;
# ifdef IN_LIBGLOCALE
search.encoding = encoding;
# endif
/* Since tfind/tsearch manage a balanced tree, concurrent tfind and
tsearch calls can be fatal. */
@@ -512,6 +562,12 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
DETERMINE_SECURE;
/* First find matching binding. */
#ifdef IN_LIBGLOCALE
/* We can use a trivial binding, since _nl_find_msg will ignore it anyway,
and _nl_load_domain and _nl_find_domain just pass it through. */
binding = NULL;
dirname = bindtextdomain (domainname, NULL);
#else
for (binding = _nl_domain_bindings; binding != NULL; binding = binding->next)
{
int compare = strcmp (domainname, binding->domainname);
@@ -531,6 +587,7 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
else
{
dirname = binding->dirname;
#endif
if (!IS_ABSOLUTE_PATH (dirname))
{
/* We have a relative path. Make it absolute now. */
@@ -546,13 +603,20 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
goto return_untranslated;
dirname = xdirname;
}
#ifndef IN_LIBGLOCALE
}
#endif
/* Now determine the symbolic name of CATEGORY and its value. */
#ifndef CATEGORYNAME_INITIALIZED
categoryname = category_to_name (category);
#endif
#ifdef IN_LIBGLOCALE
categoryvalue = guess_category_value (category, categoryname, localename);
#else
categoryvalue = guess_category_value (category, categoryname);
#endif
domainname_len = strlen (domainname);
xdomainname = (char *) malloc (strlen (categoryname)
+ domainname_len + 5);
@@ -611,7 +675,11 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
if (domain != NULL)
{
#if defined IN_LIBGLOCALE
retval = _nl_find_msg (domain, binding, encoding, msgid1, &retlen);
#else
retval = _nl_find_msg (domain, binding, msgid1, 1, &retlen);
#endif
if (retval == NULL)
{
@@ -619,8 +687,13 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
for (cnt = 0; domain->successor[cnt] != NULL; ++cnt)
{
#if defined IN_LIBGLOCALE
retval = _nl_find_msg (domain->successor[cnt], binding,
encoding, msgid1, &retlen);
#else
retval = _nl_find_msg (domain->successor[cnt], binding,
msgid1, 1, &retlen);
#endif
/* Resource problems are not fatal, instead we return no
translation. */
@@ -681,6 +754,9 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
newp->category = category;
#ifdef HAVE_PER_THREAD_LOCALE
newp->localename = new_localename;
#endif
#ifdef IN_LIBGLOCALE
newp->encoding = encoding;
#endif
newp->counter = _nl_msg_cat_cntr;
newp->domain = domain;
@@ -761,10 +837,17 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
in case of a memory allocation failure during conversion (only if
ENCODING != NULL resp. CONVERT == true). */
char *
#ifdef IN_LIBGLOCALE
_nl_find_msg (struct loaded_l10nfile *domain_file,
struct binding *domainbinding, const char *encoding,
const char *msgid,
size_t *lengthp)
#else
_nl_find_msg (struct loaded_l10nfile *domain_file,
struct binding *domainbinding,
const char *msgid, int convert,
size_t *lengthp)
#endif
{
struct loaded_domain *domain;
nls_uint32 nstrings;
@@ -870,10 +953,16 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
}
#if defined _LIBC || HAVE_ICONV
# ifdef IN_LIBGLOCALE
if (encoding != NULL)
# else
if (convert)
# endif
{
/* We are supposed to do a conversion. */
# ifndef IN_LIBGLOCALE
const char *encoding = get_output_charset (domainbinding);
# endif
size_t nconversions;
struct converted_domain *convd;
size_t i;
@@ -967,7 +1056,12 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
reallocate domain->conversions because we pass
encoding = NULL or convert = 0, respectively. */
nullentry =
# ifdef IN_LIBGLOCALE
_nl_find_msg (domain_file, domainbinding, NULL, "",
&nullentrylen);
# else
_nl_find_msg (domain_file, domainbinding, "", 0, &nullentrylen);
# endif
/* Resource problems are fatal. If we continue onwards we will
only attempt to calloc a new conv_tab and fail later. */
@@ -1136,6 +1230,7 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
not_translated_yet:
inbuf = (const unsigned char *) result;
outbuf = freemem + sizeof (size_t);
# ifndef _LIBC
transmem_list = NULL;
# endif
@@ -1144,16 +1239,13 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
while (1)
{
transmem_block_t *newmem;
if (freemem_size < sizeof (size_t))
goto resize_freemem;
outbuf = freemem + sizeof (size_t);
# ifdef _LIBC
size_t non_reversible;
int res;
if (freemem_size < sizeof (size_t))
goto resize_freemem;
res = __gconv (convd->conv,
&inbuf, inbuf + resultlen,
&outbuf,
@@ -1179,6 +1271,9 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
char *outptr = (char *) outbuf;
size_t outleft;
if (freemem_size < sizeof (size_t))
goto resize_freemem;
outleft = freemem_size - sizeof (size_t);
if (iconv (convd->conv,
(ICONV_CONST char **) &inptr, &inleft,
@@ -1247,6 +1342,8 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
transmem_list = newmem;
freemem = newmem;
# endif
outbuf = freemem + sizeof (size_t);
}
/* We have now in our buffer a converted string. Put this
@@ -1284,19 +1381,14 @@ plural_lookup (struct loaded_l10nfile *domain, unsigned long int n,
const char *translation, size_t translation_len)
{
struct loaded_domain *domaindata = (struct loaded_domain *) domain->data;
struct eval_result result;
unsigned long int index;
const char *p;
result = plural_eval (domaindata->plural, n);
if (result.status != PE_OK)
/* The plural expression evaluation failed. */
index = plural_eval (domaindata->plural, n);
if (index >= domaindata->nplurals)
/* This should never happen. It means the plural expression and the
given maximum value do not match. */
index = 0;
else if (result.value >= domaindata->nplurals)
/* The plural expression and the given maximum value do not match. */
index = 0;
else
index = result.value;
/* Skip INDEX strings at TRANSLATION. */
p = translation;
@@ -1378,13 +1470,21 @@ category_to_name (int category)
/* Guess value of current locale from value of the environment variables
or system-dependent defaults. */
static const char *
#ifdef IN_LIBGLOCALE
guess_category_value (int category, const char *categoryname,
const char *locale)
#else
guess_category_value (int category, const char *categoryname)
#endif
{
const char *language;
#ifndef IN_LIBGLOCALE
const char *locale;
#ifndef _LIBC
# ifndef _LIBC
const char *language_default;
int locale_defaulted;
# endif
#endif
/* We use the settings in the following order:
@@ -1401,17 +1501,18 @@ guess_category_value (int category, const char *categoryname)
- If the system provides both a list of languages and a default locale,
the former is used. */
#ifndef IN_LIBGLOCALE
/* Fetch the locale name, through the POSIX method of looking to `LC_ALL',
`LC_xxx', and `LANG'. On some systems this can be done by the
`setlocale' function itself. */
#ifdef _LIBC
# ifdef _LIBC
locale = __current_locale_name (category);
#else
# else
locale_defaulted = 0;
# if HAVE_USELOCALE
# if HAVE_USELOCALE
locale = _nl_locale_name_thread_unsafe (category, categoryname);
if (locale == NULL)
# endif
# endif
{
locale = _nl_locale_name_posix (category, categoryname);
if (locale == NULL)
@@ -1420,6 +1521,7 @@ guess_category_value (int category, const char *categoryname)
locale_defaulted = 1;
}
}
# endif
#endif
/* Ignore LANGUAGE and its system-dependent analogon if the locale is set
@@ -1445,7 +1547,7 @@ guess_category_value (int category, const char *categoryname)
language = getenv ("LANGUAGE");
if (language != NULL && language[0] != '\0')
return language;
#ifndef _LIBC
#if !defined IN_LIBGLOCALE && !defined _LIBC
/* The next priority value is the locale name, if not defaulted. */
if (locale_defaulted)
{
@@ -1459,7 +1561,7 @@ guess_category_value (int category, const char *categoryname)
return locale;
}
#if (defined _LIBC || HAVE_ICONV)
#if (defined _LIBC || HAVE_ICONV) && !defined IN_LIBGLOCALE
/* Returns the output charset. */
static const char *
get_output_charset (struct binding *domainbinding)
+34 -104
View File
@@ -14,59 +14,24 @@
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
/* Written by Ulrich Drepper and Bruno Haible. */
#ifndef STATIC
#define STATIC static
#endif
/* While the bison parser is able to support expressions of a maximum depth
of YYMAXDEPTH = 10000, the runtime evaluation of a parsed plural expression
has a smaller maximum recursion depth.
If we did not limit the recursion depth, a program that just invokes
ngettext() on a thread other than the main thread could get a crash by
stack overflow in the following circumstances:
- On systems with glibc, after the stack size has been reduced,
e.g. on x86_64 systems after "ulimit -s 260".
This stack size is only sufficient for ca. 3919 recursions.
Cf. <https://unix.stackexchange.com/questions/620720/>
- On systems with musl libc, because there the thread stack size (for a
thread other than the main thread) by default is only 128 KB, see
<https://wiki.musl-libc.org/functional-differences-from-glibc.html#Thread-stack-size>.
- On AIX 7 systems, because there the thread stack size (for a thread
other than the main thread) by default is only 96 KB, see
<https://www.ibm.com/docs/en/aix/7.1?topic=programming-threads-library-options>.
This stack size is only sufficient for between 887 and 1363 recursions,
depending on the compiler and compiler optimization options.
A maximum depth of 100 is a large enough for all practical needs
and also small enough to avoid stack overflow even with small thread stack
sizes. */
#ifndef EVAL_MAXDEPTH
# define EVAL_MAXDEPTH 100
#endif
/* A shorthand that denotes a successful evaluation result with a value V. */
#define OK(v) (struct eval_result) { .status = PE_OK, .value = (v) }
/* Evaluates a plural expression PEXP for n=N, up to ALLOWED_DEPTH. */
static struct eval_result
plural_eval_recurse (const struct expression *pexp, unsigned long int n,
unsigned int allowed_depth)
/* Evaluate the plural expression and return an index value. */
STATIC
unsigned long int
plural_eval (const struct expression *pexp, unsigned long int n)
{
if (allowed_depth == 0)
/* The allowed recursion depth is exhausted. */
return (struct eval_result) { .status = PE_STACKOVF };
allowed_depth--;
switch (pexp->nargs)
{
case 0:
switch (pexp->operation)
{
case var:
return OK (n);
return n;
case num:
return OK (pexp->val.num);
return pexp->val.num;
default:
break;
}
@@ -75,73 +40,52 @@ plural_eval_recurse (const struct expression *pexp, unsigned long int n,
case 1:
{
/* pexp->operation must be lnot. */
struct eval_result arg =
plural_eval_recurse (pexp->val.args[0], n, allowed_depth);
if (arg.status != PE_OK)
return arg;
return OK (! arg.value);
unsigned long int arg = plural_eval (pexp->val.args[0], n);
return ! arg;
}
case 2:
{
struct eval_result leftarg =
plural_eval_recurse (pexp->val.args[0], n, allowed_depth);
if (leftarg.status != PE_OK)
return leftarg;
unsigned long int leftarg = plural_eval (pexp->val.args[0], n);
if (pexp->operation == lor)
{
if (leftarg.value)
return OK (1);
struct eval_result rightarg =
plural_eval_recurse (pexp->val.args[1], n, allowed_depth);
if (rightarg.status != PE_OK)
return rightarg;
return OK (rightarg.value ? 1 : 0);
}
return leftarg || plural_eval (pexp->val.args[1], n);
else if (pexp->operation == land)
{
if (!leftarg.value)
return OK (0);
struct eval_result rightarg =
plural_eval_recurse (pexp->val.args[1], n, allowed_depth);
if (rightarg.status != PE_OK)
return rightarg;
return OK (rightarg.value ? 1 : 0);
}
return leftarg && plural_eval (pexp->val.args[1], n);
else
{
struct eval_result rightarg =
plural_eval_recurse (pexp->val.args[1], n, allowed_depth);
if (rightarg.status != PE_OK)
return rightarg;
unsigned long int rightarg = plural_eval (pexp->val.args[1], n);
switch (pexp->operation)
{
case mult:
return OK (leftarg.value * rightarg.value);
return leftarg * rightarg;
case divide:
if (rightarg.value == 0)
return (struct eval_result) { .status = PE_INTDIV };
return OK (leftarg.value / rightarg.value);
#if !INTDIV0_RAISES_SIGFPE
if (rightarg == 0)
raise (SIGFPE);
#endif
return leftarg / rightarg;
case module:
if (rightarg.value == 0)
return (struct eval_result) { .status = PE_INTDIV };
return OK (leftarg.value % rightarg.value);
#if !INTDIV0_RAISES_SIGFPE
if (rightarg == 0)
raise (SIGFPE);
#endif
return leftarg % rightarg;
case plus:
return OK (leftarg.value + rightarg.value);
return leftarg + rightarg;
case minus:
return OK (leftarg.value - rightarg.value);
return leftarg - rightarg;
case less_than:
return OK (leftarg.value < rightarg.value);
return leftarg < rightarg;
case greater_than:
return OK (leftarg.value > rightarg.value);
return leftarg > rightarg;
case less_or_equal:
return OK (leftarg.value <= rightarg.value);
return leftarg <= rightarg;
case greater_or_equal:
return OK (leftarg.value >= rightarg.value);
return leftarg >= rightarg;
case equal:
return OK (leftarg.value == rightarg.value);
return leftarg == rightarg;
case not_equal:
return OK (leftarg.value != rightarg.value);
return leftarg != rightarg;
default:
break;
}
@@ -152,24 +96,10 @@ plural_eval_recurse (const struct expression *pexp, unsigned long int n,
case 3:
{
/* pexp->operation must be qmop. */
struct eval_result boolarg =
plural_eval_recurse (pexp->val.args[0], n, allowed_depth);
if (boolarg.status != PE_OK)
return boolarg;
return plural_eval_recurse (pexp->val.args[boolarg.value ? 1 : 2], n,
allowed_depth);
unsigned long int boolarg = plural_eval (pexp->val.args[0], n);
return plural_eval (pexp->val.args[boolarg ? 1 : 2], n);
}
}
/* NOTREACHED */
return (struct eval_result) { .status = PE_ASSERT };
return 0;
}
/* Evaluates a plural expression PEXP for n=N. */
STATIC
struct eval_result
plural_eval (const struct expression *pexp, unsigned long int n)
{
return plural_eval_recurse (pexp, n, EVAL_MAXDEPTH);
}
#undef OK
+38 -34
View File
@@ -50,6 +50,7 @@
/* List of already loaded domains. */
static struct loaded_l10nfile *_nl_loaded_domains;
/* Return a data structure describing the message catalog described by
the DOMAINNAME and CATEGORY parameters with respect to the currently
established bindings. */
@@ -132,52 +133,55 @@ _nl_find_domain (const char *dirname, char *locale,
/* Now we determine the single parts of the locale name. First
look for the language. Termination symbols are `_', '.', and `@'. */
mask = _nl_explode_name (locale, &language, &modifier, &territory, &codeset,
&normalized_codeset);
if (mask != -1) /* Not out of memory? */
mask = _nl_explode_name (locale, &language, &modifier, &territory,
&codeset, &normalized_codeset);
if (mask == -1)
/* This means we are out of core. */
return NULL;
/* We need to protect modifying the _NL_LOADED_DOMAINS data. */
gl_rwlock_wrlock (lock);
/* Create all possible locale entries which might be interested in
generalization. */
retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
strlen (dirname) + 1, mask, language, territory,
codeset, normalized_codeset, modifier,
domainname, 1);
gl_rwlock_unlock (lock);
if (retval == NULL)
/* This means we are out of core. */
goto out;
if (retval->decided <= 0)
_nl_load_domain (retval, domainbinding);
if (retval->data == NULL)
{
/* We need to protect modifying the _NL_LOADED_DOMAINS data. */
gl_rwlock_wrlock (lock);
/* Create all possible locale entries which might be interested in
generalization. */
retval = _nl_make_l10nflist (&_nl_loaded_domains, dirname,
strlen (dirname) + 1, mask, language,
territory, codeset, normalized_codeset,
modifier, domainname, 1);
gl_rwlock_unlock (lock);
if (retval != NULL) /* Not out of memory? */
int cnt;
for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
{
if (retval->decided <= 0)
_nl_load_domain (retval, domainbinding);
if (retval->data == NULL)
{
int cnt;
for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
{
if (retval->successor[cnt]->decided <= 0)
_nl_load_domain (retval->successor[cnt], domainbinding);
if (retval->successor[cnt]->data != NULL)
break;
}
}
if (retval->successor[cnt]->decided <= 0)
_nl_load_domain (retval->successor[cnt], domainbinding);
if (retval->successor[cnt]->data != NULL)
break;
}
/* The space for normalized_codeset is dynamically allocated.
Free it. */
if (mask & XPG_NORM_CODESET)
free ((void *) normalized_codeset);
}
/* The room for an alias was dynamically allocated. Free it now. */
if (alias_value != NULL)
free (locale);
out:
/* The space for normalized_codeset is dynamically allocated. Free it. */
if (mask & XPG_NORM_CODESET)
free ((void *) normalized_codeset);
return retval;
}
#ifdef _LIBC
/* This is called from iconv/gconv_db.c's free_mem, as locales must
be freed before freeing gconv steps arrays. */
+21
View File
@@ -70,10 +70,18 @@ extern void _nl_unload_domain (struct loaded_domain *__domain)
# undef _INTL_REDIRECT_MACROS
# define _INTL_REDIRECT_MACROS
# include "libgnuintl.h"
# ifdef IN_LIBGLOCALE
extern char *gl_dcigettext (const char *__domainname,
const char *__msgid1, const char *__msgid2,
int __plural, unsigned long int __n,
int __category,
const char *__localename, const char *__encoding);
# else
extern char *libintl_dcigettext (const char *__domainname,
const char *__msgid1, const char *__msgid2,
int __plural, unsigned long int __n,
int __category);
# endif
#endif
#include "loadinfo.h"
@@ -208,7 +216,12 @@ struct binding
/* A counter which is incremented each time some previous translations
become invalid.
This variable is part of the external ABI of the GNU libintl. */
#ifdef IN_LIBGLOCALE
# include <glocale/config.h>
extern LIBGLOCALE_DLL_EXPORTED int _nl_msg_cat_cntr;
#else
extern LIBINTL_DLL_EXPORTED int _nl_msg_cat_cntr;
#endif
#ifndef _LIBC
extern const char *_nl_language_preferences_default (void);
@@ -245,10 +258,18 @@ void _nl_load_domain (struct loaded_l10nfile *__domain,
struct binding *__domainbinding)
attribute_hidden;
#ifdef IN_LIBGLOCALE
char *_nl_find_msg (struct loaded_l10nfile *domain_file,
struct binding *domainbinding, const char *encoding,
const char *msgid,
size_t *lengthp)
attribute_hidden;
#else
char *_nl_find_msg (struct loaded_l10nfile *domain_file,
struct binding *domainbinding, const char *msgid,
int convert, size_t *lengthp)
attribute_hidden;
#endif
/* The internal variables in the standalone libintl.a must have different
names than the internal variables in GNU libc, otherwise programs
+93 -84
View File
@@ -77,357 +77,361 @@
# include "lock.h"
#endif
#ifdef _LIBC
# define PRI_MACROS_BROKEN 0
#endif
/* Provide fallback values for macros that ought to be defined in <inttypes.h>.
Note that our fallback values need not be literal strings, because we don't
use them with preprocessor string concatenation. */
#if !defined PRId8
#if !defined PRId8 || PRI_MACROS_BROKEN
# undef PRId8
# define PRId8 "d"
#endif
#if !defined PRIi8
#if !defined PRIi8 || PRI_MACROS_BROKEN
# undef PRIi8
# define PRIi8 "i"
#endif
#if !defined PRIo8
#if !defined PRIo8 || PRI_MACROS_BROKEN
# undef PRIo8
# define PRIo8 "o"
#endif
#if !defined PRIu8
#if !defined PRIu8 || PRI_MACROS_BROKEN
# undef PRIu8
# define PRIu8 "u"
#endif
#if !defined PRIx8
#if !defined PRIx8 || PRI_MACROS_BROKEN
# undef PRIx8
# define PRIx8 "x"
#endif
#if !defined PRIX8
#if !defined PRIX8 || PRI_MACROS_BROKEN
# undef PRIX8
# define PRIX8 "X"
#endif
#if !defined PRId16
#if !defined PRId16 || PRI_MACROS_BROKEN
# undef PRId16
# define PRId16 "d"
#endif
#if !defined PRIi16
#if !defined PRIi16 || PRI_MACROS_BROKEN
# undef PRIi16
# define PRIi16 "i"
#endif
#if !defined PRIo16
#if !defined PRIo16 || PRI_MACROS_BROKEN
# undef PRIo16
# define PRIo16 "o"
#endif
#if !defined PRIu16
#if !defined PRIu16 || PRI_MACROS_BROKEN
# undef PRIu16
# define PRIu16 "u"
#endif
#if !defined PRIx16
#if !defined PRIx16 || PRI_MACROS_BROKEN
# undef PRIx16
# define PRIx16 "x"
#endif
#if !defined PRIX16
#if !defined PRIX16 || PRI_MACROS_BROKEN
# undef PRIX16
# define PRIX16 "X"
#endif
#if !defined PRId32
#if !defined PRId32 || PRI_MACROS_BROKEN
# undef PRId32
# define PRId32 "d"
#endif
#if !defined PRIi32
#if !defined PRIi32 || PRI_MACROS_BROKEN
# undef PRIi32
# define PRIi32 "i"
#endif
#if !defined PRIo32
#if !defined PRIo32 || PRI_MACROS_BROKEN
# undef PRIo32
# define PRIo32 "o"
#endif
#if !defined PRIu32
#if !defined PRIu32 || PRI_MACROS_BROKEN
# undef PRIu32
# define PRIu32 "u"
#endif
#if !defined PRIx32
#if !defined PRIx32 || PRI_MACROS_BROKEN
# undef PRIx32
# define PRIx32 "x"
#endif
#if !defined PRIX32
#if !defined PRIX32 || PRI_MACROS_BROKEN
# undef PRIX32
# define PRIX32 "X"
#endif
#if !defined PRId64
#if !defined PRId64 || PRI_MACROS_BROKEN
# undef PRId64
# define PRId64 (sizeof (long) == 8 ? "ld" : "lld")
#endif
#if !defined PRIi64
#if !defined PRIi64 || PRI_MACROS_BROKEN
# undef PRIi64
# define PRIi64 (sizeof (long) == 8 ? "li" : "lli")
#endif
#if !defined PRIo64
#if !defined PRIo64 || PRI_MACROS_BROKEN
# undef PRIo64
# define PRIo64 (sizeof (long) == 8 ? "lo" : "llo")
#endif
#if !defined PRIu64
#if !defined PRIu64 || PRI_MACROS_BROKEN
# undef PRIu64
# define PRIu64 (sizeof (long) == 8 ? "lu" : "llu")
#endif
#if !defined PRIx64
#if !defined PRIx64 || PRI_MACROS_BROKEN
# undef PRIx64
# define PRIx64 (sizeof (long) == 8 ? "lx" : "llx")
#endif
#if !defined PRIX64
#if !defined PRIX64 || PRI_MACROS_BROKEN
# undef PRIX64
# define PRIX64 (sizeof (long) == 8 ? "lX" : "llX")
#endif
#if !defined PRIdLEAST8
#if !defined PRIdLEAST8 || PRI_MACROS_BROKEN
# undef PRIdLEAST8
# define PRIdLEAST8 "d"
#endif
#if !defined PRIiLEAST8
#if !defined PRIiLEAST8 || PRI_MACROS_BROKEN
# undef PRIiLEAST8
# define PRIiLEAST8 "i"
#endif
#if !defined PRIoLEAST8
#if !defined PRIoLEAST8 || PRI_MACROS_BROKEN
# undef PRIoLEAST8
# define PRIoLEAST8 "o"
#endif
#if !defined PRIuLEAST8
#if !defined PRIuLEAST8 || PRI_MACROS_BROKEN
# undef PRIuLEAST8
# define PRIuLEAST8 "u"
#endif
#if !defined PRIxLEAST8
#if !defined PRIxLEAST8 || PRI_MACROS_BROKEN
# undef PRIxLEAST8
# define PRIxLEAST8 "x"
#endif
#if !defined PRIXLEAST8
#if !defined PRIXLEAST8 || PRI_MACROS_BROKEN
# undef PRIXLEAST8
# define PRIXLEAST8 "X"
#endif
#if !defined PRIdLEAST16
#if !defined PRIdLEAST16 || PRI_MACROS_BROKEN
# undef PRIdLEAST16
# define PRIdLEAST16 "d"
#endif
#if !defined PRIiLEAST16
#if !defined PRIiLEAST16 || PRI_MACROS_BROKEN
# undef PRIiLEAST16
# define PRIiLEAST16 "i"
#endif
#if !defined PRIoLEAST16
#if !defined PRIoLEAST16 || PRI_MACROS_BROKEN
# undef PRIoLEAST16
# define PRIoLEAST16 "o"
#endif
#if !defined PRIuLEAST16
#if !defined PRIuLEAST16 || PRI_MACROS_BROKEN
# undef PRIuLEAST16
# define PRIuLEAST16 "u"
#endif
#if !defined PRIxLEAST16
#if !defined PRIxLEAST16 || PRI_MACROS_BROKEN
# undef PRIxLEAST16
# define PRIxLEAST16 "x"
#endif
#if !defined PRIXLEAST16
#if !defined PRIXLEAST16 || PRI_MACROS_BROKEN
# undef PRIXLEAST16
# define PRIXLEAST16 "X"
#endif
#if !defined PRIdLEAST32
#if !defined PRIdLEAST32 || PRI_MACROS_BROKEN
# undef PRIdLEAST32
# define PRIdLEAST32 "d"
#endif
#if !defined PRIiLEAST32
#if !defined PRIiLEAST32 || PRI_MACROS_BROKEN
# undef PRIiLEAST32
# define PRIiLEAST32 "i"
#endif
#if !defined PRIoLEAST32
#if !defined PRIoLEAST32 || PRI_MACROS_BROKEN
# undef PRIoLEAST32
# define PRIoLEAST32 "o"
#endif
#if !defined PRIuLEAST32
#if !defined PRIuLEAST32 || PRI_MACROS_BROKEN
# undef PRIuLEAST32
# define PRIuLEAST32 "u"
#endif
#if !defined PRIxLEAST32
#if !defined PRIxLEAST32 || PRI_MACROS_BROKEN
# undef PRIxLEAST32
# define PRIxLEAST32 "x"
#endif
#if !defined PRIXLEAST32
#if !defined PRIXLEAST32 || PRI_MACROS_BROKEN
# undef PRIXLEAST32
# define PRIXLEAST32 "X"
#endif
#if !defined PRIdLEAST64
#if !defined PRIdLEAST64 || PRI_MACROS_BROKEN
# undef PRIdLEAST64
# define PRIdLEAST64 PRId64
#endif
#if !defined PRIiLEAST64
#if !defined PRIiLEAST64 || PRI_MACROS_BROKEN
# undef PRIiLEAST64
# define PRIiLEAST64 PRIi64
#endif
#if !defined PRIoLEAST64
#if !defined PRIoLEAST64 || PRI_MACROS_BROKEN
# undef PRIoLEAST64
# define PRIoLEAST64 PRIo64
#endif
#if !defined PRIuLEAST64
#if !defined PRIuLEAST64 || PRI_MACROS_BROKEN
# undef PRIuLEAST64
# define PRIuLEAST64 PRIu64
#endif
#if !defined PRIxLEAST64
#if !defined PRIxLEAST64 || PRI_MACROS_BROKEN
# undef PRIxLEAST64
# define PRIxLEAST64 PRIx64
#endif
#if !defined PRIXLEAST64
#if !defined PRIXLEAST64 || PRI_MACROS_BROKEN
# undef PRIXLEAST64
# define PRIXLEAST64 PRIX64
#endif
#if !defined PRIdFAST8
#if !defined PRIdFAST8 || PRI_MACROS_BROKEN
# undef PRIdFAST8
# define PRIdFAST8 "d"
#endif
#if !defined PRIiFAST8
#if !defined PRIiFAST8 || PRI_MACROS_BROKEN
# undef PRIiFAST8
# define PRIiFAST8 "i"
#endif
#if !defined PRIoFAST8
#if !defined PRIoFAST8 || PRI_MACROS_BROKEN
# undef PRIoFAST8
# define PRIoFAST8 "o"
#endif
#if !defined PRIuFAST8
#if !defined PRIuFAST8 || PRI_MACROS_BROKEN
# undef PRIuFAST8
# define PRIuFAST8 "u"
#endif
#if !defined PRIxFAST8
#if !defined PRIxFAST8 || PRI_MACROS_BROKEN
# undef PRIxFAST8
# define PRIxFAST8 "x"
#endif
#if !defined PRIXFAST8
#if !defined PRIXFAST8 || PRI_MACROS_BROKEN
# undef PRIXFAST8
# define PRIXFAST8 "X"
#endif
#if !defined PRIdFAST16
#if !defined PRIdFAST16 || PRI_MACROS_BROKEN
# undef PRIdFAST16
# define PRIdFAST16 "d"
#endif
#if !defined PRIiFAST16
#if !defined PRIiFAST16 || PRI_MACROS_BROKEN
# undef PRIiFAST16
# define PRIiFAST16 "i"
#endif
#if !defined PRIoFAST16
#if !defined PRIoFAST16 || PRI_MACROS_BROKEN
# undef PRIoFAST16
# define PRIoFAST16 "o"
#endif
#if !defined PRIuFAST16
#if !defined PRIuFAST16 || PRI_MACROS_BROKEN
# undef PRIuFAST16
# define PRIuFAST16 "u"
#endif
#if !defined PRIxFAST16
#if !defined PRIxFAST16 || PRI_MACROS_BROKEN
# undef PRIxFAST16
# define PRIxFAST16 "x"
#endif
#if !defined PRIXFAST16
#if !defined PRIXFAST16 || PRI_MACROS_BROKEN
# undef PRIXFAST16
# define PRIXFAST16 "X"
#endif
#if !defined PRIdFAST32
#if !defined PRIdFAST32 || PRI_MACROS_BROKEN
# undef PRIdFAST32
# define PRIdFAST32 "d"
#endif
#if !defined PRIiFAST32
#if !defined PRIiFAST32 || PRI_MACROS_BROKEN
# undef PRIiFAST32
# define PRIiFAST32 "i"
#endif
#if !defined PRIoFAST32
#if !defined PRIoFAST32 || PRI_MACROS_BROKEN
# undef PRIoFAST32
# define PRIoFAST32 "o"
#endif
#if !defined PRIuFAST32
#if !defined PRIuFAST32 || PRI_MACROS_BROKEN
# undef PRIuFAST32
# define PRIuFAST32 "u"
#endif
#if !defined PRIxFAST32
#if !defined PRIxFAST32 || PRI_MACROS_BROKEN
# undef PRIxFAST32
# define PRIxFAST32 "x"
#endif
#if !defined PRIXFAST32
#if !defined PRIXFAST32 || PRI_MACROS_BROKEN
# undef PRIXFAST32
# define PRIXFAST32 "X"
#endif
#if !defined PRIdFAST64
#if !defined PRIdFAST64 || PRI_MACROS_BROKEN
# undef PRIdFAST64
# define PRIdFAST64 PRId64
#endif
#if !defined PRIiFAST64
#if !defined PRIiFAST64 || PRI_MACROS_BROKEN
# undef PRIiFAST64
# define PRIiFAST64 PRIi64
#endif
#if !defined PRIoFAST64
#if !defined PRIoFAST64 || PRI_MACROS_BROKEN
# undef PRIoFAST64
# define PRIoFAST64 PRIo64
#endif
#if !defined PRIuFAST64
#if !defined PRIuFAST64 || PRI_MACROS_BROKEN
# undef PRIuFAST64
# define PRIuFAST64 PRIu64
#endif
#if !defined PRIxFAST64
#if !defined PRIxFAST64 || PRI_MACROS_BROKEN
# undef PRIxFAST64
# define PRIxFAST64 PRIx64
#endif
#if !defined PRIXFAST64
#if !defined PRIXFAST64 || PRI_MACROS_BROKEN
# undef PRIXFAST64
# define PRIXFAST64 PRIX64
#endif
#if !defined PRIdMAX
#if !defined PRIdMAX || PRI_MACROS_BROKEN
# undef PRIdMAX
# define PRIdMAX (sizeof (uintmax_t) == sizeof (long) ? "ld" : "lld")
#endif
#if !defined PRIiMAX
#if !defined PRIiMAX || PRI_MACROS_BROKEN
# undef PRIiMAX
# define PRIiMAX (sizeof (uintmax_t) == sizeof (long) ? "li" : "lli")
#endif
#if !defined PRIoMAX
#if !defined PRIoMAX || PRI_MACROS_BROKEN
# undef PRIoMAX
# define PRIoMAX (sizeof (uintmax_t) == sizeof (long) ? "lo" : "llo")
#endif
#if !defined PRIuMAX
#if !defined PRIuMAX || PRI_MACROS_BROKEN
# undef PRIuMAX
# define PRIuMAX (sizeof (uintmax_t) == sizeof (long) ? "lu" : "llu")
#endif
#if !defined PRIxMAX
#if !defined PRIxMAX || PRI_MACROS_BROKEN
# undef PRIxMAX
# define PRIxMAX (sizeof (uintmax_t) == sizeof (long) ? "lx" : "llx")
#endif
#if !defined PRIXMAX
#if !defined PRIXMAX || PRI_MACROS_BROKEN
# undef PRIXMAX
# define PRIXMAX (sizeof (uintmax_t) == sizeof (long) ? "lX" : "llX")
#endif
#if !defined PRIdPTR
#if !defined PRIdPTR || PRI_MACROS_BROKEN
# undef PRIdPTR
# define PRIdPTR \
(sizeof (void *) == sizeof (long) ? "ld" : \
sizeof (void *) == sizeof (int) ? "d" : \
"lld")
#endif
#if !defined PRIiPTR
#if !defined PRIiPTR || PRI_MACROS_BROKEN
# undef PRIiPTR
# define PRIiPTR \
(sizeof (void *) == sizeof (long) ? "li" : \
sizeof (void *) == sizeof (int) ? "i" : \
"lli")
#endif
#if !defined PRIoPTR
#if !defined PRIoPTR || PRI_MACROS_BROKEN
# undef PRIoPTR
# define PRIoPTR \
(sizeof (void *) == sizeof (long) ? "lo" : \
sizeof (void *) == sizeof (int) ? "o" : \
"llo")
#endif
#if !defined PRIuPTR
#if !defined PRIuPTR || PRI_MACROS_BROKEN
# undef PRIuPTR
# define PRIuPTR \
(sizeof (void *) == sizeof (long) ? "lu" : \
sizeof (void *) == sizeof (int) ? "u" : \
"llu")
#endif
#if !defined PRIxPTR
#if !defined PRIxPTR || PRI_MACROS_BROKEN
# undef PRIxPTR
# define PRIxPTR \
(sizeof (void *) == sizeof (long) ? "lx" : \
sizeof (void *) == sizeof (int) ? "x" : \
"llx")
#endif
#if !defined PRIXPTR
#if !defined PRIXPTR || PRI_MACROS_BROKEN
# undef PRIXPTR
# define PRIXPTR \
(sizeof (void *) == sizeof (long) ? "lX" : \
@@ -1252,7 +1256,12 @@ _nl_load_domain (struct loaded_l10nfile *domain_file,
#endif
/* Get the header entry and look for a plural specification. */
#ifdef IN_LIBGLOCALE
nullentry =
_nl_find_msg (domain_file, domainbinding, NULL, "", &nullentrylen);
#else
nullentry = _nl_find_msg (domain_file, domainbinding, "", 0, &nullentrylen);
#endif
if (__builtin_expect (nullentry == (char *) -1, 0))
{
#ifdef _LIBC
+10 -5
View File
@@ -294,7 +294,14 @@ read_alias_file (const char *fname, int fname_len)
else if (cp[0] != '\0')
*cp++ = '\0';
{
#ifdef IN_LIBGLOCALE
/* glibc's locale.alias contains entries for ja_JP and ko_KR
that make it impossible to use a Japanese or Korean UTF-8
locale under the name "ja_JP" or "ko_KR". Ignore these
entries. */
if (strchr (alias, '_') == NULL)
#endif
{
size_t alias_len;
size_t value_len;
@@ -329,10 +336,8 @@ read_alias_file (const char *fname, int fname_len)
for (i = 0; i < nmap; i++)
{
map[i].alias =
new_pool + (map[i].alias - string_space);
map[i].value =
new_pool + (map[i].value - string_space);
map[i].alias += new_pool - string_space;
map[i].value += new_pool - string_space;
}
}
-54
View File
@@ -1,54 +0,0 @@
# plural-depth.awk - Generate .po file with deeply nested plural expression.
# 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/>.
# Generate a .po file whose Plural-Forms header contains a plural
# expression nested DEPTH levels deep. Each level wraps as !(1-(...)),
# producing an expression that is accepted by the parser (YYMAXDEPTH=10000)
# but exceeds EVAL_MAXDEPTH=100 at runtime.
#
# Usage: awk -v DEPTH=5000 -f plural-depth.awk > plural-depth.po
BEGIN {
if (DEPTH == 0)
DEPTH = 5000
expr = ""
for (i = 0; i < DEPTH; i++)
expr = expr "!(1-"
expr = expr "(n!=1)"
for (i = 0; i < DEPTH; i++)
expr = expr ")"
print "msgid \"\""
print "msgstr \"\""
print "\"Project-Id-Version: test\\n\""
print "\"PO-Revision-Date: 2026-01-01 00:00+0000\\n\""
print "\"Last-Translator: \\n\""
print "\"Language-Team: \\n\""
print "\"Language: ll\\n\""
print "\"MIME-Version: 1.0\\n\""
print "\"Content-Type: text/plain; charset=ASCII\\n\""
print "\"Content-Transfer-Encoding: 8bit\\n\""
print "\"Plural-Forms: nplurals=2; plural=" expr ";\\n\""
print ""
print "msgid \"X\""
print "msgid_plural \"Y\""
print "msgstr[0] \"x\""
print "msgstr[1] \"y\""
}
+44
View File
@@ -25,6 +25,10 @@
#include <plural-exp.h>
#if (defined __GNUC__ && !(defined __APPLE_CC_ && __APPLE_CC__ > 1) && \
!defined __cplusplus) \
|| (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
/* These structs are the constant expression for the germanic plural
form determination. It represents the expression "n != 1". */
static const struct expression plvar =
@@ -55,6 +59,40 @@ const struct expression GERMANIC_PLURAL =
}
};
# define INIT_GERMANIC_PLURAL()
#else
/* For compilers without support for ISO C 99 struct/union initializers:
Initialization at run-time. */
static struct expression plvar;
static struct expression plone;
struct expression GERMANIC_PLURAL;
static void
init_germanic_plural (void)
{
if (plone.val.num == 0)
{
plvar.nargs = 0;
plvar.operation = var;
plone.nargs = 0;
plone.operation = num;
plone.val.num = 1;
GERMANIC_PLURAL.nargs = 2;
GERMANIC_PLURAL.operation = not_equal;
GERMANIC_PLURAL.val.args[0] = &plvar;
GERMANIC_PLURAL.val.args[1] = &plone;
}
}
# define INIT_GERMANIC_PLURAL() init_germanic_plural ()
#endif
void
EXTRACT_PLURAL_EXPRESSION (const char *nullentry,
const struct expression **pluralp,
@@ -81,7 +119,12 @@ EXTRACT_PLURAL_EXPRESSION (const char *nullentry,
++nplurals;
if (!(*nplurals >= '0' && *nplurals <= '9'))
goto no_plural;
#if defined HAVE_STRTOUL || defined _LIBC
n = strtoul (nplurals, &endp, 10);
#else
for (endp = nplurals, n = 0; *endp >= '0' && *endp <= '9'; endp++)
n = n * 10 + (*endp - '0');
#endif
if (nplurals == endp)
goto no_plural;
*npluralsp = n;
@@ -103,6 +146,7 @@ EXTRACT_PLURAL_EXPRESSION (const char *nullentry,
for `one', the plural form otherwise. Yes, this is also what
English is using since English is a Germanic language. */
no_plural:
INIT_GERMANIC_PLURAL ();
*pluralp = &GERMANIC_PLURAL;
*npluralsp = 2;
}

Some files were not shown because too many files have changed in this diff Show More