mirror of
git://sourceware.org/git/glibc.git
synced 2026-09-09 23:58:37 +08:00
Compare commits
178
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e51a6bf18e | ||
|
|
b50e24aea3 | ||
|
|
c0a1c6e06d | ||
|
|
ccd4cd5324 | ||
|
|
96773cc77b | ||
|
|
21103aef12 | ||
|
|
ead80441e6 | ||
|
|
657fe73fb1 | ||
|
|
c9b5a4a593 | ||
|
|
a014af39fd | ||
|
|
16c74b94b9 | ||
|
|
8e5c299980 | ||
|
|
d00085d6d0 | ||
|
|
98e1e467a8 | ||
|
|
2cce7ec3c8 | ||
|
|
c5a4d7ccb2 | ||
|
|
5a60281b88 | ||
|
|
f9deb2f50b | ||
|
|
b61b98e3d0 | ||
|
|
91eb5057b9 | ||
|
|
bc76aa1658 | ||
|
|
07c24f3539 | ||
|
|
60f7247847 | ||
|
|
53d909a283 | ||
|
|
9310db5a08 | ||
|
|
da11a7d92c | ||
|
|
4ba0b79b95 | ||
|
|
a7b60d23bb | ||
|
|
ca44a6609c | ||
|
|
cd0db208d5 | ||
|
|
d58415eb17 | ||
|
|
f69b7f95e3 | ||
|
|
360f352c9a | ||
|
|
1607beb5f4 | ||
|
|
cdbefe2f94 | ||
|
|
4e105d9855 | ||
|
|
708185c829 | ||
|
|
e585d88982 | ||
|
|
faa9ea921a | ||
|
|
a3b1faddaf | ||
|
|
242a1f4b0f | ||
|
|
368438d8b6 | ||
|
|
89b4605be8 | ||
|
|
145b53b22a | ||
|
|
8d184c42cc | ||
|
|
337e18d661 | ||
|
|
604fe0b87b | ||
|
|
78f1f0e39c | ||
|
|
c626d764d7 | ||
|
|
afc90416e9 | ||
|
|
d50ae543a4 | ||
|
|
a5ec880f80 | ||
|
|
3785d4c0d0 | ||
|
|
a767fbcff0 | ||
|
|
82b710c190 | ||
|
|
2169401438 | ||
|
|
1111fbdd3e | ||
|
|
6adb7400fd | ||
|
|
1cba6073e5 | ||
|
|
9ed3576e61 | ||
|
|
df83fa8813 | ||
|
|
8ae37adb21 | ||
|
|
d8fa402f5e | ||
|
|
690472e2a5 | ||
|
|
9cf4dd03bd | ||
|
|
6f072238e9 | ||
|
|
e9325bd7d0 | ||
|
|
88099e289d | ||
|
|
4e22f95057 | ||
|
|
d0cc9bf859 | ||
|
|
2b6f6710a1 | ||
|
|
f9a11a380d | ||
|
|
49244da110 | ||
|
|
c275c424b3 | ||
|
|
bbc3867f8d | ||
|
|
512f99e71d | ||
|
|
496ccfe312 | ||
|
|
2efc702628 | ||
|
|
93e6135904 | ||
|
|
13dc1040af | ||
|
|
20fffa2c39 | ||
|
|
b7e256c32c | ||
|
|
4a7805d05c | ||
|
|
0b27186151 | ||
|
|
e951752cd0 | ||
|
|
5879990337 | ||
|
|
7193069543 | ||
|
|
c5278a6428 | ||
|
|
0458560b5d | ||
|
|
7d213541cb | ||
|
|
70be9819b5 | ||
|
|
7052455f0e | ||
|
|
41e9457c53 | ||
|
|
808035fdff | ||
|
|
b9e893b60e | ||
|
|
bec480cbe6 | ||
|
|
9ef37798fa | ||
|
|
1e7935ae87 | ||
|
|
b20b94006c | ||
|
|
eceaece54b | ||
|
|
b26cc4fbb7 | ||
|
|
c289868492 | ||
|
|
37318986e7 | ||
|
|
b507b6d4c6 | ||
|
|
7073112263 | ||
|
|
ec98b25af5 | ||
|
|
59a0de89ba | ||
|
|
c1b589b21c | ||
|
|
f531cab589 | ||
|
|
156141a8f3 | ||
|
|
58ae7f8f0c | ||
|
|
8560164813 | ||
|
|
8eeed69baf | ||
|
|
c056561d31 | ||
|
|
b0f985a21e | ||
|
|
b75ad99d45 | ||
|
|
af34b1376a | ||
|
|
e563a57105 | ||
|
|
746a560a62 | ||
|
|
a7b6e53462 | ||
|
|
8ed4c70915 | ||
|
|
f7eea1b258 | ||
|
|
54abc8566f | ||
|
|
97bd3b3d56 | ||
|
|
b1ab27286e | ||
|
|
213ffdfbba | ||
|
|
994a437878 | ||
|
|
566135bb77 | ||
|
|
8aa4a70431 | ||
|
|
209ad6ff9d | ||
|
|
999fa30b36 | ||
|
|
3215ea5c45 | ||
|
|
3e3b621910 | ||
|
|
26ed1cb8ca | ||
|
|
f86b51dd6a | ||
|
|
4e3489bd2a | ||
|
|
1233e256f2 | ||
|
|
aadbf5a10a | ||
|
|
aa56ccb98b | ||
|
|
66efdda2f8 | ||
|
|
dee4e2446f | ||
|
|
3237d63b84 | ||
|
|
d420b69b81 | ||
|
|
4c6f92daea | ||
|
|
08e963f675 | ||
|
|
64efa1451b | ||
|
|
63b31c05a8 | ||
|
|
17a79a5120 | ||
|
|
bb91c88af7 | ||
|
|
5ce8201bf5 | ||
|
|
9985b162d8 | ||
|
|
6f837cdedd | ||
|
|
d093102297 | ||
|
|
d9981b5439 | ||
|
|
1172d0e9cf | ||
|
|
3bd334037e | ||
|
|
785a028ab7 | ||
|
|
389ecf52bc | ||
|
|
1cc165baed | ||
|
|
0981c03c27 | ||
|
|
18ff9235eb | ||
|
|
fdd1684c85 | ||
|
|
ba305d8268 | ||
|
|
e7f5359db7 | ||
|
|
c2d6afb4a0 | ||
|
|
b28ffb926f | ||
|
|
c20e60e350 | ||
|
|
9b8ff36d0c | ||
|
|
d62f110fd1 | ||
|
|
37ee8868dc | ||
|
|
8398987772 | ||
|
|
1a70b352c3 | ||
|
|
63545c6d2c | ||
|
|
f7623844a7 | ||
|
|
6b9135f75b | ||
|
|
ef3bfb5f91 | ||
|
|
79dbb41f15 | ||
|
|
331c7a4cd0 |
@@ -154,30 +154,10 @@ passed to 'configure'. For example:
|
||||
|
||||
NOTE: '--enable-cet' is only supported on x86_64 and x32.
|
||||
|
||||
'--enable-memory-tagging'
|
||||
Enable memory tagging support if the architecture supports it.
|
||||
When the GNU C Library is built with this option then the resulting
|
||||
library will be able to control the use of tagged memory when
|
||||
hardware support is present by use of the tunable
|
||||
'glibc.mem.tagging'. This includes the generation of tagged memory
|
||||
when using the 'malloc' APIs.
|
||||
|
||||
At present only AArch64 platforms with MTE provide this
|
||||
functionality, although the library will still operate (without
|
||||
memory tagging) on older versions of the architecture.
|
||||
|
||||
The default is to disable support for memory tagging.
|
||||
|
||||
'--disable-profile'
|
||||
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
|
||||
|
||||
@@ -9,6 +9,15 @@ 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.
|
||||
@@ -18,6 +27,16 @@ 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
|
||||
@@ -29,6 +48,15 @@ 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]
|
||||
|
||||
@@ -288,7 +288,8 @@ $(addprefix $(objpfx),$(binaries-malloc-check-tests)): %-malloc-check: %.o \
|
||||
endif
|
||||
|
||||
ifneq "$(strip $(binaries-malloc-hugetlb1-tests))" ""
|
||||
$(addprefix $(objpfx),$(binaries-malloc-hugetlb1-tests)): %-malloc-hugetlb1: %.o \
|
||||
$(addprefix $(objpfx),$(binaries-malloc-hugetlb1-tests)): \
|
||||
%-malloc-hugetlb1: %-malloc-hugetlb1.o \
|
||||
$(link-extra-libs-tests) \
|
||||
$(sort $(filter $(common-objpfx)lib%,$(link-libc))) \
|
||||
$(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
|
||||
@@ -296,7 +297,8 @@ $(addprefix $(objpfx),$(binaries-malloc-hugetlb1-tests)): %-malloc-hugetlb1: %.o
|
||||
endif
|
||||
|
||||
ifneq "$(strip $(binaries-malloc-hugetlb2-tests))" ""
|
||||
$(addprefix $(objpfx),$(binaries-malloc-hugetlb2-tests)): %-malloc-hugetlb2: %.o \
|
||||
$(addprefix $(objpfx),$(binaries-malloc-hugetlb2-tests)): \
|
||||
%-malloc-hugetlb2: %-malloc-hugetlb2.o \
|
||||
$(link-extra-libs-tests) \
|
||||
$(sort $(filter $(common-objpfx)lib%,$(link-libc))) \
|
||||
$(addprefix $(csu-objpfx),start.o) $(+preinit) $(+postinit)
|
||||
|
||||
+529
-7
@@ -113,6 +113,7 @@ 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
|
||||
@@ -151,9 +152,7 @@ gnulib:
|
||||
time/strptime.c
|
||||
time/timegm.c
|
||||
|
||||
# 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.
|
||||
# Synced with GNU gettext 1.0 (2026-05-18), through gettext commit 2ebbdd0e2.
|
||||
#
|
||||
# This commit was omitted from the merge as it does not appear to be compatible
|
||||
# with how glibc expects things to work:
|
||||
@@ -170,6 +169,7 @@ gettext:
|
||||
intl/dcngettext.c
|
||||
intl/dgettext.c
|
||||
intl/dngettext.c
|
||||
intl/eval-plural.h
|
||||
intl/explodename.c
|
||||
intl/finddomain.c
|
||||
intl/gettext.c
|
||||
@@ -211,8 +211,9 @@ 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 2024b, announced and distributed here:
|
||||
# https://github.com/eggert/tz/releases/tag/2024b
|
||||
# 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
|
||||
tzcode:
|
||||
timezone/private.h
|
||||
timezone/tzfile.h
|
||||
@@ -257,11 +258,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 1bd85b89
|
||||
# src/binary64/acosh/acosh.c, revision 887cab6f
|
||||
sysdeps/ieee754/dbl-64/e_acosh.c
|
||||
# src/binary64/atanh/atanh.c, revision 532e37dc
|
||||
sysdeps/ieee754/dbl-64/e_atanh.c
|
||||
@@ -331,3 +332,524 @@ 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
|
||||
|
||||
@@ -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.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.
|
||||
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.
|
||||
|
||||
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: b43b13ac2544b11f35be301d1589b51a8473e32b (2.2)
|
||||
Vulnerable-Commit: ee188d555b8c32ad9704a7440cab400af967292f (1.90)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
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
-6
@@ -32,9 +32,6 @@
|
||||
/* 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. */
|
||||
@@ -170,6 +167,9 @@
|
||||
/* 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
|
||||
|
||||
@@ -182,9 +182,6 @@
|
||||
/* Define if inlined system calls are available. */
|
||||
#undef HAVE_INLINED_SYSCALLS
|
||||
|
||||
/* Define if memory tagging support should be enabled. */
|
||||
#undef USE_MTAG
|
||||
|
||||
/* Package description. */
|
||||
#undef PKGVERSION
|
||||
|
||||
|
||||
@@ -78,12 +78,9 @@ multi-arch = @multi_arch@
|
||||
|
||||
mach-interface-list = @mach_interface_list@
|
||||
|
||||
memory-tagging = @memory_tagging@
|
||||
|
||||
# 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@
|
||||
|
||||
@@ -616,7 +616,6 @@ RELEASE
|
||||
VERSION
|
||||
mach_interface_list
|
||||
DEFINES
|
||||
static_nss
|
||||
profile
|
||||
libc_cv_multidir
|
||||
test_enable_cet
|
||||
@@ -703,7 +702,6 @@ INSTALL_PROGRAM
|
||||
base_machine
|
||||
build_pt_chown
|
||||
build_nscd
|
||||
memory_tagging
|
||||
enable_werror
|
||||
force_install
|
||||
bindnow
|
||||
@@ -805,13 +803,11 @@ 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
|
||||
enable_werror
|
||||
enable_multi_arch
|
||||
enable_memory_tagging
|
||||
enable_systemtap
|
||||
enable_build_nscd
|
||||
enable_nscd
|
||||
@@ -1479,7 +1475,6 @@ 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
|
||||
@@ -1490,8 +1485,6 @@ Optional Features:
|
||||
--disable-werror do not build with -Werror
|
||||
--enable-multi-arch enable single DSO with optimizations for multiple
|
||||
architectures
|
||||
--enable-memory-tagging enable memory tagging if supported by the
|
||||
architecture [default=no]
|
||||
--enable-systemtap enable systemtap static probe points [default=no]
|
||||
--disable-build-nscd disable building and installing the nscd daemon
|
||||
--disable-nscd library functions will not contact the nscd daemon
|
||||
@@ -4618,21 +4611,6 @@ 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 :
|
||||
@@ -4690,26 +4668,6 @@ esac
|
||||
fi
|
||||
|
||||
|
||||
# Check whether --enable-memory-tagging was given.
|
||||
if test ${enable_memory_tagging+y}
|
||||
then :
|
||||
enableval=$enable_memory_tagging; memory_tagging=$enableval
|
||||
else case e in #(
|
||||
e) memory_tagging=no ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test "$memory_tagging" = yes; then
|
||||
# Only enable this on architectures that support it.
|
||||
case $host_cpu in
|
||||
aarch64)
|
||||
printf "%s\n" "#define USE_MTAG 1" >>confdefs.h
|
||||
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
|
||||
# Check whether --enable-systemtap was given.
|
||||
if test ${enable_systemtap+y}
|
||||
then :
|
||||
@@ -7437,47 +7395,6 @@ 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}
|
||||
@@ -9660,7 +9577,6 @@ 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`
|
||||
|
||||
|
||||
@@ -272,19 +272,6 @@ 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]),
|
||||
@@ -328,21 +315,6 @@ AC_ARG_ENABLE([multi-arch],
|
||||
[multi_arch=$enableval],
|
||||
[multi_arch=default])
|
||||
|
||||
AC_ARG_ENABLE([memory-tagging],
|
||||
AS_HELP_STRING([--enable-memory-tagging],
|
||||
[enable memory tagging if supported by the architecture @<:@default=no@:>@]),
|
||||
[memory_tagging=$enableval],
|
||||
[memory_tagging=no])
|
||||
if test "$memory_tagging" = yes; then
|
||||
# Only enable this on architectures that support it.
|
||||
case $host_cpu in
|
||||
aarch64)
|
||||
AC_DEFINE(USE_MTAG)
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
AC_SUBST(memory_tagging)
|
||||
|
||||
AC_ARG_ENABLE([systemtap],
|
||||
[AS_HELP_STRING([--enable-systemtap],
|
||||
[enable systemtap static probe points @<:@default=no@:>@])],
|
||||
@@ -1371,22 +1343,6 @@ 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
|
||||
@@ -2231,7 +2187,6 @@ 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)
|
||||
|
||||
|
||||
+37
-25
@@ -44,21 +44,20 @@ 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)
|
||||
@@ -205,6 +204,7 @@ 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,23 +268,22 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
|
||||
|
||||
ARCH_INIT_CPU_FEATURES ();
|
||||
|
||||
/* Do static pie self relocation after tunables and cpu features
|
||||
are setup for ifunc resolvers. Before this point relocations
|
||||
must be avoided. */
|
||||
/* 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. */
|
||||
_dl_relocate_static_pie ();
|
||||
|
||||
/* Perform IREL{,A} relocations. */
|
||||
ARCH_SETUP_IREL ();
|
||||
|
||||
/* The stack guard goes into the TCB, so initialize it early. */
|
||||
/* 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. */
|
||||
ARCH_SETUP_TLS ();
|
||||
|
||||
/* 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. */
|
||||
/* 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. */
|
||||
uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard (_dl_random);
|
||||
# ifdef THREAD_SET_STACK_GUARD
|
||||
THREAD_SET_STACK_GUARD (stack_chk_guard);
|
||||
@@ -292,18 +291,31 @@ LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
|
||||
__stack_chk_guard = stack_chk_guard;
|
||||
# endif
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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 ();
|
||||
|
||||
#endif /* !SHARED */
|
||||
|
||||
|
||||
@@ -23,4 +23,9 @@ void
|
||||
_dl_relocate_static_pie (void)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
_dl_relocate_static_pie_ifunc (void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -67,6 +67,7 @@ tests = \
|
||||
glrefmain \
|
||||
tst-dladdr \
|
||||
tst-dlinfo \
|
||||
tst-dlsym-ctor \
|
||||
tst-rec-dlopen \
|
||||
tstatexit \
|
||||
tstcxaatexit \
|
||||
@@ -90,6 +91,7 @@ modules-names = \
|
||||
modcxaatexit \
|
||||
moddummy1 \
|
||||
moddummy2 \
|
||||
tst-dlsym-ctormod \
|
||||
# modules-names
|
||||
|
||||
failtestmod.so-no-z-defs = yes
|
||||
@@ -197,3 +199,5 @@ $(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
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,28 @@
|
||||
/* 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");
|
||||
}
|
||||
+153
-2
@@ -78,6 +78,7 @@ dl-routines = \
|
||||
dl-reloc \
|
||||
dl-runtime \
|
||||
dl-scope \
|
||||
dl-scratch-buffer \
|
||||
dl-setup_hash \
|
||||
dl-sort-maps \
|
||||
dl-thread_gscope_wait \
|
||||
@@ -269,6 +270,7 @@ 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 \
|
||||
@@ -277,6 +279,7 @@ 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 \
|
||||
@@ -288,10 +291,20 @@ 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
|
||||
@@ -404,9 +417,13 @@ 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 \
|
||||
@@ -429,6 +446,7 @@ tests += \
|
||||
tst-dlopenfail-2 \
|
||||
tst-dlopenrpath \
|
||||
tst-dlsym-error \
|
||||
tst-dst-needed-minstack \
|
||||
tst-filterobj \
|
||||
tst-filterobj-dlopen \
|
||||
tst-glibc-hwcaps \
|
||||
@@ -531,6 +549,7 @@ tests-internal += \
|
||||
neededtest2 \
|
||||
neededtest3 \
|
||||
neededtest4 \
|
||||
tst-atrandom-scrub \
|
||||
tst-audit19a \
|
||||
tst-create_format1 \
|
||||
tst-dl-hwcaps_split \
|
||||
@@ -560,6 +579,7 @@ tests-container += \
|
||||
tst-dlopen-tlsmodid-container \
|
||||
tst-pldd \
|
||||
tst-preload-pthread-libc \
|
||||
tst-ptrguard-static-dlopen \
|
||||
tst-rootdir \
|
||||
# tests-container
|
||||
|
||||
@@ -700,6 +720,7 @@ 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
|
||||
@@ -736,6 +757,19 @@ 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 \
|
||||
@@ -909,6 +943,7 @@ 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 \
|
||||
@@ -939,6 +974,8 @@ 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 \
|
||||
@@ -980,6 +1017,7 @@ 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 \
|
||||
@@ -1223,6 +1261,7 @@ modules-names-nobuild += \
|
||||
tst-audit24bmod1 \
|
||||
tst-audit24bmod2 \
|
||||
tst-big-note-lib \
|
||||
tst-bz26577-mod \
|
||||
tst-nodeps1-mod \
|
||||
tst-nodeps2-mod \
|
||||
tst-ro-dynamic-mod \
|
||||
@@ -1244,7 +1283,11 @@ tests-ifuncstatic := \
|
||||
ifuncmain7static \
|
||||
# tests-ifuncstatic
|
||||
ifeq (yes,$(have-gcc-ifunc))
|
||||
tests-ifuncstatic += ifuncmain9static ifuncmain9picstatic
|
||||
tests-ifuncstatic += \
|
||||
ifuncmain9picstatic \
|
||||
ifuncmain9static \
|
||||
tst-ifunc-bz28817 \
|
||||
# tests-ifuncstatic
|
||||
endif
|
||||
tests-static += $(tests-ifuncstatic)
|
||||
tests-internal += $(tests-ifuncstatic)
|
||||
@@ -1252,6 +1295,15 @@ 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 += \
|
||||
@@ -1314,7 +1366,23 @@ 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
|
||||
@@ -1721,6 +1789,19 @@ $(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
|
||||
@@ -1793,6 +1874,7 @@ 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
|
||||
@@ -2267,6 +2349,16 @@ 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
|
||||
@@ -2330,6 +2422,9 @@ 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)
|
||||
@@ -2426,6 +2521,39 @@ $(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
|
||||
|
||||
@@ -2714,7 +2842,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 maxmimize this list.
|
||||
# complete list of libraries we build but we try to maximize this list.
|
||||
$(objpfx)tst-linkall-static: \
|
||||
$(common-objpfx)math/libm.a \
|
||||
$(common-objpfx)resolv/libresolv.a \
|
||||
@@ -2758,6 +2886,16 @@ $(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
|
||||
@@ -2839,6 +2977,16 @@ 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
|
||||
@@ -3061,6 +3209,9 @@ $(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
|
||||
|
||||
+9
-5
@@ -21,6 +21,7 @@
|
||||
#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>
|
||||
@@ -490,12 +491,15 @@ _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. */
|
||||
char *temp;
|
||||
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 ();
|
||||
size_t best_len = strlen (best) + 1;
|
||||
temp = alloca (best_len);
|
||||
memcpy (temp, best, best_len);
|
||||
return __strdup (temp);
|
||||
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;
|
||||
}
|
||||
|
||||
#ifndef MAP_COPY
|
||||
|
||||
+1
-1
@@ -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 specitic TLS state. */
|
||||
/* Protects global and module specific TLS state. */
|
||||
__rtld_lock_lock_recursive (GL(dl_load_tls_lock));
|
||||
|
||||
/* We modify the list of loaded objects. */
|
||||
|
||||
+69
-47
@@ -30,6 +30,7 @@
|
||||
#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. */
|
||||
@@ -80,47 +81,35 @@ struct list
|
||||
};
|
||||
|
||||
|
||||
/* 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; })
|
||||
/* 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;
|
||||
}
|
||||
|
||||
static void
|
||||
preload (struct list *known, unsigned int *nlist, struct link_map *map)
|
||||
@@ -224,12 +213,26 @@ _dl_map_object_deps (struct link_map *map,
|
||||
/* Map in the needed object. */
|
||||
struct link_map *dep;
|
||||
|
||||
/* Recognize DSTs. */
|
||||
name = expand_dst (l, strtab + d->d_un.d_val, 0);
|
||||
/* 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;
|
||||
}
|
||||
/* 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)
|
||||
@@ -267,9 +270,25 @@ _dl_map_object_deps (struct link_map *map,
|
||||
{
|
||||
struct list *newp;
|
||||
|
||||
/* Recognize DSTs. */
|
||||
name = expand_dst (l, strtab + d->d_un.d_val,
|
||||
d->d_tag == DT_AUXILIARY);
|
||||
/* 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;
|
||||
}
|
||||
/* Store the tag in the argument structure. */
|
||||
args.name = name;
|
||||
|
||||
@@ -285,6 +304,9 @@ _dl_map_object_deps (struct link_map *map,
|
||||
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)
|
||||
@@ -535,7 +557,7 @@ _dl_map_object_deps (struct link_map *map,
|
||||
/* Copy the binary into position 0. */
|
||||
l_initfini[0] = map->l_searchlist.r_list[map_index];
|
||||
|
||||
/* Copy the filtees. */
|
||||
/* Copy the filters. */
|
||||
for (i = 0; i < map_index; ++i)
|
||||
l_initfini[i+1] = map->l_searchlist.r_list[i];
|
||||
|
||||
|
||||
@@ -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 noncontigous link
|
||||
/* Subroutine of _dlfo_process_initial to split out noncontiguous 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
|
||||
coverying any valid PC. */
|
||||
covering any valid PC. */
|
||||
_dlfo_main.map_start = -1;
|
||||
_dlfo_main.map_end = -1;
|
||||
}
|
||||
|
||||
+277
-234
@@ -33,31 +33,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <gnu/lib-names.h>
|
||||
#include <dl-tunables.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 <dl-scratch-buffer.h>
|
||||
|
||||
#include "dynamic-link.h"
|
||||
#include "get-dynamic-info.h"
|
||||
@@ -121,7 +97,9 @@ is_trusted_path_normalize (const char *path, size_t len)
|
||||
if (len == 0)
|
||||
return false;
|
||||
|
||||
char *npath = (char *) alloca (len + 2);
|
||||
struct dl_scratch_buffer scratch = dl_scratch_buffer_init ();
|
||||
dl_scratch_buffer_allocate (&scratch, len + 2, 0);
|
||||
char *npath = scratch.data;
|
||||
char *wnp = npath;
|
||||
while (*path != '\0')
|
||||
{
|
||||
@@ -156,19 +134,24 @@ 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. */
|
||||
return true;
|
||||
{
|
||||
/* Found it. */
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
trun += system_dirs_len[idx] + 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
dl_scratch_buffer_free (&scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Given a substring starting at INPUT, just after the DST '$' start
|
||||
@@ -439,7 +422,10 @@ struct r_search_path_struct __rtld_search_dirs attribute_relro;
|
||||
|
||||
static size_t max_dirnamelen;
|
||||
|
||||
static struct r_search_path_elem **
|
||||
/* 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
|
||||
fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
|
||||
const char *what, const char *where, struct link_map *l)
|
||||
{
|
||||
@@ -507,8 +493,10 @@ 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)
|
||||
_dl_signal_error (ENOMEM, NULL, NULL,
|
||||
N_("cannot create cache for search path"));
|
||||
{
|
||||
free (to_free);
|
||||
return false;
|
||||
}
|
||||
|
||||
dirp->dirname = ((char *) dirp + sizeof (*dirp)
|
||||
+ ncapstr * sizeof (enum r_dir_status));
|
||||
@@ -545,7 +533,7 @@ fillin_rpath (char *rpath, struct r_search_path_elem **result, const char *sep,
|
||||
/* Terminate the array. */
|
||||
result[nelems] = NULL;
|
||||
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -626,7 +614,13 @@ decompose_rpath (struct r_search_path_struct *sps,
|
||||
_dl_signal_error (ENOMEM, NULL, NULL, errstring);
|
||||
}
|
||||
|
||||
fillin_rpath (copy, result, ":", what, where, l);
|
||||
if (!fillin_rpath (copy, result, ":", what, where, l))
|
||||
{
|
||||
free (copy);
|
||||
free (result);
|
||||
errstring = N_("cannot create cache for search path");
|
||||
goto signal_error;
|
||||
}
|
||||
|
||||
/* Free the copied RPATH string. `fillin_rpath' make own copies if
|
||||
necessary. */
|
||||
@@ -799,12 +793,13 @@ _dl_init_paths (const char *llp, const char *source,
|
||||
|
||||
if (llp != NULL && *llp != '\0')
|
||||
{
|
||||
char *llp_tmp = strdupa (llp);
|
||||
|
||||
/* Decompose the LD_LIBRARY_PATH contents. First determine how many
|
||||
elements it has. */
|
||||
/* 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. */
|
||||
size_t nllp = 1;
|
||||
for (const char *cp = llp_tmp; *cp != '\0'; ++cp)
|
||||
for (const char *cp = llp; *cp != '\0'; ++cp)
|
||||
if (*cp == ':' || *cp == ';')
|
||||
++nllp;
|
||||
|
||||
@@ -816,8 +811,25 @@ _dl_init_paths (const char *llp, const char *source,
|
||||
goto signal_error;
|
||||
}
|
||||
|
||||
(void) fillin_rpath (llp_tmp, __rtld_env_path_list.dirs, ":;",
|
||||
source, NULL, l);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
if (__rtld_env_path_list.dirs[0] == NULL)
|
||||
{
|
||||
@@ -935,6 +947,167 @@ _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. */
|
||||
|
||||
@@ -948,8 +1121,7 @@ _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;
|
||||
const ElfW(Ehdr) *header;
|
||||
const ElfW(Phdr) *phdr;
|
||||
ElfW(Ehdr) header;
|
||||
const ElfW(Phdr) *ph;
|
||||
size_t maplength;
|
||||
int type;
|
||||
@@ -1059,8 +1231,10 @@ _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);
|
||||
|
||||
/* This is the ELF header. We read it in `open_verify'. */
|
||||
header = (void *) fbp->buf;
|
||||
/* 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);
|
||||
|
||||
/* Enter the new object in the list of loaded objects. */
|
||||
l = _dl_new_object (realname, name, l_type, loader, mode, nsid);
|
||||
@@ -1072,26 +1246,9 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
errstring = N_("cannot create shared object descriptor");
|
||||
goto lose_errno;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
l->l_entry = header.e_entry;
|
||||
type = header.e_type;
|
||||
l->l_phnum = header.e_phnum;
|
||||
|
||||
/* 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
|
||||
@@ -1099,140 +1256,29 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
unsigned int stack_flags = DEFAULT_STACK_PROT_PERMS;
|
||||
|
||||
{
|
||||
/* Scan the program header table, collecting its load commands. */
|
||||
struct loadcmd loadcmds[l->l_phnum];
|
||||
size_t nloadcmds = 0;
|
||||
bool has_holes = false;
|
||||
/* 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;
|
||||
bool empty_dynamic = false;
|
||||
ElfW(Addr) p_align_max = 0;
|
||||
|
||||
/* 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;
|
||||
_dl_pt_load_iterator_init (&it, fd, fbp, header.e_phoff, l->l_phnum);
|
||||
has_holes = false;
|
||||
|
||||
case PT_PHDR:
|
||||
l->l_phdr = (void *) ph->p_vaddr;
|
||||
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_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))
|
||||
if (__glibc_unlikely (it.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
|
||||
@@ -1254,13 +1300,13 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
}
|
||||
|
||||
/* Length of the sections to be loaded. */
|
||||
maplength = loadcmds[nloadcmds - 1].allocend - loadcmds[0].mapstart;
|
||||
maplength = it.last_allocend - it.first_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, loadcmds, nloadcmds,
|
||||
errstring = _dl_map_segments (l, fd, &header, type, &it,
|
||||
maplength, has_holes, loader);
|
||||
if (__glibc_unlikely (errstring != NULL))
|
||||
{
|
||||
@@ -1293,18 +1339,22 @@ _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.
|
||||
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)));
|
||||
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);
|
||||
if (newp == NULL)
|
||||
{
|
||||
errstring = N_("cannot allocate memory for program header");
|
||||
goto lose_errno;
|
||||
}
|
||||
|
||||
l->l_phdr = memcpy (newp, phdr,
|
||||
(header->e_phnum * sizeof (ElfW(Phdr))));
|
||||
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_allocated = 1;
|
||||
}
|
||||
else
|
||||
@@ -1457,12 +1507,17 @@ cannot enable executable stack as shared object requires");
|
||||
return l;
|
||||
}
|
||||
|
||||
/* Print search path. */
|
||||
/* 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. */
|
||||
static void
|
||||
print_search_path (struct r_search_path_elem **list,
|
||||
const char *what, const char *name)
|
||||
const char *what, const char *name, char *buf)
|
||||
{
|
||||
char buf[max_dirnamelen + max_capstrlen];
|
||||
int first = 1;
|
||||
|
||||
_dl_debug_printf (" search path=");
|
||||
@@ -1564,8 +1619,6 @@ 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. */
|
||||
@@ -1691,32 +1744,6 @@ 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
|
||||
{
|
||||
@@ -1740,7 +1767,6 @@ 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;
|
||||
@@ -1750,7 +1776,18 @@ open_path (const char *name, size_t namelen, int mode,
|
||||
given on the command line when rtld is run directly. */
|
||||
return -1;
|
||||
|
||||
buf = alloca (max_dirnamelen + max_capstrlen + namelen);
|
||||
/* 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;
|
||||
do
|
||||
{
|
||||
struct r_search_path_elem *this_dir = *dirs;
|
||||
@@ -1765,7 +1802,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);
|
||||
print_search_path (dirs, current_what, this_dir->where, buf);
|
||||
}
|
||||
|
||||
edp = (char *) __mempcpy (buf, this_dir->dirname, this_dir->dirnamelen);
|
||||
@@ -1853,6 +1890,7 @@ 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
|
||||
@@ -1860,6 +1898,7 @@ 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;
|
||||
}
|
||||
}
|
||||
@@ -1869,7 +1908,10 @@ 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)
|
||||
return -1;
|
||||
{
|
||||
dl_scratch_buffer_free (&scratch);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Remember whether we found anything. */
|
||||
any |= here_any;
|
||||
@@ -1890,6 +1932,7 @@ open_path (const char *name, size_t namelen, int mode,
|
||||
sps->dirs = (void *) -1;
|
||||
}
|
||||
|
||||
dl_scratch_buffer_free (&scratch);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
+124
-2
@@ -21,7 +21,39 @@
|
||||
#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. */
|
||||
@@ -80,6 +112,95 @@ 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
|
||||
@@ -95,6 +216,8 @@ _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);
|
||||
}
|
||||
|
||||
|
||||
@@ -113,8 +236,7 @@ _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,
|
||||
const struct loadcmd loadcmds[],
|
||||
size_t nloadcmds,
|
||||
struct dl_pt_load_iterator *it,
|
||||
const size_t maplength,
|
||||
bool has_holes,
|
||||
struct link_map *loader); */
|
||||
|
||||
@@ -21,12 +21,36 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Return true iff ELF program headers are incompatible with the running
|
||||
host. */
|
||||
/* 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. */
|
||||
static inline bool
|
||||
elf_machine_reject_phdr_p (const ElfW(Phdr) *phdr, unsigned int phnum,
|
||||
const char *buf, size_t len, struct link_map *map,
|
||||
int fd)
|
||||
elf_machine_reject_phdr_p (const struct dl_machine_phdr_info *info
|
||||
__attribute__ ((__unused__)),
|
||||
struct link_map *map __attribute__ ((__unused__)),
|
||||
int fd __attribute__ ((__unused__)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
+29
-28
@@ -75,11 +75,14 @@ _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,
|
||||
const struct loadcmd loadcmds[], size_t nloadcmds,
|
||||
struct dl_pt_load_iterator *it,
|
||||
const size_t maplength, bool has_holes,
|
||||
struct link_map *loader)
|
||||
{
|
||||
const struct loadcmd *c = loadcmds;
|
||||
/* 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);
|
||||
|
||||
if (__glibc_likely (type == ET_DYN))
|
||||
{
|
||||
@@ -95,16 +98,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)
|
||||
{
|
||||
@@ -113,12 +116,11 @@ _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 (loadcmds[nloadcmds - 1].mapstart <
|
||||
c->mapend))
|
||||
return N_("ELF load command address/offset not page-aligned");
|
||||
if (__glibc_unlikely (it->last_mapstart < c.mapend))
|
||||
return N_("ELF load command address/offset not page-aligned");
|
||||
if (__glibc_unlikely
|
||||
(__mprotect ((caddr_t) (l->l_addr + c->mapend),
|
||||
loadcmds[nloadcmds - 1].mapstart - c->mapend,
|
||||
(__mprotect ((caddr_t) (l->l_addr + c.mapend),
|
||||
it->last_mapstart - c.mapend,
|
||||
PROT_NONE) < 0))
|
||||
return DL_MAP_SEGMENTS_ERROR_MPROTECT;
|
||||
}
|
||||
@@ -129,32 +131,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;
|
||||
|
||||
while (c < &loadcmds[nloadcmds])
|
||||
do
|
||||
{
|
||||
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));
|
||||
|
||||
@@ -166,18 +168,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)
|
||||
@@ -187,7 +189,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;
|
||||
@@ -220,13 +222,12 @@ _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, c->mapstart);
|
||||
ELF_FIXED_ADDRESS (loader, it->last_mapstart);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
+26
-27
@@ -658,6 +658,31 @@ 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
|
||||
@@ -682,19 +707,6 @@ 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. */
|
||||
@@ -716,19 +728,6 @@ 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);
|
||||
@@ -777,7 +776,7 @@ dl_open_worker (void *a)
|
||||
|
||||
#ifdef SHARED
|
||||
if (was_not_consistent)
|
||||
/* Avoid redudant/recursive signalling. */
|
||||
/* Avoid redundant/recursive signalling. */
|
||||
_dl_audit_activity_nsid (nsid, LA_ACT_CONSISTENT);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -31,8 +31,10 @@
|
||||
#include "dynamic-link.h"
|
||||
#include "get-dynamic-info.h"
|
||||
|
||||
/* Relocate static executable with PIE. */
|
||||
|
||||
/* 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. */
|
||||
void
|
||||
_dl_relocate_static_pie (void)
|
||||
{
|
||||
@@ -76,10 +78,9 @@ _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. */
|
||||
ELF_DYNAMIC_RELOCATE (main_map, NULL, 0, 0, 0);
|
||||
main_map->l_relocated = 1;
|
||||
/* 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);
|
||||
|
||||
/* Initialize _r_debug_extended. */
|
||||
struct r_debug *r = _dl_debug_initialize (0, LM_ID_BASE);
|
||||
@@ -89,4 +90,15 @@ _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
|
||||
|
||||
+35
-16
@@ -107,22 +107,21 @@ _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
|
||||
|
||||
/* If the object is not yet relocated we cannot initialize the
|
||||
static TLS region. Delay it. */
|
||||
if (map->l_real->l_relocated)
|
||||
{
|
||||
/* 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. */
|
||||
#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 (__builtin_expect (THREAD_DTV()[0].counter != GL(dl_tls_generation),
|
||||
0))
|
||||
(void) _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 (__glibc_unlikely (THREAD_DTV()[0].counter != GL(dl_tls_generation)))
|
||||
_dl_update_slotinfo (map->l_tls_modid, GL(dl_tls_generation));
|
||||
#endif
|
||||
|
||||
_dl_init_static_tls (map);
|
||||
}
|
||||
else
|
||||
map->l_need_tls_init = 1;
|
||||
_dl_init_static_tls (map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -270,9 +269,29 @@ _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.
|
||||
|
||||
ELF_DYNAMIC_RELOCATE (l, scope, lazy, consider_profiling, skip_ifunc);
|
||||
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);
|
||||
|
||||
if ((consider_profiling || consider_symbind)
|
||||
&& l->l_info[DT_PLTRELSZ] != NULL)
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/* 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)
|
||||
@@ -0,0 +1,145 @@
|
||||
/* 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 */
|
||||
@@ -179,6 +179,12 @@ 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. */
|
||||
|
||||
+9
-5
@@ -22,12 +22,16 @@ static struct link_map *
|
||||
_dl_sym_find_caller_link_map (ElfW(Addr) caller)
|
||||
{
|
||||
struct link_map *l = _dl_find_dso_for_object (caller);
|
||||
if (l != NULL)
|
||||
/* 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)
|
||||
return l;
|
||||
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;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Translates RESULT, *REF, VALUE into a symbol address from the point
|
||||
|
||||
+3
-3
@@ -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 aligment.
|
||||
'_dl_extra_tls_get_size ()' is always a multiple of the alignment.
|
||||
|
||||
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 aligment.
|
||||
'_dl_extra_tls_get_size ()' is always a multiple of the alignment.
|
||||
|
||||
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 initialze TLS for the current
|
||||
pthread_create need to initialize TLS for the current
|
||||
thread regardless of namespace. */
|
||||
if (map->l_ns != LM_ID_BASE && main_thread)
|
||||
continue;
|
||||
|
||||
@@ -116,11 +116,6 @@ glibc {
|
||||
}
|
||||
|
||||
mem {
|
||||
tagging {
|
||||
type: INT_32
|
||||
minval: 0
|
||||
maxval: 255
|
||||
}
|
||||
decorate_maps {
|
||||
type: INT_32
|
||||
minval: 0
|
||||
|
||||
+138
-96
@@ -23,6 +23,9 @@
|
||||
|
||||
#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
|
||||
@@ -34,16 +37,53 @@
|
||||
(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. */
|
||||
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. */
|
||||
|
||||
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 skip_ifunc)
|
||||
int lazy)
|
||||
{
|
||||
const ElfW(Rel) *relative = (const void *) reladdr;
|
||||
const ElfW(Rel) *r = relative + nrelative;
|
||||
@@ -65,14 +105,9 @@ 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, skip_ifunc);
|
||||
elf_machine_rel (map, scope, r, sym, rversion, r_addr_arg, 0);
|
||||
}
|
||||
#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
|
||||
@@ -81,23 +116,12 @@ 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)
|
||||
# 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
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@@ -125,46 +149,13 @@ 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
|
||||
|
||||
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 (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);
|
||||
}
|
||||
|
||||
#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
|
||||
{
|
||||
@@ -172,46 +163,97 @@ 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);
|
||||
# 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
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
#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
|
||||
|
||||
+84
-10
@@ -78,7 +78,18 @@ 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. */
|
||||
|
||||
# define _ELF_DYNAMIC_DO_RELOC(RELOC, reloc, map, scope, do_lazy, skip_ifunc, test_rel) \
|
||||
/* 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) \
|
||||
do { \
|
||||
struct { ElfW(Addr) start, size; \
|
||||
__typeof (((ElfW(Dyn) *) 0)->d_un.d_val) nrelative; int lazy; } \
|
||||
@@ -118,13 +129,36 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
|
||||
} \
|
||||
} \
|
||||
\
|
||||
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); \
|
||||
/* 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); \
|
||||
} while (0)
|
||||
|
||||
# if ELF_MACHINE_NO_REL || ELF_MACHINE_NO_RELA
|
||||
@@ -136,18 +170,36 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
|
||||
# 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)
|
||||
_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)
|
||||
# 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)
|
||||
_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)
|
||||
# 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) \
|
||||
@@ -199,4 +251,26 @@ elf_machine_lazy_rel (struct link_map *map, struct r_scope_elem *scope[],
|
||||
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
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
#!/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()
|
||||
+32
-29
@@ -35,6 +35,7 @@
|
||||
#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>
|
||||
@@ -52,6 +53,7 @@
|
||||
#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>
|
||||
|
||||
@@ -166,12 +168,8 @@ 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
|
||||
@@ -827,15 +825,12 @@ 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. The less
|
||||
information we leave behind, the better, so clear the
|
||||
variable. */
|
||||
_dl_random = NULL;
|
||||
/* 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);
|
||||
}
|
||||
|
||||
#include <setup-vdso.h>
|
||||
@@ -1209,14 +1204,8 @@ rtld_setup_main_map (struct link_map *main_map)
|
||||
main_map->l_relro_size = ph->p_memsz;
|
||||
break;
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
|
||||
_dl_executable_postprocess (main_map, phdr, phnum);
|
||||
|
||||
/* Adjust the address of the TLS initialization image in case
|
||||
the executable is actually an ET_DYN object. */
|
||||
@@ -1589,6 +1578,9 @@ 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);
|
||||
@@ -2263,6 +2255,26 @@ 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);
|
||||
{
|
||||
@@ -2286,10 +2298,6 @@ 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);
|
||||
@@ -2310,12 +2318,7 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
|| count_modids != _dl_count_modids ())
|
||||
++GL(dl_tls_generation);
|
||||
|
||||
/* 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);
|
||||
/* TLS .tdata copy moved before the relocation loop above. */
|
||||
|
||||
/* And finally install it for the main thread. */
|
||||
if (! __rtld_tls_init_tp_called)
|
||||
|
||||
@@ -81,5 +81,15 @@ __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);
|
||||
}
|
||||
|
||||
+30
-7
@@ -1,6 +1,5 @@
|
||||
/* 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.
|
||||
/* Test header that defines feature-marking macros 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.
|
||||
|
||||
@@ -27,7 +26,6 @@
|
||||
#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; \
|
||||
@@ -41,7 +39,32 @@
|
||||
.word 0; \
|
||||
.text
|
||||
|
||||
/* 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)
|
||||
#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)
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
#include "tst-atrandom-scrub.c"
|
||||
@@ -0,0 +1,89 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,67 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,169 @@
|
||||
/* 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>
|
||||
@@ -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 ();
|
||||
size_t stacksize = support_small_thread_stack_size (false);
|
||||
void *stack = xmmap (0,
|
||||
stacksize,
|
||||
PROT_READ | PROT_WRITE,
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,152 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,23 @@
|
||||
/* 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;
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,20 @@
|
||||
/* 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;
|
||||
@@ -0,0 +1,122 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,21 @@
|
||||
/* 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;
|
||||
@@ -46,7 +46,7 @@ do_test (void)
|
||||
|
||||
{
|
||||
/* Install the default implementation of libmarkermod1.so. */
|
||||
char *conf_path = xasprintf ("%s/ld.so.conf", support_sysconfdir_prefix);
|
||||
char *conf_path = xasprintf ("%s/ld.so.conf.d/hwcaps.conf", support_sysconfdir_prefix);
|
||||
xmkdirp (support_sysconfdir_prefix, 0777);
|
||||
support_write_file_string (conf_path, "/glibc-test/lib\n");
|
||||
free (conf_path);
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,24 @@
|
||||
/* 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"
|
||||
@@ -0,0 +1,23 @@
|
||||
/* 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;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,46 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,59 @@
|
||||
/* 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")));
|
||||
@@ -0,0 +1,38 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,64 @@
|
||||
/* 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;
|
||||
@@ -0,0 +1,69 @@
|
||||
/* 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;
|
||||
@@ -0,0 +1,2 @@
|
||||
/* Companion module for tst-ifunc-resolver-protector-static-non-pie. */
|
||||
#include "tst-ifunc-resolver-protector-static-mod.c"
|
||||
@@ -0,0 +1,5 @@
|
||||
/* 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"
|
||||
@@ -0,0 +1,61 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,42 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,55 @@
|
||||
/* 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;
|
||||
@@ -0,0 +1,3 @@
|
||||
#define TLS_MODEL "global-dynamic"
|
||||
#define SENTINEL_STORAGE /* empty */
|
||||
#include "tst-ifunc-tls-init-gd-ld-lib-skeleton.c"
|
||||
@@ -0,0 +1,73 @@
|
||||
/* 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;
|
||||
@@ -0,0 +1,89 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,2 @@
|
||||
#define TLS_MODEL "global-dynamic"
|
||||
#include "tst-ifunc-tls-init-gd-ld-lib-skeleton.c"
|
||||
@@ -0,0 +1,2 @@
|
||||
#define TLS_MODEL "local-dynamic"
|
||||
#include "tst-ifunc-tls-init-gd-ld-lib-skeleton.c"
|
||||
@@ -0,0 +1,61 @@
|
||||
/* 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;
|
||||
@@ -0,0 +1 @@
|
||||
#include "tst-ifunc-tls-init-lib-skeleton.c"
|
||||
@@ -0,0 +1 @@
|
||||
#include "tst-ifunc-tls-init-lib-skeleton.c"
|
||||
@@ -0,0 +1,3 @@
|
||||
#define TLS_MODEL "global-dynamic"
|
||||
#define SENTINEL_STORAGE /* empty */
|
||||
#include "tst-ifunc-tls-init-gd-ld-lib-skeleton.c"
|
||||
@@ -0,0 +1,69 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,85 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,58 @@
|
||||
/* 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;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/* PowerPC definitions for libc main startup.
|
||||
Copyright (C) 2017-2026 Free Software Foundation, Inc.
|
||||
/* 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
|
||||
@@ -16,15 +16,19 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef _LIBC_START_H
|
||||
#define _LIBC_START_H
|
||||
#include <support/check.h>
|
||||
|
||||
#ifndef SHARED
|
||||
/* IREL{,A} must happen after TCB initialization in order to allow IFUNC
|
||||
resolvers to read TCB fields, e.g. hwcap and at_platform. */
|
||||
#define ARCH_SETUP_IREL()
|
||||
#define ARCH_SETUP_TLS() __libc_setup_tls ()
|
||||
#define ARCH_APPLY_IREL() apply_irel ()
|
||||
#endif /* ! SHARED */
|
||||
#define MARKER 0x32125A5Au
|
||||
|
||||
#endif /* _LIBC_START_H */
|
||||
extern unsigned int (*fptr) (void);
|
||||
extern unsigned int get_counter (void);
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
TEST_VERIFY (fptr != NULL);
|
||||
TEST_COMPARE (get_counter (), MARKER);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
@@ -0,0 +1,29 @@
|
||||
/* 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);
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1 @@
|
||||
cp $B/elf/tst-ptrguard-static-dlopen-mod.so $L/tst-ptrguard-static-dlopen-mod.so
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Shared library for tst-tls-tdata-reloc.
|
||||
Copyright (C) 2026 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#define EXPECTED 0x12345678
|
||||
|
||||
static int
|
||||
callee (void)
|
||||
{
|
||||
return EXPECTED;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int (*func) (void);
|
||||
} cache_t;
|
||||
|
||||
/* General-dynamic TLS: the initialiser of '.func' produces an
|
||||
R_*_RELATIVE on .tdata. */
|
||||
__thread cache_t cache = { .func = callee };
|
||||
|
||||
int
|
||||
call_through_tls (void)
|
||||
{
|
||||
if (cache.func == 0)
|
||||
return -1;
|
||||
return cache.func ();
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* 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>
|
||||
@@ -0,0 +1,6 @@
|
||||
# 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 $@
|
||||
@@ -39,8 +39,8 @@ struct protoent_data {};
|
||||
#include <nss/nss_files/files-parse.c>
|
||||
LINE_PARSER
|
||||
("#",
|
||||
STRING_FIELD (result->p_name, isspace, 1);
|
||||
INT_FIELD (result->p_proto, isspace, 1, 10,);
|
||||
STRING_FIELD (result->p_name, isspace, true);
|
||||
INT_FIELD (result->p_proto, isspace, true, 10,);
|
||||
)
|
||||
|
||||
enum nss_status
|
||||
|
||||
@@ -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, 1);
|
||||
STRING_FIELD (result->s_proto, ISSC_OR_SPACE, 1);
|
||||
INT_FIELD (result->s_port, ISSC_OR_SPACE, 10, 0, htons);
|
||||
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);
|
||||
)
|
||||
|
||||
enum nss_status
|
||||
|
||||
+2
-1
@@ -139,7 +139,8 @@ $(objpfx)test-iconvconfig.out: $(objpfx)iconvconfig
|
||||
rm -f $$tmp) > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
$(objpfx)tst-iconv_prog.out: tst-iconv_prog.sh $(objpfx)iconv_prog
|
||||
$(objpfx)tst-iconv_prog.out: tst-iconv_prog.sh $(objpfx)iconv_prog \
|
||||
$(gen-locales)
|
||||
$(BASH) $< $(common-objdir) '$(test-wrapper-env)' \
|
||||
'$(run-program-env)' '$(rpath-link)' > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
+4
-2
@@ -144,8 +144,10 @@
|
||||
if (irreversible == NULL) \
|
||||
{ \
|
||||
/* This means we are in call from __gconv_transliterate. In this \
|
||||
case we are not doing any error recovery ourselves. */ \
|
||||
result = __gconv_mark_illegal_input (step_data); \
|
||||
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; \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
|
||||
+20
-10
@@ -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,6 +220,7 @@ 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
|
||||
@@ -280,12 +281,21 @@ check_errtest_result ()
|
||||
fi
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
@@ -113,6 +113,7 @@
|
||||
#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. */
|
||||
@@ -670,7 +671,7 @@ for linking")
|
||||
|
||||
/* Helper / base macros for indirect function symbols. */
|
||||
#define __ifunc_resolver(type_name, name, expr, init, classifier, ...) \
|
||||
classifier inhibit_stack_protector \
|
||||
classifier \
|
||||
__typeof (type_name) *name##_ifunc (__VA_ARGS__) \
|
||||
{ \
|
||||
init (); \
|
||||
|
||||
@@ -70,6 +70,11 @@ 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;
|
||||
|
||||
|
||||
+65
-4
@@ -5,12 +5,73 @@
|
||||
#ifndef _ISOMAC
|
||||
# include <stdint.h>
|
||||
|
||||
# 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_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))
|
||||
# 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
|
||||
@@ -48,7 +109,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);
|
||||
@@ -62,7 +123,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 */
|
||||
|
||||
|
||||
@@ -134,10 +134,6 @@ 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
|
||||
|
||||
+26
-6
@@ -22,13 +22,21 @@ 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
|
||||
test-srcs := \
|
||||
tst-gettext \
|
||||
tst-translit \
|
||||
tst-gettext2 \
|
||||
tst-codeset \
|
||||
tst-gettext3 \
|
||||
tst-plural-eval
|
||||
# test-srcs
|
||||
|
||||
ifeq ($(have-thread-library),yes)
|
||||
test-srcs += $(multithread-test-srcs)
|
||||
endif
|
||||
@@ -53,9 +61,15 @@ $(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
|
||||
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
|
||||
|
||||
ifeq ($(have-thread-library),yes)
|
||||
tests-special += $(objpfx)tst-gettext4.out $(objpfx)tst-gettext5.out \
|
||||
$(objpfx)tst-gettext6.out
|
||||
@@ -103,6 +117,10 @@ $(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)
|
||||
@@ -140,6 +158,7 @@ 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))
|
||||
@@ -156,6 +175,7 @@ $(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)"' \
|
||||
|
||||
+28
-130
@@ -68,20 +68,6 @@ 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
|
||||
@@ -95,9 +81,6 @@ extern int errno;
|
||||
#ifdef _LIBC
|
||||
# include <libintl.h>
|
||||
#else
|
||||
# ifdef IN_LIBGLOCALE
|
||||
# include <libintl.h>
|
||||
# endif
|
||||
# include "libgnuintl.h"
|
||||
#endif
|
||||
#include "hash-string.h"
|
||||
@@ -228,7 +211,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 || defined IN_LIBGLOCALE
|
||||
#if defined _LIBC || HAVE_USELOCALE
|
||||
# define HAVE_PER_THREAD_LOCALE
|
||||
#endif
|
||||
|
||||
@@ -247,11 +230,6 @@ 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;
|
||||
|
||||
@@ -299,10 +277,6 @@ 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). */
|
||||
@@ -318,11 +292,9 @@ 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__
|
||||
@@ -338,25 +310,17 @@ 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"
|
||||
@@ -364,7 +328,7 @@ static const char *guess_category_value (int category,
|
||||
#else
|
||||
static const char *category_to_name (int category);
|
||||
#endif
|
||||
#if (defined _LIBC || HAVE_ICONV) && !defined IN_LIBGLOCALE
|
||||
#if (defined _LIBC || HAVE_ICONV)
|
||||
static const char *get_output_charset (struct binding *domainbinding);
|
||||
#endif
|
||||
|
||||
@@ -443,18 +407,9 @@ 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;
|
||||
@@ -469,7 +424,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 && !defined IN_LIBGLOCALE
|
||||
#if defined HAVE_PER_THREAD_LOCALE
|
||||
const char *localename;
|
||||
#endif
|
||||
size_t domainname_len;
|
||||
@@ -516,21 +471,16 @@ DCIGETTEXT (const char *domainname, const char *msgid1, const char *msgid2,
|
||||
search.domainname = domainname;
|
||||
search.category = category;
|
||||
#ifdef HAVE_PER_THREAD_LOCALE
|
||||
# ifndef IN_LIBGLOCALE
|
||||
# ifdef _LIBC
|
||||
# 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. */
|
||||
@@ -562,12 +512,6 @@ 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);
|
||||
@@ -587,7 +531,6 @@ 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. */
|
||||
@@ -603,20 +546,13 @@ 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);
|
||||
@@ -675,11 +611,7 @@ 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)
|
||||
{
|
||||
@@ -687,13 +619,8 @@ 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. */
|
||||
@@ -754,9 +681,6 @@ 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;
|
||||
@@ -837,17 +761,10 @@ 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;
|
||||
@@ -953,16 +870,10 @@ _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;
|
||||
@@ -1056,12 +967,7 @@ _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. */
|
||||
@@ -1230,7 +1136,6 @@ _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
|
||||
@@ -1239,13 +1144,16 @@ _nl_find_msg (struct loaded_l10nfile *domain_file,
|
||||
while (1)
|
||||
{
|
||||
transmem_block_t *newmem;
|
||||
# ifdef _LIBC
|
||||
size_t non_reversible;
|
||||
int res;
|
||||
|
||||
if (freemem_size < sizeof (size_t))
|
||||
goto resize_freemem;
|
||||
|
||||
outbuf = freemem + sizeof (size_t);
|
||||
|
||||
# ifdef _LIBC
|
||||
size_t non_reversible;
|
||||
int res;
|
||||
|
||||
res = __gconv (convd->conv,
|
||||
&inbuf, inbuf + resultlen,
|
||||
&outbuf,
|
||||
@@ -1271,9 +1179,6 @@ _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,
|
||||
@@ -1342,8 +1247,6 @@ _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
|
||||
@@ -1381,14 +1284,19 @@ 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;
|
||||
|
||||
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. */
|
||||
result = plural_eval (domaindata->plural, n);
|
||||
if (result.status != PE_OK)
|
||||
/* The plural expression evaluation failed. */
|
||||
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;
|
||||
@@ -1470,21 +1378,13 @@ 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:
|
||||
@@ -1501,18 +1401,17 @@ 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)
|
||||
@@ -1521,7 +1420,6 @@ 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
|
||||
@@ -1547,7 +1445,7 @@ guess_category_value (int category, const char *categoryname)
|
||||
language = getenv ("LANGUAGE");
|
||||
if (language != NULL && language[0] != '\0')
|
||||
return language;
|
||||
#if !defined IN_LIBGLOCALE && !defined _LIBC
|
||||
#ifndef _LIBC
|
||||
/* The next priority value is the locale name, if not defaulted. */
|
||||
if (locale_defaulted)
|
||||
{
|
||||
@@ -1561,7 +1459,7 @@ guess_category_value (int category, const char *categoryname)
|
||||
return locale;
|
||||
}
|
||||
|
||||
#if (defined _LIBC || HAVE_ICONV) && !defined IN_LIBGLOCALE
|
||||
#if (defined _LIBC || HAVE_ICONV)
|
||||
/* Returns the output charset. */
|
||||
static const char *
|
||||
get_output_charset (struct binding *domainbinding)
|
||||
|
||||
+104
-34
@@ -14,24 +14,59 @@
|
||||
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
|
||||
|
||||
/* Evaluate the plural expression and return an index value. */
|
||||
STATIC
|
||||
unsigned long int
|
||||
plural_eval (const struct expression *pexp, unsigned long int n)
|
||||
/* 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)
|
||||
{
|
||||
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 n;
|
||||
return OK (n);
|
||||
case num:
|
||||
return pexp->val.num;
|
||||
return OK (pexp->val.num);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -40,52 +75,73 @@ plural_eval (const struct expression *pexp, unsigned long int n)
|
||||
case 1:
|
||||
{
|
||||
/* pexp->operation must be lnot. */
|
||||
unsigned long int arg = plural_eval (pexp->val.args[0], n);
|
||||
return ! arg;
|
||||
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);
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
unsigned long int leftarg = plural_eval (pexp->val.args[0], n);
|
||||
struct eval_result leftarg =
|
||||
plural_eval_recurse (pexp->val.args[0], n, allowed_depth);
|
||||
if (leftarg.status != PE_OK)
|
||||
return leftarg;
|
||||
if (pexp->operation == lor)
|
||||
return leftarg || plural_eval (pexp->val.args[1], n);
|
||||
{
|
||||
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);
|
||||
}
|
||||
else if (pexp->operation == land)
|
||||
return leftarg && plural_eval (pexp->val.args[1], n);
|
||||
{
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned long int rightarg = plural_eval (pexp->val.args[1], n);
|
||||
struct eval_result rightarg =
|
||||
plural_eval_recurse (pexp->val.args[1], n, allowed_depth);
|
||||
if (rightarg.status != PE_OK)
|
||||
return rightarg;
|
||||
|
||||
switch (pexp->operation)
|
||||
{
|
||||
case mult:
|
||||
return leftarg * rightarg;
|
||||
return OK (leftarg.value * rightarg.value);
|
||||
case divide:
|
||||
#if !INTDIV0_RAISES_SIGFPE
|
||||
if (rightarg == 0)
|
||||
raise (SIGFPE);
|
||||
#endif
|
||||
return leftarg / rightarg;
|
||||
if (rightarg.value == 0)
|
||||
return (struct eval_result) { .status = PE_INTDIV };
|
||||
return OK (leftarg.value / rightarg.value);
|
||||
case module:
|
||||
#if !INTDIV0_RAISES_SIGFPE
|
||||
if (rightarg == 0)
|
||||
raise (SIGFPE);
|
||||
#endif
|
||||
return leftarg % rightarg;
|
||||
if (rightarg.value == 0)
|
||||
return (struct eval_result) { .status = PE_INTDIV };
|
||||
return OK (leftarg.value % rightarg.value);
|
||||
case plus:
|
||||
return leftarg + rightarg;
|
||||
return OK (leftarg.value + rightarg.value);
|
||||
case minus:
|
||||
return leftarg - rightarg;
|
||||
return OK (leftarg.value - rightarg.value);
|
||||
case less_than:
|
||||
return leftarg < rightarg;
|
||||
return OK (leftarg.value < rightarg.value);
|
||||
case greater_than:
|
||||
return leftarg > rightarg;
|
||||
return OK (leftarg.value > rightarg.value);
|
||||
case less_or_equal:
|
||||
return leftarg <= rightarg;
|
||||
return OK (leftarg.value <= rightarg.value);
|
||||
case greater_or_equal:
|
||||
return leftarg >= rightarg;
|
||||
return OK (leftarg.value >= rightarg.value);
|
||||
case equal:
|
||||
return leftarg == rightarg;
|
||||
return OK (leftarg.value == rightarg.value);
|
||||
case not_equal:
|
||||
return leftarg != rightarg;
|
||||
return OK (leftarg.value != rightarg.value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -96,10 +152,24 @@ plural_eval (const struct expression *pexp, unsigned long int n)
|
||||
case 3:
|
||||
{
|
||||
/* pexp->operation must be qmop. */
|
||||
unsigned long int boolarg = plural_eval (pexp->val.args[0], n);
|
||||
return plural_eval (pexp->val.args[boolarg ? 1 : 2], n);
|
||||
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);
|
||||
}
|
||||
}
|
||||
/* NOTREACHED */
|
||||
return 0;
|
||||
return (struct eval_result) { .status = PE_ASSERT };
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
||||
+34
-38
@@ -50,7 +50,6 @@
|
||||
/* 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. */
|
||||
@@ -133,55 +132,52 @@ _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)
|
||||
/* 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)
|
||||
mask = _nl_explode_name (locale, &language, &modifier, &territory, &codeset,
|
||||
&normalized_codeset);
|
||||
if (mask != -1) /* Not out of memory? */
|
||||
{
|
||||
int cnt;
|
||||
for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
|
||||
/* 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? */
|
||||
{
|
||||
if (retval->successor[cnt]->decided <= 0)
|
||||
_nl_load_domain (retval->successor[cnt], domainbinding);
|
||||
if (retval->successor[cnt]->data != NULL)
|
||||
break;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
|
||||
@@ -70,18 +70,10 @@ 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"
|
||||
@@ -216,12 +208,7 @@ 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);
|
||||
@@ -258,18 +245,10 @@ 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
|
||||
|
||||
+84
-93
@@ -77,361 +77,357 @@
|
||||
# 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 || PRI_MACROS_BROKEN
|
||||
#if !defined PRId8
|
||||
# undef PRId8
|
||||
# define PRId8 "d"
|
||||
#endif
|
||||
#if !defined PRIi8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIi8
|
||||
# undef PRIi8
|
||||
# define PRIi8 "i"
|
||||
#endif
|
||||
#if !defined PRIo8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIo8
|
||||
# undef PRIo8
|
||||
# define PRIo8 "o"
|
||||
#endif
|
||||
#if !defined PRIu8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIu8
|
||||
# undef PRIu8
|
||||
# define PRIu8 "u"
|
||||
#endif
|
||||
#if !defined PRIx8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIx8
|
||||
# undef PRIx8
|
||||
# define PRIx8 "x"
|
||||
#endif
|
||||
#if !defined PRIX8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIX8
|
||||
# undef PRIX8
|
||||
# define PRIX8 "X"
|
||||
#endif
|
||||
#if !defined PRId16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRId16
|
||||
# undef PRId16
|
||||
# define PRId16 "d"
|
||||
#endif
|
||||
#if !defined PRIi16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIi16
|
||||
# undef PRIi16
|
||||
# define PRIi16 "i"
|
||||
#endif
|
||||
#if !defined PRIo16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIo16
|
||||
# undef PRIo16
|
||||
# define PRIo16 "o"
|
||||
#endif
|
||||
#if !defined PRIu16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIu16
|
||||
# undef PRIu16
|
||||
# define PRIu16 "u"
|
||||
#endif
|
||||
#if !defined PRIx16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIx16
|
||||
# undef PRIx16
|
||||
# define PRIx16 "x"
|
||||
#endif
|
||||
#if !defined PRIX16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIX16
|
||||
# undef PRIX16
|
||||
# define PRIX16 "X"
|
||||
#endif
|
||||
#if !defined PRId32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRId32
|
||||
# undef PRId32
|
||||
# define PRId32 "d"
|
||||
#endif
|
||||
#if !defined PRIi32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIi32
|
||||
# undef PRIi32
|
||||
# define PRIi32 "i"
|
||||
#endif
|
||||
#if !defined PRIo32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIo32
|
||||
# undef PRIo32
|
||||
# define PRIo32 "o"
|
||||
#endif
|
||||
#if !defined PRIu32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIu32
|
||||
# undef PRIu32
|
||||
# define PRIu32 "u"
|
||||
#endif
|
||||
#if !defined PRIx32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIx32
|
||||
# undef PRIx32
|
||||
# define PRIx32 "x"
|
||||
#endif
|
||||
#if !defined PRIX32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIX32
|
||||
# undef PRIX32
|
||||
# define PRIX32 "X"
|
||||
#endif
|
||||
#if !defined PRId64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRId64
|
||||
# undef PRId64
|
||||
# define PRId64 (sizeof (long) == 8 ? "ld" : "lld")
|
||||
#endif
|
||||
#if !defined PRIi64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIi64
|
||||
# undef PRIi64
|
||||
# define PRIi64 (sizeof (long) == 8 ? "li" : "lli")
|
||||
#endif
|
||||
#if !defined PRIo64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIo64
|
||||
# undef PRIo64
|
||||
# define PRIo64 (sizeof (long) == 8 ? "lo" : "llo")
|
||||
#endif
|
||||
#if !defined PRIu64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIu64
|
||||
# undef PRIu64
|
||||
# define PRIu64 (sizeof (long) == 8 ? "lu" : "llu")
|
||||
#endif
|
||||
#if !defined PRIx64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIx64
|
||||
# undef PRIx64
|
||||
# define PRIx64 (sizeof (long) == 8 ? "lx" : "llx")
|
||||
#endif
|
||||
#if !defined PRIX64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIX64
|
||||
# undef PRIX64
|
||||
# define PRIX64 (sizeof (long) == 8 ? "lX" : "llX")
|
||||
#endif
|
||||
#if !defined PRIdLEAST8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIdLEAST8
|
||||
# undef PRIdLEAST8
|
||||
# define PRIdLEAST8 "d"
|
||||
#endif
|
||||
#if !defined PRIiLEAST8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIiLEAST8
|
||||
# undef PRIiLEAST8
|
||||
# define PRIiLEAST8 "i"
|
||||
#endif
|
||||
#if !defined PRIoLEAST8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIoLEAST8
|
||||
# undef PRIoLEAST8
|
||||
# define PRIoLEAST8 "o"
|
||||
#endif
|
||||
#if !defined PRIuLEAST8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIuLEAST8
|
||||
# undef PRIuLEAST8
|
||||
# define PRIuLEAST8 "u"
|
||||
#endif
|
||||
#if !defined PRIxLEAST8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIxLEAST8
|
||||
# undef PRIxLEAST8
|
||||
# define PRIxLEAST8 "x"
|
||||
#endif
|
||||
#if !defined PRIXLEAST8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIXLEAST8
|
||||
# undef PRIXLEAST8
|
||||
# define PRIXLEAST8 "X"
|
||||
#endif
|
||||
#if !defined PRIdLEAST16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIdLEAST16
|
||||
# undef PRIdLEAST16
|
||||
# define PRIdLEAST16 "d"
|
||||
#endif
|
||||
#if !defined PRIiLEAST16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIiLEAST16
|
||||
# undef PRIiLEAST16
|
||||
# define PRIiLEAST16 "i"
|
||||
#endif
|
||||
#if !defined PRIoLEAST16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIoLEAST16
|
||||
# undef PRIoLEAST16
|
||||
# define PRIoLEAST16 "o"
|
||||
#endif
|
||||
#if !defined PRIuLEAST16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIuLEAST16
|
||||
# undef PRIuLEAST16
|
||||
# define PRIuLEAST16 "u"
|
||||
#endif
|
||||
#if !defined PRIxLEAST16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIxLEAST16
|
||||
# undef PRIxLEAST16
|
||||
# define PRIxLEAST16 "x"
|
||||
#endif
|
||||
#if !defined PRIXLEAST16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIXLEAST16
|
||||
# undef PRIXLEAST16
|
||||
# define PRIXLEAST16 "X"
|
||||
#endif
|
||||
#if !defined PRIdLEAST32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIdLEAST32
|
||||
# undef PRIdLEAST32
|
||||
# define PRIdLEAST32 "d"
|
||||
#endif
|
||||
#if !defined PRIiLEAST32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIiLEAST32
|
||||
# undef PRIiLEAST32
|
||||
# define PRIiLEAST32 "i"
|
||||
#endif
|
||||
#if !defined PRIoLEAST32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIoLEAST32
|
||||
# undef PRIoLEAST32
|
||||
# define PRIoLEAST32 "o"
|
||||
#endif
|
||||
#if !defined PRIuLEAST32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIuLEAST32
|
||||
# undef PRIuLEAST32
|
||||
# define PRIuLEAST32 "u"
|
||||
#endif
|
||||
#if !defined PRIxLEAST32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIxLEAST32
|
||||
# undef PRIxLEAST32
|
||||
# define PRIxLEAST32 "x"
|
||||
#endif
|
||||
#if !defined PRIXLEAST32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIXLEAST32
|
||||
# undef PRIXLEAST32
|
||||
# define PRIXLEAST32 "X"
|
||||
#endif
|
||||
#if !defined PRIdLEAST64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIdLEAST64
|
||||
# undef PRIdLEAST64
|
||||
# define PRIdLEAST64 PRId64
|
||||
#endif
|
||||
#if !defined PRIiLEAST64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIiLEAST64
|
||||
# undef PRIiLEAST64
|
||||
# define PRIiLEAST64 PRIi64
|
||||
#endif
|
||||
#if !defined PRIoLEAST64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIoLEAST64
|
||||
# undef PRIoLEAST64
|
||||
# define PRIoLEAST64 PRIo64
|
||||
#endif
|
||||
#if !defined PRIuLEAST64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIuLEAST64
|
||||
# undef PRIuLEAST64
|
||||
# define PRIuLEAST64 PRIu64
|
||||
#endif
|
||||
#if !defined PRIxLEAST64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIxLEAST64
|
||||
# undef PRIxLEAST64
|
||||
# define PRIxLEAST64 PRIx64
|
||||
#endif
|
||||
#if !defined PRIXLEAST64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIXLEAST64
|
||||
# undef PRIXLEAST64
|
||||
# define PRIXLEAST64 PRIX64
|
||||
#endif
|
||||
#if !defined PRIdFAST8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIdFAST8
|
||||
# undef PRIdFAST8
|
||||
# define PRIdFAST8 "d"
|
||||
#endif
|
||||
#if !defined PRIiFAST8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIiFAST8
|
||||
# undef PRIiFAST8
|
||||
# define PRIiFAST8 "i"
|
||||
#endif
|
||||
#if !defined PRIoFAST8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIoFAST8
|
||||
# undef PRIoFAST8
|
||||
# define PRIoFAST8 "o"
|
||||
#endif
|
||||
#if !defined PRIuFAST8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIuFAST8
|
||||
# undef PRIuFAST8
|
||||
# define PRIuFAST8 "u"
|
||||
#endif
|
||||
#if !defined PRIxFAST8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIxFAST8
|
||||
# undef PRIxFAST8
|
||||
# define PRIxFAST8 "x"
|
||||
#endif
|
||||
#if !defined PRIXFAST8 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIXFAST8
|
||||
# undef PRIXFAST8
|
||||
# define PRIXFAST8 "X"
|
||||
#endif
|
||||
#if !defined PRIdFAST16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIdFAST16
|
||||
# undef PRIdFAST16
|
||||
# define PRIdFAST16 "d"
|
||||
#endif
|
||||
#if !defined PRIiFAST16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIiFAST16
|
||||
# undef PRIiFAST16
|
||||
# define PRIiFAST16 "i"
|
||||
#endif
|
||||
#if !defined PRIoFAST16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIoFAST16
|
||||
# undef PRIoFAST16
|
||||
# define PRIoFAST16 "o"
|
||||
#endif
|
||||
#if !defined PRIuFAST16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIuFAST16
|
||||
# undef PRIuFAST16
|
||||
# define PRIuFAST16 "u"
|
||||
#endif
|
||||
#if !defined PRIxFAST16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIxFAST16
|
||||
# undef PRIxFAST16
|
||||
# define PRIxFAST16 "x"
|
||||
#endif
|
||||
#if !defined PRIXFAST16 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIXFAST16
|
||||
# undef PRIXFAST16
|
||||
# define PRIXFAST16 "X"
|
||||
#endif
|
||||
#if !defined PRIdFAST32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIdFAST32
|
||||
# undef PRIdFAST32
|
||||
# define PRIdFAST32 "d"
|
||||
#endif
|
||||
#if !defined PRIiFAST32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIiFAST32
|
||||
# undef PRIiFAST32
|
||||
# define PRIiFAST32 "i"
|
||||
#endif
|
||||
#if !defined PRIoFAST32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIoFAST32
|
||||
# undef PRIoFAST32
|
||||
# define PRIoFAST32 "o"
|
||||
#endif
|
||||
#if !defined PRIuFAST32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIuFAST32
|
||||
# undef PRIuFAST32
|
||||
# define PRIuFAST32 "u"
|
||||
#endif
|
||||
#if !defined PRIxFAST32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIxFAST32
|
||||
# undef PRIxFAST32
|
||||
# define PRIxFAST32 "x"
|
||||
#endif
|
||||
#if !defined PRIXFAST32 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIXFAST32
|
||||
# undef PRIXFAST32
|
||||
# define PRIXFAST32 "X"
|
||||
#endif
|
||||
#if !defined PRIdFAST64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIdFAST64
|
||||
# undef PRIdFAST64
|
||||
# define PRIdFAST64 PRId64
|
||||
#endif
|
||||
#if !defined PRIiFAST64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIiFAST64
|
||||
# undef PRIiFAST64
|
||||
# define PRIiFAST64 PRIi64
|
||||
#endif
|
||||
#if !defined PRIoFAST64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIoFAST64
|
||||
# undef PRIoFAST64
|
||||
# define PRIoFAST64 PRIo64
|
||||
#endif
|
||||
#if !defined PRIuFAST64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIuFAST64
|
||||
# undef PRIuFAST64
|
||||
# define PRIuFAST64 PRIu64
|
||||
#endif
|
||||
#if !defined PRIxFAST64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIxFAST64
|
||||
# undef PRIxFAST64
|
||||
# define PRIxFAST64 PRIx64
|
||||
#endif
|
||||
#if !defined PRIXFAST64 || PRI_MACROS_BROKEN
|
||||
#if !defined PRIXFAST64
|
||||
# undef PRIXFAST64
|
||||
# define PRIXFAST64 PRIX64
|
||||
#endif
|
||||
#if !defined PRIdMAX || PRI_MACROS_BROKEN
|
||||
#if !defined PRIdMAX
|
||||
# undef PRIdMAX
|
||||
# define PRIdMAX (sizeof (uintmax_t) == sizeof (long) ? "ld" : "lld")
|
||||
#endif
|
||||
#if !defined PRIiMAX || PRI_MACROS_BROKEN
|
||||
#if !defined PRIiMAX
|
||||
# undef PRIiMAX
|
||||
# define PRIiMAX (sizeof (uintmax_t) == sizeof (long) ? "li" : "lli")
|
||||
#endif
|
||||
#if !defined PRIoMAX || PRI_MACROS_BROKEN
|
||||
#if !defined PRIoMAX
|
||||
# undef PRIoMAX
|
||||
# define PRIoMAX (sizeof (uintmax_t) == sizeof (long) ? "lo" : "llo")
|
||||
#endif
|
||||
#if !defined PRIuMAX || PRI_MACROS_BROKEN
|
||||
#if !defined PRIuMAX
|
||||
# undef PRIuMAX
|
||||
# define PRIuMAX (sizeof (uintmax_t) == sizeof (long) ? "lu" : "llu")
|
||||
#endif
|
||||
#if !defined PRIxMAX || PRI_MACROS_BROKEN
|
||||
#if !defined PRIxMAX
|
||||
# undef PRIxMAX
|
||||
# define PRIxMAX (sizeof (uintmax_t) == sizeof (long) ? "lx" : "llx")
|
||||
#endif
|
||||
#if !defined PRIXMAX || PRI_MACROS_BROKEN
|
||||
#if !defined PRIXMAX
|
||||
# undef PRIXMAX
|
||||
# define PRIXMAX (sizeof (uintmax_t) == sizeof (long) ? "lX" : "llX")
|
||||
#endif
|
||||
#if !defined PRIdPTR || PRI_MACROS_BROKEN
|
||||
#if !defined PRIdPTR
|
||||
# undef PRIdPTR
|
||||
# define PRIdPTR \
|
||||
(sizeof (void *) == sizeof (long) ? "ld" : \
|
||||
sizeof (void *) == sizeof (int) ? "d" : \
|
||||
"lld")
|
||||
#endif
|
||||
#if !defined PRIiPTR || PRI_MACROS_BROKEN
|
||||
#if !defined PRIiPTR
|
||||
# undef PRIiPTR
|
||||
# define PRIiPTR \
|
||||
(sizeof (void *) == sizeof (long) ? "li" : \
|
||||
sizeof (void *) == sizeof (int) ? "i" : \
|
||||
"lli")
|
||||
#endif
|
||||
#if !defined PRIoPTR || PRI_MACROS_BROKEN
|
||||
#if !defined PRIoPTR
|
||||
# undef PRIoPTR
|
||||
# define PRIoPTR \
|
||||
(sizeof (void *) == sizeof (long) ? "lo" : \
|
||||
sizeof (void *) == sizeof (int) ? "o" : \
|
||||
"llo")
|
||||
#endif
|
||||
#if !defined PRIuPTR || PRI_MACROS_BROKEN
|
||||
#if !defined PRIuPTR
|
||||
# undef PRIuPTR
|
||||
# define PRIuPTR \
|
||||
(sizeof (void *) == sizeof (long) ? "lu" : \
|
||||
sizeof (void *) == sizeof (int) ? "u" : \
|
||||
"llu")
|
||||
#endif
|
||||
#if !defined PRIxPTR || PRI_MACROS_BROKEN
|
||||
#if !defined PRIxPTR
|
||||
# undef PRIxPTR
|
||||
# define PRIxPTR \
|
||||
(sizeof (void *) == sizeof (long) ? "lx" : \
|
||||
sizeof (void *) == sizeof (int) ? "x" : \
|
||||
"llx")
|
||||
#endif
|
||||
#if !defined PRIXPTR || PRI_MACROS_BROKEN
|
||||
#if !defined PRIXPTR
|
||||
# undef PRIXPTR
|
||||
# define PRIXPTR \
|
||||
(sizeof (void *) == sizeof (long) ? "lX" : \
|
||||
@@ -1256,12 +1252,7 @@ _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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user