diff --git a/glibc-RHEL-93320-1.patch b/glibc-RHEL-93320-1.patch new file mode 100644 index 0000000..d5063cf --- /dev/null +++ b/glibc-RHEL-93320-1.patch @@ -0,0 +1,781 @@ +commit 44829b3ddb64e99e37343a0f25b2c082387d31a5 +Author: Noah Goldstein +Date: Thu Oct 21 15:54:57 2021 -0500 + + String: Add support for __memcmpeq() ABI on all targets + + No bug. + + This commit adds support for __memcmpeq() as a new ABI for all + targets. In this commit __memcmpeq() is implemented only as an alias + to the corresponding targets memcmp() implementation. __memcmpeq() is + added as a new symbol starting with GLIBC_2.35 and defined in string.h + with comments explaining its behavior. Basic tests that it is callable + and works where added in string/tester.c + + As discussed in the proposal "Add new ABI '__memcmpeq()' to libc" + __memcmpeq() is essentially a reserved namespace for bcmp(). The means + is shares the same specifications as memcmp() except the return value + for non-equal byte sequences is any non-zero value. This is less + strict than memcmp()'s return value specification and can be better + optimized when a boolean return is all that is needed. + + __memcmpeq() is meant to only be called by compilers if they can prove + that the return value of a memcmp() call is only used for its boolean + value. + + All tests in string/tester.c passed. As well build succeeds on + x86_64-linux-gnu target. + +diff --git a/string/Versions b/string/Versions +index 298ecd401aa49fd5..864c4cf7a4bda473 100644 +--- a/string/Versions ++++ b/string/Versions +@@ -89,4 +89,7 @@ libc { + sigdescr_np; sigabbrev_np; + strerrordesc_np; strerrorname_np; + } ++ GLIBC_2.35 { ++ __memcmpeq; ++ } + } +diff --git a/string/memcmp.c b/string/memcmp.c +index 9b46d7a905c8b788..eac411253050c0e7 100644 +--- a/string/memcmp.c ++++ b/string/memcmp.c +@@ -359,3 +359,6 @@ libc_hidden_builtin_def(memcmp) + # undef bcmp + weak_alias (memcmp, bcmp) + #endif ++ ++#undef __memcmpeq ++strong_alias (memcmp, __memcmpeq) +diff --git a/string/string.h b/string/string.h +index 8dcafb4ac4952853..639e2f56818f16b6 100644 +--- a/string/string.h ++++ b/string/string.h +@@ -64,6 +64,22 @@ extern void *memset (void *__s, int __c, size_t __n) __THROW __nonnull ((1)); + extern int memcmp (const void *__s1, const void *__s2, size_t __n) + __THROW __attribute_pure__ __nonnull ((1, 2)); + ++/* Compare N bytes of S1 and S2. Return zero if S1 and S2 are equal. ++ Return some non-zero value otherwise. ++ ++ Essentially __memcmpeq has the exact same semantics as memcmp ++ except the return value is less constrained. memcmp is always a ++ correct implementation of __memcmpeq. As well !!memcmp, -memcmp, ++ or bcmp are correct implementations. ++ ++ __memcmpeq is meant to be used by compilers when memcmp return is ++ only used for its bolean value. ++ ++ __memcmpeq is declared only for use by compilers. Programs should ++ continue to use memcmp. */ ++extern int __memcmpeq (const void *__s1, const void *__s2, size_t __n) ++ __THROW __attribute_pure__ __nonnull ((1, 2)); ++ + /* Search N bytes of S for C. */ + #ifdef __CORRECT_ISO_CPP_STRING_H_PROTO + extern "C++" +diff --git a/string/tester.c b/string/tester.c +index 778160ae6ecd648e..605b3f00f97ae854 100644 +--- a/string/tester.c ++++ b/string/tester.c +@@ -1449,6 +1449,19 @@ test_bcmp (void) + check(bcmp("abc", "def", 0) == 0, 8); /* Zero count. */ + } + ++static void ++test_memcmpeq (void) ++{ ++ it = "__memcmpeq"; ++ check (__memcmpeq ("a", "a", 1) == 0, 1); /* Identity. */ ++ check (__memcmpeq ("abc", "abc", 3) == 0, 2); /* Multicharacter. */ ++ check (__memcmpeq ("abcd", "abce", 4) != 0, 3); /* Honestly unequal. */ ++ check (__memcmpeq ("abce", "abcd", 4) != 0, 4); ++ check (__memcmpeq ("alph", "beta", 4) != 0, 5); ++ check (__memcmpeq ("abce", "abcd", 3) == 0, 6); /* Count limited. */ ++ check (__memcmpeq ("abc", "def", 0) == 0, 8); /* Zero count. */ ++} ++ + static void + test_strerror (void) + { +@@ -1611,6 +1624,9 @@ main (void) + /* bcmp - somewhat like memcmp. */ + test_bcmp (); + ++ /* __memcmpeq - somewhat like memcmp. */ ++ test_memcmpeq (); ++ + /* strndup. */ + test_strndup (); + +diff --git a/sysdeps/aarch64/memcmp.S b/sysdeps/aarch64/memcmp.S +index c1937f6f5c103a6f..37f37b91914c518b 100644 +--- a/sysdeps/aarch64/memcmp.S ++++ b/sysdeps/aarch64/memcmp.S +@@ -177,4 +177,6 @@ L(ret_0): + END (memcmp) + #undef bcmp + weak_alias (memcmp, bcmp) ++#undef __memcmpeq ++strong_alias (memcmp, __memcmpeq) + libc_hidden_builtin_def (memcmp) +diff --git a/sysdeps/csky/abiv2/memcmp.S b/sysdeps/csky/abiv2/memcmp.S +index 1560387618799d0e..2a4ae577b024277d 100644 +--- a/sysdeps/csky/abiv2/memcmp.S ++++ b/sysdeps/csky/abiv2/memcmp.S +@@ -138,5 +138,6 @@ ENTRY (memcmp) + br .L_s1_aligned + END (memcmp) + weak_alias (memcmp, bcmp) ++strong_alias (memcmp, __memcmpeq) + libc_hidden_def (memcmp) + .weak memcmp +diff --git a/sysdeps/i386/i686/memcmp.S b/sysdeps/i386/i686/memcmp.S +index b26b124fada2048f..90266d904b52368a 100644 +--- a/sysdeps/i386/i686/memcmp.S ++++ b/sysdeps/i386/i686/memcmp.S +@@ -405,4 +405,6 @@ L(table_32bytes) : + + #undef bcmp + weak_alias (memcmp, bcmp) ++#undef __memcmpeq ++strong_alias (memcmp, __memcmpeq) + libc_hidden_builtin_def (memcmp) +diff --git a/sysdeps/i386/i686/multiarch/memcmp-ia32.S b/sysdeps/i386/i686/multiarch/memcmp-ia32.S +index 5f6658b89a0d1d77..a5b5c3d3491f2e5a 100644 +--- a/sysdeps/i386/i686/multiarch/memcmp-ia32.S ++++ b/sysdeps/i386/i686/multiarch/memcmp-ia32.S +@@ -30,6 +30,9 @@ + + # undef weak_alias + # define weak_alias(original, alias) ++ ++# undef strong_alias ++# define strong_alias(original, alias) + #endif + + #include +diff --git a/sysdeps/i386/i686/multiarch/memcmp.c b/sysdeps/i386/i686/multiarch/memcmp.c +index 6e058a885775135c..3b2815edbc4d9d54 100644 +--- a/sysdeps/i386/i686/multiarch/memcmp.c ++++ b/sysdeps/i386/i686/multiarch/memcmp.c +@@ -29,4 +29,5 @@ + libc_ifunc_redirected (__redirect_memcmp, memcmp, IFUNC_SELECTOR ()); + + weak_alias (memcmp, bcmp) ++strong_alias (memcmp, __memcmpeq) + #endif +diff --git a/sysdeps/i386/memcmp.S b/sysdeps/i386/memcmp.S +index 1f212b0f6de8cde9..02473c2c0c827ba2 100644 +--- a/sysdeps/i386/memcmp.S ++++ b/sysdeps/i386/memcmp.S +@@ -70,4 +70,6 @@ END (memcmp) + + #undef bcmp + weak_alias (memcmp, bcmp) ++#undef __memcmpeq ++strong_alias (memcmp, __memcmpeq) + libc_hidden_builtin_def (memcmp) +diff --git a/sysdeps/ia64/memcmp.S b/sysdeps/ia64/memcmp.S +index 98570f4e364464ec..e9c3b645c742d34b 100644 +--- a/sysdeps/ia64/memcmp.S ++++ b/sysdeps/ia64/memcmp.S +@@ -161,4 +161,5 @@ ENTRY(memcmp) + END(memcmp) + + weak_alias (memcmp, bcmp) ++strong_alias (memcmp, __memcmpeq) + libc_hidden_builtin_def (memcmp) +diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist +index c5da10a0cd0140e4..e849d6fa35456b4b 100644 +--- a/sysdeps/mach/hurd/i386/libc.abilist ++++ b/sysdeps/mach/hurd/i386/libc.abilist +@@ -2285,6 +2285,7 @@ GLIBC_2.34 res_send F + GLIBC_2.34 shm_open F + GLIBC_2.34 shm_unlink F + GLIBC_2.34 timespec_getres F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/powerpc/powerpc32/405/memcmp.S b/sysdeps/powerpc/powerpc32/405/memcmp.S +index 6a6a54d90f93e751..c2836040a7783fb2 100644 +--- a/sysdeps/powerpc/powerpc32/405/memcmp.S ++++ b/sysdeps/powerpc/powerpc32/405/memcmp.S +@@ -126,3 +126,4 @@ L(st2): + END (memcmp) + libc_hidden_builtin_def (memcmp) + weak_alias (memcmp,bcmp) ++strong_alias (memcmp, __memcmpeq) +diff --git a/sysdeps/powerpc/powerpc32/power4/memcmp.S b/sysdeps/powerpc/powerpc32/power4/memcmp.S +index 814d2f211d95992e..f58e34aba5c2d3dc 100644 +--- a/sysdeps/powerpc/powerpc32/power4/memcmp.S ++++ b/sysdeps/powerpc/powerpc32/power4/memcmp.S +@@ -1373,3 +1373,4 @@ END (memcmp) + + libc_hidden_builtin_def (memcmp) + weak_alias (memcmp, bcmp) ++strong_alias (memcmp, __memcmpeq) +diff --git a/sysdeps/powerpc/powerpc32/power4/multiarch/memcmp-power7.S b/sysdeps/powerpc/powerpc32/power4/multiarch/memcmp-power7.S +index 8a929b2b443a8aff..b17d0e43b77e532d 100644 +--- a/sysdeps/powerpc/powerpc32/power4/multiarch/memcmp-power7.S ++++ b/sysdeps/powerpc/powerpc32/power4/multiarch/memcmp-power7.S +@@ -38,4 +38,7 @@ + #undef weak_alias + #define weak_alias(a, b) + ++#undef strong_alias ++#define strong_alias(a, b) ++ + #include +diff --git a/sysdeps/powerpc/powerpc32/power4/multiarch/memcmp-ppc32.S b/sysdeps/powerpc/powerpc32/power4/multiarch/memcmp-ppc32.S +index 317523b7435b8553..893b6cac9cd7e0cc 100644 +--- a/sysdeps/powerpc/powerpc32/power4/multiarch/memcmp-ppc32.S ++++ b/sysdeps/powerpc/powerpc32/power4/multiarch/memcmp-ppc32.S +@@ -40,6 +40,10 @@ + # undef weak_alias + # define weak_alias(a, b) \ + .weak b ; b = __memcmp_ppc ++ ++# undef strong_alias ++# define strong_alias(a, b) \ ++ .globl b ; b = __memcmp_ppc + #endif + + #include +diff --git a/sysdeps/powerpc/powerpc32/power7/memcmp.S b/sysdeps/powerpc/powerpc32/power7/memcmp.S +index 8a19953e2d0e8dbc..f8deb4e32cfcce9f 100644 +--- a/sysdeps/powerpc/powerpc32/power7/memcmp.S ++++ b/sysdeps/powerpc/powerpc32/power7/memcmp.S +@@ -1373,3 +1373,4 @@ END (memcmp) + + libc_hidden_builtin_def (memcmp) + weak_alias (memcmp, bcmp) ++strong_alias (memcmp, __memcmpeq) +diff --git a/sysdeps/powerpc/powerpc64/le/power10/memcmp.S b/sysdeps/powerpc/powerpc64/le/power10/memcmp.S +index 52f244e7e77cbdf9..f81c73a29c7f65e5 100644 +--- a/sysdeps/powerpc/powerpc64/le/power10/memcmp.S ++++ b/sysdeps/powerpc/powerpc64/le/power10/memcmp.S +@@ -177,3 +177,4 @@ L(tail8): + END (MEMCMP) + libc_hidden_builtin_def (memcmp) + weak_alias (memcmp, bcmp) ++strong_alias (memcmp, __memcmpeq) +diff --git a/sysdeps/powerpc/powerpc64/multiarch/memcmp-power10.S b/sysdeps/powerpc/powerpc64/multiarch/memcmp-power10.S +index 73a0debd4a811d8e..22399f143d089b13 100644 +--- a/sysdeps/powerpc/powerpc64/multiarch/memcmp-power10.S ++++ b/sysdeps/powerpc/powerpc64/multiarch/memcmp-power10.S +@@ -22,5 +22,7 @@ + #define libc_hidden_builtin_def(name) + #undef weak_alias + #define weak_alias(name,alias) ++#undef strong_alias ++#define strong_alias(name,alias) + + #include +diff --git a/sysdeps/powerpc/powerpc64/multiarch/memcmp-power4.S b/sysdeps/powerpc/powerpc64/multiarch/memcmp-power4.S +index d2b6c2f934e38001..fe68912a3b347916 100644 +--- a/sysdeps/powerpc/powerpc64/multiarch/memcmp-power4.S ++++ b/sysdeps/powerpc/powerpc64/multiarch/memcmp-power4.S +@@ -22,5 +22,7 @@ + #define libc_hidden_builtin_def(name) + #undef weak_alias + #define weak_alias(name,alias) ++#undef strong_alias ++#define strong_alias(name,alias) + + #include +diff --git a/sysdeps/powerpc/powerpc64/multiarch/memcmp-power7.S b/sysdeps/powerpc/powerpc64/multiarch/memcmp-power7.S +index 8671e930f093cbdb..5739471a7d1a4f65 100644 +--- a/sysdeps/powerpc/powerpc64/multiarch/memcmp-power7.S ++++ b/sysdeps/powerpc/powerpc64/multiarch/memcmp-power7.S +@@ -22,5 +22,7 @@ + #define libc_hidden_builtin_def(name) + #undef weak_alias + #define weak_alias(name,alias) ++#undef strong_alias ++#define strong_alias(name,alias) + + #include +diff --git a/sysdeps/powerpc/powerpc64/multiarch/memcmp-power8.S b/sysdeps/powerpc/powerpc64/multiarch/memcmp-power8.S +index eb2273d468478add..e6a93e88c616961e 100644 +--- a/sysdeps/powerpc/powerpc64/multiarch/memcmp-power8.S ++++ b/sysdeps/powerpc/powerpc64/multiarch/memcmp-power8.S +@@ -22,5 +22,7 @@ + #define libc_hidden_builtin_def(name) + #undef weak_alias + #define weak_alias(name,alias) ++#undef strong_alias ++#define strong_alias(name,alias) + + #include +diff --git a/sysdeps/powerpc/powerpc64/multiarch/memcmp-ppc64.c b/sysdeps/powerpc/powerpc64/multiarch/memcmp-ppc64.c +index 1f9f219971fdbbd9..2bc5fa50d701c0c3 100644 +--- a/sysdeps/powerpc/powerpc64/multiarch/memcmp-ppc64.c ++++ b/sysdeps/powerpc/powerpc64/multiarch/memcmp-ppc64.c +@@ -22,6 +22,10 @@ + #define weak_alias(name, aliasname) \ + extern __typeof (__memcmp_ppc) aliasname \ + __attribute__ ((weak, alias ("__memcmp_ppc"))); ++#undef strong_alias ++#define strong_alias(name, aliasname) \ ++ extern __typeof (__memcmp_ppc) aliasname \ ++ __attribute__ ((alias ("__memcmp_ppc"))); + #if IS_IN (libc) && defined(SHARED) + # undef libc_hidden_builtin_def + # define libc_hidden_builtin_def(name) \ +diff --git a/sysdeps/powerpc/powerpc64/power4/memcmp.S b/sysdeps/powerpc/powerpc64/power4/memcmp.S +index dc1be3a0d8d80d35..cc82be115ff2bd8e 100644 +--- a/sysdeps/powerpc/powerpc64/power4/memcmp.S ++++ b/sysdeps/powerpc/powerpc64/power4/memcmp.S +@@ -1374,3 +1374,4 @@ L(duzeroLength): + END (MEMCMP) + libc_hidden_builtin_def (memcmp) + weak_alias (memcmp, bcmp) ++strong_alias (memcmp, __memcmpeq) +diff --git a/sysdeps/powerpc/powerpc64/power7/memcmp.S b/sysdeps/powerpc/powerpc64/power7/memcmp.S +index bc034a55bc18f520..3044f7ede95ca9bd 100644 +--- a/sysdeps/powerpc/powerpc64/power7/memcmp.S ++++ b/sysdeps/powerpc/powerpc64/power7/memcmp.S +@@ -1059,3 +1059,4 @@ L(duzeroLength): + END (MEMCMP) + libc_hidden_builtin_def (memcmp) + weak_alias (memcmp, bcmp) ++strong_alias (memcmp, __memcmpeq) +diff --git a/sysdeps/powerpc/powerpc64/power8/memcmp.S b/sysdeps/powerpc/powerpc64/power8/memcmp.S +index b676b09a9b33c643..0c6a154502719064 100644 +--- a/sysdeps/powerpc/powerpc64/power8/memcmp.S ++++ b/sysdeps/powerpc/powerpc64/power8/memcmp.S +@@ -1442,3 +1442,4 @@ L(duzeroLength): + END (MEMCMP) + libc_hidden_builtin_def (memcmp) + weak_alias (memcmp, bcmp) ++strong_alias (memcmp, __memcmpeq) +diff --git a/sysdeps/s390/memcmp-z900.S b/sysdeps/s390/memcmp-z900.S +index 995d52e47d713623..d625bf90dd7250ee 100644 +--- a/sysdeps/s390/memcmp-z900.S ++++ b/sysdeps/s390/memcmp-z900.S +@@ -164,6 +164,7 @@ END(MEMCMP_Z196) + Otherwise see sysdeps/s390/memcmp.c. */ + strong_alias (MEMCMP_DEFAULT, memcmp) + weak_alias (memcmp, bcmp) ++strong_alias (memcmp, __memcmpeq) + #endif + + #if defined SHARED && IS_IN (libc) +diff --git a/sysdeps/s390/memcmp.c b/sysdeps/s390/memcmp.c +index 0b4e9da71784fda3..52c20af77229d92f 100644 +--- a/sysdeps/s390/memcmp.c ++++ b/sysdeps/s390/memcmp.c +@@ -46,4 +46,5 @@ s390_libc_ifunc_expr (__redirect_memcmp, memcmp, + }) + ) + weak_alias (memcmp, bcmp); ++strong_alias (memcmp, __memcmpeq) + #endif +diff --git a/sysdeps/sparc/sparc64/memcmp.S b/sysdeps/sparc/sparc64/memcmp.S +index 0935d31fec61e0c4..ded0fab090763336 100644 +--- a/sysdeps/sparc/sparc64/memcmp.S ++++ b/sysdeps/sparc/sparc64/memcmp.S +@@ -139,4 +139,6 @@ END(memcmp) + + #undef bcmp + weak_alias (memcmp, bcmp) ++#undef __memcmpeq ++strong_alias (memcmp, __memcmpeq) + libc_hidden_builtin_def (memcmp) +diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist +index 21a2e50a884c3d64..f227ae6ceec97c73 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist +@@ -2612,3 +2612,4 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F +diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist +index a201fd69bacc3281..0ccc3fc73ecc0e4d 100644 +--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist ++++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist +@@ -2709,6 +2709,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist +index 261143693778ba9f..fd80704787f4ef41 100644 +--- a/sysdeps/unix/sysv/linux/arc/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arc/libc.abilist +@@ -2373,3 +2373,4 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F +diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist +index a426241965d56df9..2ae6c58b8ad6fc01 100644 +--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist +@@ -491,6 +491,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 + GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 +diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist +index 02f80418cc40ac06..fcfd1e8594d80aa1 100644 +--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist +@@ -488,6 +488,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 + GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 +diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist +index b7676eb372398daf..ba034b85414a2b55 100644 +--- a/sysdeps/unix/sysv/linux/csky/libc.abilist ++++ b/sysdeps/unix/sysv/linux/csky/libc.abilist +@@ -2647,3 +2647,4 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F +diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist +index f6965c9d9594910a..b7460bec8ace47c2 100644 +--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist ++++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist +@@ -2596,6 +2596,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist +index 2e7603d9ed6728e2..a4dc341dededdc3b 100644 +--- a/sysdeps/unix/sysv/linux/i386/libc.abilist ++++ b/sysdeps/unix/sysv/linux/i386/libc.abilist +@@ -2780,6 +2780,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist +index dd3a56d3fef14600..94b222dbc7ffbb81 100644 +--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist +@@ -2547,6 +2547,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +index c1e0ea9c102a69d1..12fd3b63103c3e6e 100644 +--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist ++++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +@@ -492,6 +492,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0x98 + GLIBC_2.4 _IO_2_1_stdin_ D 0x98 +diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +index 93161048ca26b91b..4d2296007ab1d922 100644 +--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist ++++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +@@ -2723,6 +2723,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +index 0aaeec8a2707da2a..a223278a3d4a33d8 100644 +--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +@@ -2696,3 +2696,4 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F +diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +index bec5f456c9756e83..780a4f5b0bf5518c 100644 +--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +@@ -2693,3 +2693,4 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F +diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +index 97d2127f7828312a..cd65136062a6a876 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +@@ -2688,6 +2688,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +index acb0756c11995d34..b5b9902db56a4c79 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +@@ -2686,6 +2686,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +index ebc21dde1eca0d6b..57593d5f94a184f1 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +@@ -2694,6 +2694,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +index c68f7e3c6cc8baa7..e944d76bed0bfe06 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +@@ -2598,6 +2598,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist +index e5b6834f147f1edc..8af5a3a90dfe4089 100644 +--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist ++++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist +@@ -2735,3 +2735,4 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +index 132707c8ad52832b..3a0213b39f8f7abd 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +@@ -2750,6 +2750,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +index 0af2be31a0f4ff91..f57df0234b8bdee3 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +@@ -2783,6 +2783,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +index cf864632d0cc3438..259a0cfc5126ca9e 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +@@ -2506,6 +2506,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +index d566d675d00c881b..126541daf152e1ad 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +@@ -2808,3 +2808,4 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F +diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +index c9a7eacb32ebe277..05df4d13d2c35ad1 100644 +--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +@@ -2375,3 +2375,4 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F +diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +index 8299131cb2ce932e..8e349cbff8cc0507 100644 +--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +@@ -2575,3 +2575,4 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F +diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +index c3fe78f77fd11c78..e9de402766af0d8a 100644 +--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +@@ -2748,6 +2748,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +index 83e542aa8c2563fa..1a010c745d78a07e 100644 +--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +@@ -2543,6 +2543,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist +index dc502f683336ad37..22ce530975944ff6 100644 +--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist +@@ -2603,6 +2603,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist +index cba1abb55621ca74..960df07b83bd2cbf 100644 +--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist +@@ -2600,6 +2600,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +index d4a516fb47518e12..eedb376f3dfeb8fb 100644 +--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +@@ -2743,6 +2743,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +index 6268875ba37ac0d4..86e0c92bef9255ab 100644 +--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +@@ -2570,6 +2570,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +index 095e914b73705601..5e59d90623c2bcba 100644 +--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +@@ -2521,6 +2521,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +index dd910f7fe934f260..94412dc134af283a 100644 +--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +@@ -2627,3 +2627,4 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __memcmpeq F +diff --git a/sysdeps/x86_64/memcmp.S b/sysdeps/x86_64/memcmp.S +index a9266494635ad2dd..1c217935edb18210 100644 +--- a/sysdeps/x86_64/memcmp.S ++++ b/sysdeps/x86_64/memcmp.S +@@ -359,4 +359,6 @@ END(memcmp) + + #undef bcmp + weak_alias (memcmp, bcmp) ++#undef __memcmpeq ++strong_alias (memcmp, __memcmpeq) + libc_hidden_builtin_def (memcmp) +diff --git a/sysdeps/x86_64/multiarch/memcmp-sse2.S b/sysdeps/x86_64/multiarch/memcmp-sse2.S +index b135fa2d4084b6d0..af737c5e6c65e9b2 100644 +--- a/sysdeps/x86_64/multiarch/memcmp-sse2.S ++++ b/sysdeps/x86_64/multiarch/memcmp-sse2.S +@@ -26,6 +26,9 @@ + + # undef weak_alias + # define weak_alias(ignored1, ignored2) ++ ++# undef strong_alias ++# define strong_alias(ignored1, ignored2) + #endif + + #include +diff --git a/sysdeps/x86_64/multiarch/memcmp.c b/sysdeps/x86_64/multiarch/memcmp.c +index fe725f35639793c2..4a3aad2c9c4a58fd 100644 +--- a/sysdeps/x86_64/multiarch/memcmp.c ++++ b/sysdeps/x86_64/multiarch/memcmp.c +@@ -29,6 +29,8 @@ + libc_ifunc_redirected (__redirect_memcmp, memcmp, IFUNC_SELECTOR ()); + # undef bcmp + weak_alias (memcmp, bcmp) ++# undef __memcmpeq ++strong_alias (memcmp, __memcmpeq) + + # ifdef SHARED + __hidden_ver1 (memcmp, __GI_memcmp, __redirect_memcmp) diff --git a/glibc-RHEL-93320-10.patch b/glibc-RHEL-93320-10.patch new file mode 100644 index 0000000..169f761 --- /dev/null +++ b/glibc-RHEL-93320-10.patch @@ -0,0 +1,35 @@ +commit 4a41fc3cd9cea9223ea4f13f9c766a1e149a0ccc +Author: Florian Weimer +Date: Wed Apr 13 14:18:28 2022 +0200 + + elf: Fix memory leak in _dl_find_object_update (bug 29062) + + The count can be zero if an object has already been loaded as + an indirect dependency (so that l_searchlist.r_list in its link + map is still NULL) is promoted to global scope via RTLD_GLOBAL. + + Fixes commit 5d28a8962dc ("elf: Add _dl_find_object function"). + +diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c +index 2952c651ff26db04..f4484e69c226a0a5 100644 +--- a/elf/dl-find_object.c ++++ b/elf/dl-find_object.c +@@ -788,6 +788,9 @@ _dl_find_object_update (struct link_map *new_map) + for (struct link_map *l = new_map; l != NULL; l = l->l_next) + /* Skip proxy maps and already-processed maps. */ + count += l == l->l_real && !l->l_find_object_processed; ++ if (count == 0) ++ return true; ++ + struct link_map **map_array = malloc (count * sizeof (*map_array)); + if (map_array == NULL) + return false; +@@ -797,8 +800,6 @@ _dl_find_object_update (struct link_map *new_map) + if (l == l->l_real && !l->l_find_object_processed) + map_array[i++] = l; + } +- if (count == 0) +- return true; + + _dl_find_object_link_map_sort (map_array, count); + bool ok = _dl_find_object_update_1 (map_array, count); diff --git a/glibc-RHEL-93320-11.patch b/glibc-RHEL-93320-11.patch new file mode 100644 index 0000000..9086a7b --- /dev/null +++ b/glibc-RHEL-93320-11.patch @@ -0,0 +1,55 @@ +commit 2a5b4f7a715921a232f67f6810268c6cd6aa0af2 +Author: Florian Weimer +Date: Fri Jul 8 12:08:48 2022 +0200 + + elf: Rename tst-audit26 to tst-audit28 + + tst-audit26 and tst-audit27 are already used by aarch64. + + Reviewed-by: Szabolcs Nagy + +(cherry picked from commit 90365b164b8061d891a640a3ae477a6500753291) + +diff --git a/elf/Makefile b/elf/Makefile +index e1e6107c277f5f76..7382cf6dd498ce8a 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -395,7 +395,7 @@ tests += \ + tst-audit24d \ + tst-audit25a \ + tst-audit25b \ +- tst-audit26 \ ++ tst-audit28 \ + tst-auditmany \ + tst-auxobj \ + tst-auxobj-dlopen \ +@@ -782,7 +782,7 @@ modules-names = \ + tst-auditmod24c \ + tst-auditmod24d \ + tst-auditmod25 \ +- tst-auditmod26 \ ++ tst-auditmod28 \ + tst-auxvalmod \ + tst-big-note-lib \ + tst-deep1mod1 \ +@@ -2347,9 +2347,9 @@ $(objpfx)tst-audit25b: $(objpfx)tst-audit25mod1.so \ + LDFLAGS-tst-audit25b = -Wl,-z,now + tst-audit25b-ARGS = -- $(host-test-program-cmd) + +-$(objpfx)tst-audit26.out: $(objpfx)tst-auditmod26.so +-$(objpfx)tst-auditmod26.so: $(libsupport) +-tst-audit26-ENV = LD_AUDIT=$(objpfx)tst-auditmod26.so ++$(objpfx)tst-audit28.out: $(objpfx)tst-auditmod28.so ++$(objpfx)tst-auditmod28.so: $(libsupport) ++tst-audit28-ENV = LD_AUDIT=$(objpfx)tst-auditmod28.so + + # tst-sonamemove links against an older implementation of the library. + LDFLAGS-tst-sonamemove-linkmod1.so = \ +diff --git a/elf/tst-audit26.c b/elf/tst-audit28.c +similarity index 100% +rename from elf/tst-audit26.c +rename to elf/tst-audit28.c +diff --git a/elf/tst-auditmod26.c b/elf/tst-auditmod28.c +similarity index 100% +rename from elf/tst-auditmod26.c +rename to elf/tst-auditmod28.c diff --git a/glibc-RHEL-93320-12.patch b/glibc-RHEL-93320-12.patch new file mode 100644 index 0000000..c170ab2 --- /dev/null +++ b/glibc-RHEL-93320-12.patch @@ -0,0 +1,51 @@ +commit 1bcfe0f732066ae5336b252295591ebe7e51c301 +Author: Florian Weimer +Date: Fri Jul 7 10:11:26 2023 +0200 + + elf: _dl_find_object may return 1 during early startup (bug 30515) + + Success is reported with a 0 return value, and failure is -1. + Enhance the kitchen sink test elf/tst-audit28 to cover + _dl_find_object as well. + + Fixes commit 5d28a8962dcb ("elf: Add _dl_find_object function") + and bug 30515. + + Reviewed-by: Carlos O'Donell + Tested-by: Carlos O'Donell + +diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c +index f4484e69c226a0a5..83a03fbd7c83c6be 100644 +--- a/elf/dl-find_object.c ++++ b/elf/dl-find_object.c +@@ -46,7 +46,7 @@ _dl_find_object_slow (void *pc, struct dl_find_object *result) + struct dl_find_object_internal internal; + _dl_find_object_from_map (l, &internal); + _dl_find_object_to_external (&internal, result); +- return 1; ++ return 0; + } + + /* Object not found. */ +diff --git a/elf/tst-auditmod28.c b/elf/tst-auditmod28.c +index db7ba95abec20f53..9e0a122c389535d0 100644 +--- a/elf/tst-auditmod28.c ++++ b/elf/tst-auditmod28.c +@@ -71,6 +71,17 @@ la_version (unsigned int current) + TEST_VERIFY (dladdr1 (&_exit, &info, &extra_info, RTLD_DL_LINKMAP) != 0); + TEST_VERIFY (extra_info == handle); + ++ /* Check _dl_find_object. */ ++ struct dl_find_object dlfo; ++ TEST_COMPARE (_dl_find_object (__builtin_return_address (0), &dlfo), 0); ++ /* "ld.so" is seen with --enable-hardcoded-path-in-tests. */ ++ if (strcmp (basename (dlfo.dlfo_link_map->l_name), "ld.so") != 0) ++ TEST_COMPARE_STRING (basename (dlfo.dlfo_link_map->l_name), LD_SO); ++ TEST_COMPARE (_dl_find_object (dlsym (handle, "environ"), &dlfo), 0); ++ TEST_COMPARE_STRING (basename (dlfo.dlfo_link_map->l_name), LIBC_SO); ++ TEST_COMPARE (_dl_find_object ((void *) 1, &dlfo), -1); ++ TEST_COMPARE (_dl_find_object ((void *) -1, &dlfo), -1); ++ + /* Verify that dlmopen creates a new namespace. */ + void *dlmopen_handle = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW); + TEST_VERIFY (dlmopen_handle != handle); diff --git a/glibc-RHEL-93320-13.patch b/glibc-RHEL-93320-13.patch new file mode 100644 index 0000000..bb81254 --- /dev/null +++ b/glibc-RHEL-93320-13.patch @@ -0,0 +1,58 @@ +commit 6c915c73d08028987232f6dc718f218c61113240 +Author: Aurelien Jarno +Date: Sun Nov 10 10:50:34 2024 +0100 + + elf: handle addition overflow in _dl_find_object_update_1 [BZ #32245] + + The remaining_to_add variable can be 0 if (current_used + count) wraps, + This is caught by GCC 14+ on hppa, which determines from there that + target_seg could be be NULL when remaining_to_add is zero, which in + turns causes a -Wstringop-overflow warning: + + In file included from ../include/atomic.h:49, + from dl-find_object.c:20: + In function '_dlfo_update_init_seg', + inlined from '_dl_find_object_update_1' at dl-find_object.c:689:30, + inlined from '_dl_find_object_update' at dl-find_object.c:805:13: + ../sysdeps/unix/sysv/linux/hppa/atomic-machine.h:44:4: error: '__atomic_store_4' writing 4 bytes into a region of size 0 overflows the destination [-Werror=stringop-overflow=] + 44 | __atomic_store_n ((mem), (val), __ATOMIC_RELAXED); \ + | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + dl-find_object.c:644:3: note: in expansion of macro 'atomic_store_relaxed' + 644 | atomic_store_relaxed (&seg->size, new_seg_size); + | ^~~~~~~~~~~~~~~~~~~~ + In function '_dl_find_object_update': + cc1: note: destination object is likely at address zero + + In practice, this is not possible as it represent counts of link maps. + Link maps have sizes larger than 1 byte, so the sum of any two link map + counts will always fit within a size_t without wrapping around. + + This patch therefore adds a check on remaining_to_add == 0 and tell GCC + that this can not happen using __builtin_unreachable. + + Thanks to Andreas Schwab for the investigation. + + Closes: BZ #32245 + Signed-off-by: Aurelien Jarno + Tested-by: John David Anglin + Reviewed-by: Florian Weimer + +diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c +index 83a03fbd7c83c6be..56894ca24a510b07 100644 +--- a/elf/dl-find_object.c ++++ b/elf/dl-find_object.c +@@ -661,6 +661,14 @@ _dl_find_object_update_1 (struct link_map **loaded, size_t count) + = _dlfo_loaded_mappings[!active_idx]; + size_t remaining_to_add = current_used + count; + ++ /* remaining_to_add can be 0 if (current_used + count) wraps, but in practice ++ this is not possible as it represent counts of link maps. Link maps have ++ sizes larger than 1 byte, so the sum of any two link map counts will ++ always fit within a size_t without wrapping around. This check ensures ++ that target_seg is not erroneously considered potentially NULL by GCC. */ ++ if (remaining_to_add == 0) ++ __builtin_unreachable (); ++ + /* Ensure that the new segment chain has enough space. */ + { + size_t new_allocated diff --git a/glibc-RHEL-93320-14.patch b/glibc-RHEL-93320-14.patch new file mode 100644 index 0000000..a14b8d3 --- /dev/null +++ b/glibc-RHEL-93320-14.patch @@ -0,0 +1,812 @@ +commit 5f3a7ebc358fdcbafcab4f1bf4067120fb519dfc +Author: Adhemerval Zanella +Date: Wed Jan 12 11:31:53 2022 -0300 + + Linux: Add epoll_pwait2 (BZ #27359) + + It is similar to epoll_wait, with the difference the timeout has + nanosecond resoluting by using struct timespec instead of int. + + Although Linux interface only provides 64 bit time_t support, old + 32 bit interface is also provided (so keep in sync with current + practice and to no force opt-in on 64 bit time_t). + + Checked on x86_64-linux-gnu and i686-linux-gnu. + + Reviewed-by: Florian Weimer + +Conflicts: + sysdeps/unix/sysv/linux/Makefile + Updated for layout change + sysdeps/unix/sysv/linux/or1k/libc.abilist + Omitted + +diff --git a/include/sys/epoll.h b/include/sys/epoll.h +index 86e0a54e62a5628b..8049381a269956ce 100644 +--- a/include/sys/epoll.h ++++ b/include/sys/epoll.h +@@ -4,6 +4,14 @@ + # ifndef _ISOMAC + + libc_hidden_proto (epoll_pwait) ++#if __TIMESIZE == 64 ++# define __epoll_pwait2_time64 epoll_pwait2 ++#else ++extern int __epoll_pwait2_time64 (int fd, struct epoll_event *ev, int maxev, ++ const struct __timespec64 *tmo, ++ const sigset_t *s); ++libc_hidden_proto (__epoll_pwait2_time64) ++#endif + + # endif /* !_ISOMAC */ + #endif +diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile +index 94eb2665b27371a4..61a40e31bacdbc22 100644 +--- a/sysdeps/unix/sysv/linux/Makefile ++++ b/sysdeps/unix/sysv/linux/Makefile +@@ -55,7 +55,7 @@ endif + + ifeq ($(subdir),misc) + sysdep_routines += adjtimex clone umount umount2 readahead sysctl \ +- setfsuid setfsgid epoll_pwait signalfd \ ++ setfsuid setfsgid epoll_pwait epoll_pwait2 signalfd \ + eventfd eventfd_read eventfd_write prlimit \ + personality epoll_wait tee vmsplice splice \ + open_by_handle_at mlock2 pkey_mprotect pkey_set pkey_get \ +@@ -131,6 +131,7 @@ tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \ + tst-clock_adjtime tst-adjtimex tst-ntp_adjtime tst-ntp_gettime \ + tst-ntp_gettimex tst-sigtimedwait tst-misalign-clone \ + tst-close_range \ ++ tst-epoll \ + tst-prctl \ + tst-scm_rights \ + tst-getauxval \ +@@ -160,6 +161,7 @@ endif + tests-time64 += \ + tst-adjtimex-time64 \ + tst-clock_adjtime-time64 \ ++ tst-epoll-time64 \ + tst-ntp_adjtime-time64 \ + tst-ntp_gettime-time64 \ + tst-ntp_gettimex-time64 \ +diff --git a/sysdeps/unix/sysv/linux/Versions b/sysdeps/unix/sysv/linux/Versions +index 3f8809a1581f27d0..ded087f30ea953fc 100644 +--- a/sysdeps/unix/sysv/linux/Versions ++++ b/sysdeps/unix/sysv/linux/Versions +@@ -293,6 +293,12 @@ libc { + %endif + close_range; + } ++ GLIBC_2.35 { ++%ifdef TIME64_NON_DEFAULT ++ __epoll_pwait2_time64; ++%endif ++ epoll_pwait2; ++ } + GLIBC_PRIVATE { + # functions used in other libraries + __syscall_rt_sigqueueinfo; +diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist +index fed942ed4b9ef469..c1a5ee90e6d4c63e 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist +@@ -2614,3 +2614,4 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F +diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist +index 28679327043ff0f2..1a30d0666bb76f41 100644 +--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist ++++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist +@@ -2711,6 +2711,7 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist +index 239db7bab0a18a87..e5dfdab357c72440 100644 +--- a/sysdeps/unix/sysv/linux/arc/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arc/libc.abilist +@@ -2375,3 +2375,4 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F +diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist +index bc79dcfe8ab4c723..4d3fd872788ba1f0 100644 +--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist +@@ -491,8 +491,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 + GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 +diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist +index 614607fd6baa8c6f..009dc9da14ee5eed 100644 +--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist +@@ -488,8 +488,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 + GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 +diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist +index 2b61543f0dd47034..df8da506cde36eeb 100644 +--- a/sysdeps/unix/sysv/linux/csky/libc.abilist ++++ b/sysdeps/unix/sysv/linux/csky/libc.abilist +@@ -2647,5 +2647,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F +diff --git a/sysdeps/unix/sysv/linux/epoll_pwait2.c b/sysdeps/unix/sysv/linux/epoll_pwait2.c +new file mode 100644 +index 0000000000000000..e38a1b2349edb040 +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/epoll_pwait2.c +@@ -0,0 +1,44 @@ ++/* Implementation of epoll_pwait2 syscall wrapper. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++ ++int ++__epoll_pwait2_time64 (int fd, struct epoll_event *ev, int maxev, ++ const struct __timespec64 *tmo, const sigset_t *s) ++{ ++ /* The syscall only supports 64-bit time_t. */ ++ return SYSCALL_CANCEL (epoll_pwait2, fd, ev, maxev, tmo, s, __NSIG_BYTES); ++} ++#if __TIMESIZE != 64 ++libc_hidden_def (__epoll_pwait2_time64) ++ ++int ++epoll_pwait2 (int fd, struct epoll_event *ev, int maxev, ++ const struct timespec *tmo, const sigset_t *s) ++{ ++ struct __timespec64 tmo64, *ptmo64 = NULL; ++ if (tmo != NULL) ++ { ++ tmo64 = valid_timespec_to_timespec64 (*tmo); ++ ptmo64 = &tmo64; ++ } ++ return __epoll_pwait2_time64 (fd, ev, maxev, ptmo64, s); ++} ++#endif +diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist +index 6b3cb1adb40c0a01..7ea1b017d05cc1fb 100644 +--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist ++++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist +@@ -2596,8 +2596,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist +index 7f608c1b64454bff..99ccf354b3d1a762 100644 +--- a/sysdeps/unix/sysv/linux/i386/libc.abilist ++++ b/sysdeps/unix/sysv/linux/i386/libc.abilist +@@ -2780,8 +2780,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist +index 865deec43f99a036..201542d1e76daa60 100644 +--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist +@@ -2549,6 +2549,7 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +index a172d746328ddeeb..32fd72a78df65bd8 100644 +--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist ++++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +@@ -492,8 +492,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0x98 + GLIBC_2.4 _IO_2_1_stdin_ D 0x98 +diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +index 174e9c7739f966ac..d26f0ae6c2475984 100644 +--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist ++++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +@@ -2723,8 +2723,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +index d042be1369b7d264..520ca0882d8d720b 100644 +--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +@@ -2696,5 +2696,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F +diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +index 332da62de23b88b8..9162c3139661b2c9 100644 +--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +@@ -2693,5 +2693,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F +diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +index 2d6ec0d0e8f3c797..656fdbdcaa70dad6 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +@@ -2688,8 +2688,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +index 6c5befa72bb5602b..5f0b90d318030622 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +@@ -2686,8 +2686,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +index 5fb24c97e1e6f05d..9f4891fc08d71521 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +@@ -2694,8 +2694,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +index f4f29fc15ee8f1a2..f1b0644bc33ba065 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +@@ -2600,6 +2600,7 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist +index 2e7300cd0512ad78..1cf88e38b943eeb9 100644 +--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist ++++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist +@@ -2735,5 +2735,7 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +index 129a2f16a7b731d3..9692335d106f9400 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +@@ -2750,8 +2750,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +index 7e232267791057a3..7da0ed59f2ccb080 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +@@ -2783,8 +2783,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +index 6f97392b7030ee95..72cf6851988a7b91 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +@@ -2508,6 +2508,7 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +index 29058a041a9cdba1..ee7f67f4d09ba7ae 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +@@ -2810,3 +2810,4 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F +diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +index d2924766d2f33039..b8c0854508ec8714 100644 +--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +@@ -2377,3 +2377,4 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F +diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +index b770e05da3ae0350..90f331fc0bc06278 100644 +--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +@@ -2577,3 +2577,4 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F +diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +index bed3433a2b75d3b4..ded5e3c0ce4da82c 100644 +--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +@@ -2748,8 +2748,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +index 4f1a143da504d98c..4b262992540d18d6 100644 +--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +@@ -2545,6 +2545,7 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist +index 92c8dec8ec28c54a..8bfd716fd238a35b 100644 +--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist +@@ -2603,8 +2603,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist +index 263da58cb7dada2c..47fd204d84ea2432 100644 +--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist +@@ -2600,8 +2600,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +index 0171efe7db109153..9b82f15109ca4b1a 100644 +--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +@@ -2743,8 +2743,10 @@ GLIBC_2.34 tss_create F + GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F ++GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +index 7f8d45f362e63584..94caf012a7e9cada 100644 +--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +@@ -2572,6 +2572,7 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/sys/epoll.h b/sysdeps/unix/sysv/linux/sys/epoll.h +index b6751f66971b82ca..5eb611da1bfd802f 100644 +--- a/sysdeps/unix/sysv/linux/sys/epoll.h ++++ b/sysdeps/unix/sysv/linux/sys/epoll.h +@@ -22,6 +22,7 @@ + #include + + #include ++#include + + /* Get the platform-dependent flags. */ + #include +@@ -133,6 +134,26 @@ extern int epoll_pwait (int __epfd, struct epoll_event *__events, + int __maxevents, int __timeout, + const __sigset_t *__ss); + ++/* Same as epoll_pwait, but the timeout as a timespec. ++ ++ This function is a cancellation point and therefore not marked with ++ __THROW. */ ++#ifndef __USE_TIME_BITS64 ++extern int epoll_pwait2 (int __epfd, struct epoll_event *__events, ++ int __maxevents, const struct timespec *__timeout, ++ const __sigset_t *__ss); ++#else ++# ifdef __REDIRECT ++extern int __REDIRECT (epoll_pwait2, (int __epfd, struct epoll_event *__ev, ++ int __maxevs, ++ const struct timespec *__timeout, ++ const __sigset_t *__ss), ++ __epoll_pwait2_time64); ++# else ++# define epoll_pwait2 __epoll_pwait2_time64 ++# endif ++#endif ++ + __END_DECLS + + #endif /* sys/epoll.h */ +diff --git a/sysdeps/unix/sysv/linux/tst-epoll-time64.c b/sysdeps/unix/sysv/linux/tst-epoll-time64.c +new file mode 100644 +index 0000000000000000..2e0236792e460ce0 +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/tst-epoll-time64.c +@@ -0,0 +1 @@ ++#include "tst-epoll.c" +diff --git a/sysdeps/unix/sysv/linux/tst-epoll.c b/sysdeps/unix/sysv/linux/tst-epoll.c +new file mode 100644 +index 0000000000000000..3ef6ca9fbd3c5a2b +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/tst-epoll.c +@@ -0,0 +1,211 @@ ++/* Basic tests for Linux epoll_* wrappers. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* The test focus on checking if the timeout argument is correctly handled ++ by glibc wrappers. */ ++ ++static void ++handler (int sig) ++{ ++} ++ ++typedef int (*epoll_wait_check_t) (int, struct epoll_event *, int, ++ int, const sigset_t *); ++ ++static void ++test_epoll_basic (epoll_wait_check_t epoll_wait_check) ++{ ++ { ++ sigset_t ss_usr1; ++ sigemptyset (&ss_usr1); ++ sigaddset (&ss_usr1, SIGUSR1); ++ TEST_COMPARE (sigprocmask (SIG_BLOCK, &ss_usr1, NULL), 0); ++ } ++ ++ int fds[2][2]; ++ xpipe (fds[0]); ++ xpipe (fds[1]); ++ ++ sigset_t ss; ++ TEST_COMPARE (sigprocmask (SIG_SETMASK, NULL, &ss), 0); ++ sigdelset (&ss, SIGUSR1); ++ ++ int efd = epoll_create1 (0); ++ TEST_VERIFY_EXIT (efd != -1); ++ ++ struct epoll_event event; ++ ++ event.data.fd = fds[1][0]; ++ event.events = EPOLLIN | EPOLLET; ++ TEST_COMPARE (epoll_ctl (efd, EPOLL_CTL_ADD, fds[1][0], &event), 0); ++ ++ pid_t parent = getpid (); ++ pid_t p = xfork (); ++ if (p == 0) ++ { ++ xclose (fds[0][1]); ++ xclose (fds[1][0]); ++ ++ event.data.fd = fds[0][0]; ++ event.events = EPOLLIN | EPOLLET; ++ TEST_COMPARE (epoll_ctl (efd, EPOLL_CTL_ADD, fds[0][0], &event), 0); ++ ++ int e; ++ do ++ { ++ if (getppid () != parent) ++ FAIL_EXIT1 ("getppid()=%d != parent=%d", getppid(), parent); ++ ++ errno = 0; ++ e = epoll_wait_check (efd, &event, 1, 500, &ss); ++ } ++ while (e == 0); ++ ++ TEST_COMPARE (e, -1); ++ TEST_COMPARE (errno, EINTR); ++ ++ TEMP_FAILURE_RETRY (write (fds[1][1], "foo", 3)); ++ ++ exit (0); ++ } ++ ++ xclose (fds[0][0]); ++ xclose (fds[1][1]); ++ ++ /* Wait some time so child is blocked on the syscall. */ ++ nanosleep (&(struct timespec) {0, 10000000}, NULL); ++ TEST_COMPARE (kill (p, SIGUSR1), 0); ++ ++ int e = epoll_wait_check (efd, &event, 1, 500000000, &ss); ++ TEST_COMPARE (e, 1); ++ TEST_VERIFY (event.events & EPOLLIN); ++ ++ xclose (fds[0][1]); ++ xclose (fds[1][0]); ++ xclose (efd); ++} ++ ++ ++static void ++test_epoll_large_timeout (epoll_wait_check_t epoll_wait_check) ++{ ++ timer_t t = support_create_timer (0, 100000000, true, NULL); ++ ++ int fds[2]; ++ xpipe (fds); ++ ++ fd_set rfds; ++ FD_ZERO (&rfds); ++ FD_SET (fds[0], &rfds); ++ ++ sigset_t ss; ++ TEST_COMPARE (sigprocmask (SIG_SETMASK, NULL, &ss), 0); ++ sigdelset (&ss, SIGALRM); ++ ++ int efd = epoll_create1 (0); ++ TEST_VERIFY_EXIT (efd != -1); ++ ++ struct epoll_event event; ++ event.data.fd = fds[0]; ++ event.events = EPOLLIN | EPOLLET; ++ TEST_COMPARE (epoll_ctl (efd, EPOLL_CTL_ADD, fds[0], &event), 0); ++ ++ int tmo = TYPE_MAXIMUM (int); ++ TEST_COMPARE (epoll_wait_check (efd, &event, 1, tmo, &ss), -1); ++ TEST_VERIFY (errno == EINTR || errno == EOVERFLOW); ++ ++ TEST_COMPARE (epoll_wait_check (efd, &event, 1, -1, &ss), -1); ++ TEST_VERIFY (errno == EINTR || errno == EOVERFLOW); ++ ++ support_delete_timer (t); ++ ++ xclose (fds[0]); ++ xclose (fds[1]); ++ xclose (efd); ++} ++ ++ ++static int ++epoll_wait_check (int epfd, struct epoll_event *ev, int maxev, int tmo, ++ const sigset_t *ss) ++{ ++ sigset_t orig; ++ TEST_COMPARE (sigprocmask (SIG_SETMASK, ss, &orig), 0); ++ int r = epoll_wait (epfd, ev, maxev, tmo); ++ TEST_COMPARE (sigprocmask (SIG_SETMASK, &orig, NULL), 0); ++ return r; ++} ++ ++static int ++epoll_pwait_check (int epfd, struct epoll_event *ev, int maxev, int tmo, ++ const sigset_t *ss) ++{ ++ return epoll_pwait (epfd, ev, maxev, tmo, ss); ++} ++ ++static int ++epoll_pwait2_check (int epfd, struct epoll_event *ev, int maxev, int tmo, ++ const sigset_t *ss) ++{ ++ time_t s = tmo == -1 ? TYPE_MAXIMUM (time_t) : tmo / 1000; ++ long int ns = tmo == -1 ? 0 : (tmo % 1000) * 1000000; ++ return epoll_pwait2 (epfd, ev, maxev, &(struct timespec) { s, ns }, ss); ++} ++ ++static int ++do_test (void) ++{ ++ { ++ struct sigaction sa; ++ sa.sa_handler = handler; ++ sa.sa_flags = 0; ++ sigemptyset (&sa.sa_mask); ++ xsigaction (SIGUSR1, &sa, NULL); ++ ++ sa.sa_handler = SIG_IGN; ++ xsigaction (SIGCHLD, &sa, NULL); ++ } ++ ++ int r = epoll_pwait2 (-1, NULL, 0, NULL, NULL); ++ TEST_COMPARE (r, -1); ++ bool pwait2_supported = errno != ENOSYS; ++ ++ test_epoll_basic (epoll_wait_check); ++ test_epoll_basic (epoll_pwait_check); ++ if (pwait2_supported) ++ test_epoll_basic (epoll_pwait2_check); ++ ++ test_epoll_large_timeout (epoll_wait_check); ++ test_epoll_large_timeout (epoll_pwait_check); ++ if (pwait2_supported) ++ test_epoll_large_timeout (epoll_pwait2_check); ++ ++ return 0; ++} ++ ++#include +diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +index c2f1a8ecc6d04dcf..140e9e8c1ce7dd37 100644 +--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +@@ -2523,6 +2523,7 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +index 8b43acf1005f1790..04d13ce27e3747f5 100644 +--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +@@ -2629,3 +2629,4 @@ GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 epoll_pwait2 F diff --git a/glibc-RHEL-93320-15.patch b/glibc-RHEL-93320-15.patch new file mode 100644 index 0000000..2cae01c --- /dev/null +++ b/glibc-RHEL-93320-15.patch @@ -0,0 +1,35 @@ +commit cc5372806a4bf34cb5c9038d1716b5ea6202abd0 +Author: Alejandro Colomar +Date: Wed May 31 22:44:22 2023 +0200 + + Fix invalid use of NULL in epoll_pwait2(2) test + + epoll_pwait2(2)'s second argument should be nonnull. We're going to add + __nonnull to the prototype, so let's fix the test accordingly. We can + use a dummy variable to avoid passing NULL. + + Reported-by: Adhemerval Zanella Netto + Signed-off-by: Alejandro Colomar + +diff --git a/sysdeps/unix/sysv/linux/tst-epoll.c b/sysdeps/unix/sysv/linux/tst-epoll.c +index 3ef6ca9fbd3c5a2b..c31d4793115acc7d 100644 +--- a/sysdeps/unix/sysv/linux/tst-epoll.c ++++ b/sysdeps/unix/sysv/linux/tst-epoll.c +@@ -180,6 +180,8 @@ epoll_pwait2_check (int epfd, struct epoll_event *ev, int maxev, int tmo, + static int + do_test (void) + { ++ struct epoll_event ev; ++ + { + struct sigaction sa; + sa.sa_handler = handler; +@@ -191,7 +193,7 @@ do_test (void) + xsigaction (SIGCHLD, &sa, NULL); + } + +- int r = epoll_pwait2 (-1, NULL, 0, NULL, NULL); ++ int r = epoll_pwait2 (-1, &ev, 0, NULL, NULL); + TEST_COMPARE (r, -1); + bool pwait2_supported = errno != ENOSYS; + diff --git a/glibc-RHEL-93320-16.patch b/glibc-RHEL-93320-16.patch new file mode 100644 index 0000000..3a1d7bb --- /dev/null +++ b/glibc-RHEL-93320-16.patch @@ -0,0 +1,891 @@ +commit 342cc934a3bf74ac618e2318d738f22ac93257ba +Author: Adhemerval Zanella +Date: Mon Jun 14 14:41:31 2021 -0300 + + posix: Add terminal control setting support for posix_spawn + + Currently there is no proper way to set the controlling terminal through + posix_spawn in race free manner [1]. This forces shell implementations + to keep using fork+exec when launching background process groups, + even when using posix_spawn yields better performance. + + This patch adds a new GNU extension so the creating process can + configure the created process terminal group. This is done with a new + flag, POSIX_SPAWN_TCSETPGROUP, along with two new attribute functions: + posix_spawnattr_tcsetpgrp_np, and posix_spawnattr_tcgetpgrp_np. + The function sets a new attribute, spawn-tcgroupfd, that references to + the controlling terminal. + + The controlling terminal is set after the spawn-pgroup attribute, and + uses the spawn-tcgroupfd along with current creating process group + (so it is composable with POSIX_SPAWN_SETPGROUP). + + To create a process and set the controlling terminal, one can use the + following sequence: + + posix_spawnattr_t attr; + posix_spawnattr_init (&attr); + posix_spawnattr_setflags (&attr, POSIX_SPAWN_TCSETPGROUP); + posix_spawnattr_tcsetpgrp_np (&attr, tcfd); + + If the idea is also to create a new process groups: + + posix_spawnattr_t attr; + posix_spawnattr_init (&attr); + posix_spawnattr_setflags (&attr, POSIX_SPAWN_TCSETPGROUP + | POSIX_SPAWN_SETPGROUP); + posix_spawnattr_tcsetpgrp_np (&attr, tcfd); + posix_spawnattr_setpgroup (&attr, 0); + + The controlling terminal file descriptor is ignored if the new flag is + not set. + + This interface is slight different than the one provided by QNX [2], + which only provides the POSIX_SPAWN_TCSETPGROUP flag. The QNX + documentation does not specify how the controlling terminal is obtained + nor how it iteracts with POSIX_SPAWN_SETPGROUP. Since a glibc + implementation is library based, it is more straightforward and avoid + requires additional file descriptor operations to request the caller + to setup the controlling terminal file descriptor (and it also allows + a bit less error handling by posix_spawn). + + Checked on x86_64-linux-gnu and i686-linux-gnu. + + [1] https://github.com/ksh93/ksh/issues/79 + [2] https://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.lib_ref/topic/p/posix_spawn.html + + Reviewed-by: Carlos O'Donell + Tested-by: Carlos O'Donell + +Conflicts: + posix/Makefile + Updated for layout change + sysdeps/unix/sysv/linux/or1k/libc.abilist + Omitted + +diff --git a/include/unistd.h b/include/unistd.h +index 5824485629793ccb..a5f6a5ca673a07cf 100644 +--- a/include/unistd.h ++++ b/include/unistd.h +@@ -173,6 +173,8 @@ extern int __truncate (const char *path, __off_t __length); + extern void *__sbrk (intptr_t __delta); + libc_hidden_proto (__sbrk) + ++extern int __tcsetpgrp (int fd, __pid_t pgrp); ++libc_hidden_proto (__tcsetpgrp) + + /* This variable is set nonzero at startup if the process's effective + IDs differ from its real IDs, or it is otherwise indicated that +diff --git a/posix/Makefile b/posix/Makefile +index 4c32a088a73723c7..8cd6963029585e3d 100644 +--- a/posix/Makefile ++++ b/posix/Makefile +@@ -64,6 +64,7 @@ routines := \ + spawnattr_getpgroup spawnattr_setpgroup spawn spawnp spawni \ + spawnattr_getsigmask spawnattr_getschedpolicy spawnattr_getschedparam \ + spawnattr_setsigmask spawnattr_setschedpolicy spawnattr_setschedparam \ ++ spawnattr_tcgetpgrp spawnattr_tcsetpgrp \ + posix_madvise \ + get_child_max sched_cpucount sched_cpualloc sched_cpufree \ + streams-compat \ +@@ -111,7 +112,7 @@ tests := test-errno tstgetopt testfnm runtests runptests \ + tst-sched_getaffinity \ + tst-cpuset-dynamic \ + tst-cpuset-static \ +- ++ tst-spawn6 + + # Test for the glob symbol version that was replaced in glibc 2.27. + ifeq ($(have-GLIBC_2.26)$(build-shared),yesyes) +@@ -292,6 +293,7 @@ tst-execvpe5-ARGS = -- $(host-test-program-cmd) + tst-spawn-ARGS = -- $(host-test-program-cmd) + tst-spawn-static-ARGS = $(tst-spawn-ARGS) + tst-spawn5-ARGS = -- $(host-test-program-cmd) ++tst-spawn6-ARGS = -- $(host-test-program-cmd) + tst-dir-ARGS = `pwd` `cd $(common-objdir)/$(subdir); pwd` `cd $(common-objdir); pwd` $(objpfx)tst-dir + tst-chmod-ARGS = $(objdir) + tst-vfork3-ARGS = --test-dir=$(objpfx) +diff --git a/posix/Versions b/posix/Versions +index a78792135fb6a707..e4f4f649b0f21e85 100644 +--- a/posix/Versions ++++ b/posix/Versions +@@ -156,6 +156,10 @@ libc { + execveat; + posix_spawn_file_actions_addclosefrom_np; + } ++ GLIBC_2.35 { ++ posix_spawnattr_tcgetpgrp_np; ++ posix_spawnattr_tcsetpgrp_np; ++ } + GLIBC_PRIVATE { + __libc_fork; __libc_pread; __libc_pwrite; + __nanosleep_nocancel; __pause_nocancel; +diff --git a/posix/spawn.h b/posix/spawn.h +index 990d8a6ba28855ae..742d4cb6257907b5 100644 +--- a/posix/spawn.h ++++ b/posix/spawn.h +@@ -34,7 +34,8 @@ typedef struct + sigset_t __ss; + struct sched_param __sp; + int __policy; +- int __pad[16]; ++ int __ctty_fd; ++ int __pad[15]; + } posix_spawnattr_t; + + +@@ -59,6 +60,7 @@ typedef struct + #ifdef __USE_GNU + # define POSIX_SPAWN_USEVFORK 0x40 + # define POSIX_SPAWN_SETSID 0x80 ++# define POSIX_SPAWN_TCSETPGROUP 0x100 + #endif + + +@@ -166,6 +168,18 @@ extern int posix_spawnattr_setschedparam (posix_spawnattr_t *__restrict __attr, + __restrict __schedparam) + __THROW __nonnull ((1, 2)); + ++#ifdef __USE_GNU ++/* Make the spawned process the foreground process group on the terminal ++ associated with FD (which must be a controlling terminal, and still be ++ associated with its session). */ ++extern int posix_spawnattr_tcsetpgrp_np (posix_spawnattr_t *__attr, int fd) ++ __THROW __nonnull ((1)); ++ ++/* Return the associated terminal FD in the attribute structure. */ ++extern int posix_spawnattr_tcgetpgrp_np (const posix_spawnattr_t * ++ __restrict __attr, int *fd) ++ __THROW __nonnull ((1, 2)); ++#endif + + /* Initialize data structure for file attribute for `spawn' call. */ + extern int posix_spawn_file_actions_init (posix_spawn_file_actions_t * +diff --git a/posix/spawnattr_setflags.c b/posix/spawnattr_setflags.c +index 2b033a50fc08180f..95f521d04d71aca2 100644 +--- a/posix/spawnattr_setflags.c ++++ b/posix/spawnattr_setflags.c +@@ -26,7 +26,8 @@ + | POSIX_SPAWN_SETSCHEDPARAM \ + | POSIX_SPAWN_SETSCHEDULER \ + | POSIX_SPAWN_SETSID \ +- | POSIX_SPAWN_USEVFORK) ++ | POSIX_SPAWN_USEVFORK \ ++ | POSIX_SPAWN_TCSETPGROUP) + + /* Store flags in the attribute structure. */ + int +diff --git a/posix/spawnattr_tcgetpgrp.c b/posix/spawnattr_tcgetpgrp.c +new file mode 100644 +index 0000000000000000..8db33e447498ce7d +--- /dev/null ++++ b/posix/spawnattr_tcgetpgrp.c +@@ -0,0 +1,26 @@ ++/* Get the controlling terminal option. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++ ++int ++posix_spawnattr_tcgetpgrp_np (const posix_spawnattr_t *attr, int *fd) ++{ ++ *fd = attr->__ctty_fd; ++ return 0; ++} +diff --git a/posix/spawnattr_tcsetpgrp.c b/posix/spawnattr_tcsetpgrp.c +new file mode 100644 +index 0000000000000000..c3b2ea2718e2a1f3 +--- /dev/null ++++ b/posix/spawnattr_tcsetpgrp.c +@@ -0,0 +1,26 @@ ++/* Set the controlling terminal option. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++ ++int ++posix_spawnattr_tcsetpgrp_np (posix_spawnattr_t *attr, int fd) ++{ ++ attr->__ctty_fd = fd; ++ return 0; ++} +diff --git a/posix/tst-spawn6.c b/posix/tst-spawn6.c +new file mode 100644 +index 0000000000000000..5f95bd1938a69552 +--- /dev/null ++++ b/posix/tst-spawn6.c +@@ -0,0 +1,175 @@ ++/* Check posix_spawn set controlling terminal extension. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static int ++handle_restart (const char *argv1) ++{ ++ int fd = xopen (_PATH_TTY, O_RDONLY, 0600); ++ ++ /* If process group is not changed (POSIX_SPAWN_SETPGROUP), then check ++ the creating process one, otherwise check against the process group ++ itself. */ ++ pid_t pgrp; ++ if (strcmp (argv1, "setgrpr") != 0) ++ TEST_COMPARE (sscanf (argv1, "%d", &pgrp), 1); ++ else ++ { ++ pgrp = getpgrp (); ++ /* Check if a new process group was actually created. */ ++ pid_t ppid = getppid (); ++ pid_t pgid = getpgid (ppid); ++ TEST_VERIFY (pgid != pgrp); ++ } ++ ++ TEST_COMPARE (tcgetpgrp (fd), pgrp); ++ ++ xclose (fd); ++ return 0; ++} ++ ++static int restart; ++#define CMDLINE_OPTIONS \ ++ { "restart", no_argument, &restart, 1 }, ++ ++static void ++run_subprogram (int argc, char *argv[], const posix_spawnattr_t *attr, ++ int exp_err) ++{ ++ short int flags; ++ TEST_COMPARE (posix_spawnattr_getflags (attr, &flags), 0); ++ bool setpgrp = flags & POSIX_SPAWN_SETPGROUP; ++ ++ char *spargv[9]; ++ char pgrp[INT_STRLEN_BOUND (pid_t)]; ++ ++ int i = 0; ++ for (; i < argc - 1; i++) ++ spargv[i] = argv[i + 1]; ++ spargv[i++] = (char *) "--direct"; ++ spargv[i++] = (char *) "--restart"; ++ if (setpgrp) ++ spargv[i++] = (char *) "setgrpr"; ++ else ++ { ++ snprintf (pgrp, sizeof pgrp, "%d", getpgrp ()); ++ spargv[i++] = pgrp; ++ } ++ spargv[i] = NULL; ++ TEST_VERIFY_EXIT (i < array_length (spargv)); ++ ++ pid_t pid; ++ TEST_COMPARE (posix_spawn (&pid, argv[1], NULL, attr, spargv, environ), ++ exp_err); ++ if (exp_err != 0) ++ return; ++ ++ int status; ++ TEST_COMPARE (xwaitpid (pid, &status, WUNTRACED), pid); ++ TEST_VERIFY (WIFEXITED (status)); ++ TEST_VERIFY (!WIFSTOPPED (status)); ++ TEST_VERIFY (!WIFSIGNALED (status)); ++ TEST_COMPARE (WEXITSTATUS (status), 0); ++} ++ ++static int ++do_test (int argc, char *argv[]) ++{ ++ /* We must have either: ++ - four parameters left if called initially: ++ + path to ld.so optional ++ + "--library-path" optional ++ + the library path optional ++ + the application name ++ - six parameters left if called through re-execution: ++ + --setgrpr optional ++ */ ++ ++ if (restart) ++ return handle_restart (argv[1]); ++ ++ int tcfd = xopen (_PATH_TTY, O_RDONLY, 0600); ++ ++ /* Check getters and setters. */ ++ { ++ posix_spawnattr_t attr; ++ TEST_COMPARE (posix_spawnattr_init (&attr), 0); ++ TEST_COMPARE (posix_spawnattr_tcsetpgrp_np (&attr, tcfd), 0); ++ ++ int fd; ++ TEST_COMPARE (posix_spawnattr_tcgetpgrp_np (&attr, &fd), 0); ++ TEST_COMPARE (tcfd, fd); ++ } ++ ++ /* Check setting the controlling terminal without changing the group. */ ++ { ++ posix_spawnattr_t attr; ++ TEST_COMPARE (posix_spawnattr_init (&attr), 0); ++ TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_TCSETPGROUP), ++ 0); ++ TEST_COMPARE (posix_spawnattr_tcsetpgrp_np (&attr, tcfd), 0); ++ ++ run_subprogram (argc, argv, &attr, 0); ++ } ++ ++ /* Check setting both the controlling terminal and the create a new process ++ group. */ ++ { ++ posix_spawnattr_t attr; ++ TEST_COMPARE (posix_spawnattr_init (&attr), 0); ++ TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_TCSETPGROUP ++ | POSIX_SPAWN_SETPGROUP), ++ 0); ++ TEST_COMPARE (posix_spawnattr_setpgroup (&attr, 0), 0); ++ TEST_COMPARE (posix_spawnattr_tcsetpgrp_np (&attr, tcfd), 0); ++ ++ run_subprogram (argc, argv, &attr, 0); ++ } ++ ++ /* Trying to set the controlling terminal after a setsid incurs in a ENOTTY ++ from tcsetpgrp. */ ++ { ++ posix_spawnattr_t attr; ++ TEST_COMPARE (posix_spawnattr_init (&attr), 0); ++ TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_TCSETPGROUP ++ | POSIX_SPAWN_SETSID), 0); ++ TEST_COMPARE (posix_spawnattr_tcsetpgrp_np (&attr, tcfd), 0); ++ ++ run_subprogram (argc, argv, &attr, ENOTTY); ++ } ++ ++ xclose (tcfd); ++ ++ return 0; ++} ++ ++#define TEST_FUNCTION_ARGV do_test ++#include +diff --git a/sysdeps/mach/hurd/spawni.c b/sysdeps/mach/hurd/spawni.c +index 060e389bbbd2d1de..4d71181643c19b0d 100644 +--- a/sysdeps/mach/hurd/spawni.c ++++ b/sysdeps/mach/hurd/spawni.c +@@ -390,6 +390,19 @@ retry: + if (!err && (flags & POSIX_SPAWN_SETPGROUP) != 0) + err = __proc_setpgrp (proc, new_pid, attrp->__pgrp); + ++ /* Set the controlling terminal. */ ++ if (!err && (flags & POSIX_SPAWN_TCSETPGROUP) != 0) ++ { ++ pid_t pgrp; ++ /* Check if it is possible to avoid an extra syscall. */ ++ if ((attrp->__flags & POSIX_SPAWN_SETPGROUP) != 0 && attrp->__pgrp != 0) ++ pgrp = attrp->__pgrp; ++ else ++ err = __proc_getpgrp (proc, new_pid, &pgrp); ++ if (!err) ++ err = __tcsetpgrp (attrp->__ctty_fd, pgrp); ++ } ++ + /* Set the effective user and group IDs. */ + if (!err && (flags & POSIX_SPAWN_RESETIDS) != 0) + { +diff --git a/sysdeps/unix/bsd/tcsetpgrp.c b/sysdeps/unix/bsd/tcsetpgrp.c +index 98c88db3ae879a17..3930b4f674172195 100644 +--- a/sysdeps/unix/bsd/tcsetpgrp.c ++++ b/sysdeps/unix/bsd/tcsetpgrp.c +@@ -22,7 +22,9 @@ + + /* Set the foreground process group ID of FD set PGRP_ID. */ + int +-tcsetpgrp (int fd, pid_t pgrp_id) ++__tcsetpgrp (int fd, pid_t pgrp_id) + { + return __ioctl (fd, TIOCSPGRP, &pgrp_id); + } ++weak_alias (__tcsetpgrp, tcsetpgrp) ++libc_hidden_def (__tcsetpgrp) +diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist +index c1a5ee90e6d4c63e..9dd574d9e2a7b541 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist +@@ -2615,3 +2615,5 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist +index 1a30d0666bb76f41..f66704877eb43052 100644 +--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist ++++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist +@@ -2712,6 +2712,8 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist +index e5dfdab357c72440..97aa3da1ad15a459 100644 +--- a/sysdeps/unix/sysv/linux/arc/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arc/libc.abilist +@@ -2376,3 +2376,5 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist +index 4d3fd872788ba1f0..18f4364856980df6 100644 +--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist +@@ -495,6 +495,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 + GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 +diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist +index 009dc9da14ee5eed..2c12c020b1f2f320 100644 +--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist +@@ -492,6 +492,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 + GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 +diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist +index df8da506cde36eeb..7f28516feb9b3ce0 100644 +--- a/sysdeps/unix/sysv/linux/csky/libc.abilist ++++ b/sysdeps/unix/sysv/linux/csky/libc.abilist +@@ -2651,3 +2651,5 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist +index 7ea1b017d05cc1fb..9776f20763a46963 100644 +--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist ++++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist +@@ -2600,6 +2600,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist +index 99ccf354b3d1a762..96b50d0a9bd68a19 100644 +--- a/sysdeps/unix/sysv/linux/i386/libc.abilist ++++ b/sysdeps/unix/sysv/linux/i386/libc.abilist +@@ -2784,6 +2784,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist +index 201542d1e76daa60..9b2eebfbf1b91f06 100644 +--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist +@@ -2550,6 +2550,8 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +index 32fd72a78df65bd8..71cd35488e3b60ab 100644 +--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist ++++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +@@ -496,6 +496,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0x98 + GLIBC_2.4 _IO_2_1_stdin_ D 0x98 +diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +index d26f0ae6c2475984..ced01a501dd816a6 100644 +--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist ++++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +@@ -2727,6 +2727,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +index 520ca0882d8d720b..5406c01f1de62803 100644 +--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +@@ -2700,3 +2700,5 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +index 9162c3139661b2c9..53b8ade4c3c16c9f 100644 +--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +@@ -2697,3 +2697,5 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +index 656fdbdcaa70dad6..919973ea46866c8a 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +@@ -2692,6 +2692,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +index 5f0b90d318030622..cf5a8dc1208d1f45 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +@@ -2690,6 +2690,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +index 9f4891fc08d71521..003c3bd0a6bb0595 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +@@ -2698,6 +2698,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +index f1b0644bc33ba065..73629c2f21aed4a3 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +@@ -2601,6 +2601,8 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist +index 1cf88e38b943eeb9..9e8645ebc0a996a7 100644 +--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist ++++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist +@@ -2739,3 +2739,5 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +index 9692335d106f9400..3d1ba9887c327899 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +@@ -2754,6 +2754,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +index 7da0ed59f2ccb080..d979a3b93b98cc5a 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +@@ -2787,6 +2787,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +index 72cf6851988a7b91..44688e52cf99e9fc 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +@@ -2509,6 +2509,8 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +index ee7f67f4d09ba7ae..40682711eb73fb12 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +@@ -2811,3 +2811,5 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +index b8c0854508ec8714..e239d626b32e03ba 100644 +--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +@@ -2378,3 +2378,5 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +index 90f331fc0bc06278..ab0c4e70927d3dc0 100644 +--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +@@ -2578,3 +2578,5 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +index ded5e3c0ce4da82c..74e3a4651f215344 100644 +--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +@@ -2752,6 +2752,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +index 4b262992540d18d6..e5553f06b205f0cf 100644 +--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +@@ -2546,6 +2546,8 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist +index 8bfd716fd238a35b..9662041cd4c5df83 100644 +--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist +@@ -2607,6 +2607,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist +index 47fd204d84ea2432..bf90e924a671f1c1 100644 +--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist +@@ -2604,6 +2604,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +index 9b82f15109ca4b1a..ddb0d0621f80426b 100644 +--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +@@ -2747,6 +2747,8 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +index 94caf012a7e9cada..ca14224cb7d5740d 100644 +--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +@@ -2573,6 +2573,8 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c +index 6b0bade4d41eafd5..601850638d538ae0 100644 +--- a/sysdeps/unix/sysv/linux/spawni.c ++++ b/sysdeps/unix/sysv/linux/spawni.c +@@ -164,6 +164,17 @@ __spawni_child (void *arguments) + && __setpgid (0, attr->__pgrp) != 0) + goto fail; + ++ /* Set the controlling terminal. */ ++ if ((attr->__flags & POSIX_SPAWN_TCSETPGROUP) != 0) ++ { ++ /* Check if it is possible to avoid an extra syscall. */ ++ pid_t pgrp = (attr->__flags & POSIX_SPAWN_SETPGROUP) != 0 ++ && attr->__pgrp != 0 ++ ? attr->__pgrp : __getpgid (0); ++ if (__tcsetpgrp (attr->__ctty_fd, pgrp) != 0) ++ goto fail; ++ } ++ + /* Set the effective user and group IDs. */ + if ((attr->__flags & POSIX_SPAWN_RESETIDS) != 0 + && (local_seteuid (__getuid ()) != 0 +diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +index 140e9e8c1ce7dd37..661d928adf2ac13c 100644 +--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +@@ -2524,6 +2524,8 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +index 04d13ce27e3747f5..bb8058dfa4144bbc 100644 +--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +@@ -2630,3 +2630,5 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F ++GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F ++GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F +diff --git a/termios/tcsetpgrp.c b/termios/tcsetpgrp.c +index 05630cd04cb5f83a..9bd94a70bc9e879f 100644 +--- a/termios/tcsetpgrp.c ++++ b/termios/tcsetpgrp.c +@@ -21,7 +21,7 @@ + + /* Set the foreground process group ID of FD set PGRP_ID. */ + int +-tcsetpgrp (int fd, pid_t pgrp_id) ++__tcsetpgrp (int fd, pid_t pgrp_id) + { + if (fd < 0) + { +@@ -32,6 +32,7 @@ tcsetpgrp (int fd, pid_t pgrp_id) + __set_errno (ENOSYS); + return -1; + } +- ++weak_alias (__tcsetpgrp, tcsetpgrp); ++libc_hidden_def (__tcsetpgrp) + + stub_warning (tcsetpgrp) diff --git a/glibc-RHEL-93320-17.patch b/glibc-RHEL-93320-17.patch new file mode 100644 index 0000000..30116b3 --- /dev/null +++ b/glibc-RHEL-93320-17.patch @@ -0,0 +1,963 @@ +commit 6289d28d3c4e56f34830cfb011c31271ef850418 +Author: Adhemerval Zanella +Date: Thu Jan 27 10:11:30 2022 -0300 + + posix: Replace posix_spawnattr_tc{get,set}pgrp_np with posix_spawn_file_actions_addtcsetpgrp_np + + The posix_spawnattr_tcsetpgrp_np works on a file descriptor (the + controlling terminal), so it would make more sense to actually fit + it on the file actions API. + + Also, POSIX_SPAWN_TCSETPGROUP is not really required since it is + implicit by the presence of tcsetpgrp file action. + + The posix/tst-spawn6.c is also fixed when TTY can is not present. + + Checked on x86_64-linux-gnu and i686-linux-gnu. + + Reviewed-by: Carlos O'Donell + Tested-by: Carlos O'Donell + +Conflicts: + sysdeps/mach/hurd/i386/libc.abilist + Updated for minor context changes + sysdeps/unix/sysv/linux/or1k/libc.abilist + Omitted + +diff --git a/posix/Makefile b/posix/Makefile +index 8cd6963029585e3d..7b70b4a736bc1215 100644 +--- a/posix/Makefile ++++ b/posix/Makefile +@@ -58,13 +58,13 @@ routines := \ + spawn_faction_addopen spawn_faction_adddup2 spawn_valid_fd \ + spawn_faction_addchdir spawn_faction_addfchdir \ + spawn_faction_addclosefrom \ ++ spawn_faction_addtcsetpgrp_np \ + spawnattr_init spawnattr_destroy \ + spawnattr_getdefault spawnattr_setdefault \ + spawnattr_getflags spawnattr_setflags \ + spawnattr_getpgroup spawnattr_setpgroup spawn spawnp spawni \ + spawnattr_getsigmask spawnattr_getschedpolicy spawnattr_getschedparam \ + spawnattr_setsigmask spawnattr_setschedpolicy spawnattr_setschedparam \ +- spawnattr_tcgetpgrp spawnattr_tcsetpgrp \ + posix_madvise \ + get_child_max sched_cpucount sched_cpualloc sched_cpufree \ + streams-compat \ +diff --git a/posix/Versions b/posix/Versions +index e4f4f649b0f21e85..3753810864c8ba97 100644 +--- a/posix/Versions ++++ b/posix/Versions +@@ -157,8 +157,7 @@ libc { + posix_spawn_file_actions_addclosefrom_np; + } + GLIBC_2.35 { +- posix_spawnattr_tcgetpgrp_np; +- posix_spawnattr_tcsetpgrp_np; ++ posix_spawn_file_actions_addtcsetpgrp_np; + } + GLIBC_PRIVATE { + __libc_fork; __libc_pread; __libc_pwrite; +diff --git a/posix/spawn.h b/posix/spawn.h +index 742d4cb6257907b5..7cf1a5b628480404 100644 +--- a/posix/spawn.h ++++ b/posix/spawn.h +@@ -34,8 +34,7 @@ typedef struct + sigset_t __ss; + struct sched_param __sp; + int __policy; +- int __ctty_fd; +- int __pad[15]; ++ int __pad[16]; + } posix_spawnattr_t; + + +@@ -60,7 +59,6 @@ typedef struct + #ifdef __USE_GNU + # define POSIX_SPAWN_USEVFORK 0x40 + # define POSIX_SPAWN_SETSID 0x80 +-# define POSIX_SPAWN_TCSETPGROUP 0x100 + #endif + + +@@ -168,19 +166,6 @@ extern int posix_spawnattr_setschedparam (posix_spawnattr_t *__restrict __attr, + __restrict __schedparam) + __THROW __nonnull ((1, 2)); + +-#ifdef __USE_GNU +-/* Make the spawned process the foreground process group on the terminal +- associated with FD (which must be a controlling terminal, and still be +- associated with its session). */ +-extern int posix_spawnattr_tcsetpgrp_np (posix_spawnattr_t *__attr, int fd) +- __THROW __nonnull ((1)); +- +-/* Return the associated terminal FD in the attribute structure. */ +-extern int posix_spawnattr_tcgetpgrp_np (const posix_spawnattr_t * +- __restrict __attr, int *fd) +- __THROW __nonnull ((1, 2)); +-#endif +- + /* Initialize data structure for file attribute for `spawn' call. */ + extern int posix_spawn_file_actions_init (posix_spawn_file_actions_t * + __file_actions) +@@ -235,6 +220,13 @@ posix_spawn_file_actions_addclosefrom_np (posix_spawn_file_actions_t *, + int __from) + __THROW __nonnull ((1)); + ++/* Add an action to set the process group of the forground process group ++ associated with the terminal TCFD. */ ++extern int ++posix_spawn_file_actions_addtcsetpgrp_np (posix_spawn_file_actions_t *, ++ int __tcfd) ++ __THROW __nonnull ((1)); ++ + #endif + + __END_DECLS +diff --git a/posix/spawn_faction_addtcsetpgrp_np.c b/posix/spawn_faction_addtcsetpgrp_np.c +new file mode 100644 +index 0000000000000000..86f882cd84128f99 +--- /dev/null ++++ b/posix/spawn_faction_addtcsetpgrp_np.c +@@ -0,0 +1,50 @@ ++/* Add tcsetpgrp to the file action list for posix_spawn. ++ Copyright (C) 2022 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++int ++__posix_spawn_file_actions_addtcsetpgrp_np (posix_spawn_file_actions_t ++ *file_actions, int tcfd) ++{ ++ struct __spawn_action *rec; ++ ++ if (!__spawn_valid_fd (tcfd)) ++ return EBADF; ++ ++ /* Allocate more memory if needed. */ ++ if (file_actions->__used == file_actions->__allocated ++ && __posix_spawn_file_actions_realloc (file_actions) != 0) ++ /* This can only mean we ran out of memory. */ ++ return ENOMEM; ++ ++ /* Add the new value. */ ++ rec = &file_actions->__actions[file_actions->__used]; ++ rec->tag = spawn_do_tcsetpgrp; ++ rec->action.setpgrp_action.fd = tcfd; ++ ++ /* Account for the new entry. */ ++ ++file_actions->__used; ++ ++ return 0; ++} ++weak_alias (__posix_spawn_file_actions_addtcsetpgrp_np, ++ posix_spawn_file_actions_addtcsetpgrp_np) +diff --git a/posix/spawn_faction_destroy.c b/posix/spawn_faction_destroy.c +index 1a01b8e80ec58c85..6eddfbe91705c9cb 100644 +--- a/posix/spawn_faction_destroy.c ++++ b/posix/spawn_faction_destroy.c +@@ -40,6 +40,7 @@ __posix_spawn_file_actions_destroy (posix_spawn_file_actions_t *file_actions) + case spawn_do_dup2: + case spawn_do_fchdir: + case spawn_do_closefrom: ++ case spawn_do_tcsetpgrp: + /* No cleanup required. */ + break; + } +diff --git a/posix/spawn_int.h b/posix/spawn_int.h +index 81d43f2fa304e08b..a80fd46c1fe899cb 100644 +--- a/posix/spawn_int.h ++++ b/posix/spawn_int.h +@@ -34,6 +34,7 @@ struct __spawn_action + spawn_do_chdir, + spawn_do_fchdir, + spawn_do_closefrom, ++ spawn_do_tcsetpgrp + } tag; + + union +@@ -66,6 +67,10 @@ struct __spawn_action + { + int from; + } closefrom_action; ++ struct ++ { ++ int fd; ++ } setpgrp_action; + } action; + }; + +diff --git a/posix/spawnattr_setflags.c b/posix/spawnattr_setflags.c +index 95f521d04d71aca2..2b033a50fc08180f 100644 +--- a/posix/spawnattr_setflags.c ++++ b/posix/spawnattr_setflags.c +@@ -26,8 +26,7 @@ + | POSIX_SPAWN_SETSCHEDPARAM \ + | POSIX_SPAWN_SETSCHEDULER \ + | POSIX_SPAWN_SETSID \ +- | POSIX_SPAWN_USEVFORK \ +- | POSIX_SPAWN_TCSETPGROUP) ++ | POSIX_SPAWN_USEVFORK) + + /* Store flags in the attribute structure. */ + int +diff --git a/posix/spawnattr_tcgetpgrp.c b/posix/spawnattr_tcgetpgrp.c +deleted file mode 100644 +index 8db33e447498ce7d..0000000000000000 +--- a/posix/spawnattr_tcgetpgrp.c ++++ /dev/null +@@ -1,26 +0,0 @@ +-/* Get the controlling terminal option. +- Copyright (C) 2022 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 +- . */ +- +-#include +- +-int +-posix_spawnattr_tcgetpgrp_np (const posix_spawnattr_t *attr, int *fd) +-{ +- *fd = attr->__ctty_fd; +- return 0; +-} +diff --git a/posix/spawnattr_tcsetpgrp.c b/posix/spawnattr_tcsetpgrp.c +deleted file mode 100644 +index c3b2ea2718e2a1f3..0000000000000000 +--- a/posix/spawnattr_tcsetpgrp.c ++++ /dev/null +@@ -1,26 +0,0 @@ +-/* Set the controlling terminal option. +- Copyright (C) 2022 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 +- . */ +- +-#include +- +-int +-posix_spawnattr_tcsetpgrp_np (posix_spawnattr_t *attr, int fd) +-{ +- attr->__ctty_fd = fd; +- return 0; +-} +diff --git a/posix/tst-spawn6.c b/posix/tst-spawn6.c +index 5f95bd1938a69552..911e90a461bfc401 100644 +--- a/posix/tst-spawn6.c ++++ b/posix/tst-spawn6.c +@@ -29,12 +29,11 @@ + #include + #include + #include ++#include + + static int +-handle_restart (const char *argv1) ++handle_restart (const char *argv1, const char *argv2) + { +- int fd = xopen (_PATH_TTY, O_RDONLY, 0600); +- + /* If process group is not changed (POSIX_SPAWN_SETPGROUP), then check + the creating process one, otherwise check against the process group + itself. */ +@@ -50,9 +49,20 @@ handle_restart (const char *argv1) + TEST_VERIFY (pgid != pgrp); + } + +- TEST_COMPARE (tcgetpgrp (fd), pgrp); ++ char *endptr; ++ long int tcfd = strtol (argv2, &endptr, 10); ++ if (*endptr != '\0' || tcfd > INT_MAX) ++ FAIL_EXIT1 ("invalid file descriptor name: %s", argv2); ++ if (tcfd != -1) ++ { ++ TEST_COMPARE (fcntl (tcfd, F_GETFD), -1); ++ TEST_COMPARE (errno, EBADF); ++ } + ++ int fd = xopen (_PATH_TTY, O_RDONLY, 0600); ++ TEST_COMPARE (tcgetpgrp (fd), pgrp); + xclose (fd); ++ + return 0; + } + +@@ -62,6 +72,7 @@ static int restart; + + static void + run_subprogram (int argc, char *argv[], const posix_spawnattr_t *attr, ++ const posix_spawn_file_actions_t *actions, int tcfd, + int exp_err) + { + short int flags; +@@ -69,7 +80,9 @@ run_subprogram (int argc, char *argv[], const posix_spawnattr_t *attr, + bool setpgrp = flags & POSIX_SPAWN_SETPGROUP; + + char *spargv[9]; ++ TEST_VERIFY_EXIT (((argc - 1) + 4) < array_length (spargv)); + char pgrp[INT_STRLEN_BOUND (pid_t)]; ++ char tcfdstr[INT_STRLEN_BOUND (int)]; + + int i = 0; + for (; i < argc - 1; i++) +@@ -83,11 +96,12 @@ run_subprogram (int argc, char *argv[], const posix_spawnattr_t *attr, + snprintf (pgrp, sizeof pgrp, "%d", getpgrp ()); + spargv[i++] = pgrp; + } ++ snprintf (tcfdstr, sizeof tcfdstr, "%d", tcfd); ++ spargv[i++] = tcfdstr; + spargv[i] = NULL; +- TEST_VERIFY_EXIT (i < array_length (spargv)); + + pid_t pid; +- TEST_COMPARE (posix_spawn (&pid, argv[1], NULL, attr, spargv, environ), ++ TEST_COMPARE (posix_spawn (&pid, argv[1], actions, attr, spargv, environ), + exp_err); + if (exp_err != 0) + return; +@@ -114,44 +128,55 @@ do_test (int argc, char *argv[]) + */ + + if (restart) +- return handle_restart (argv[1]); ++ return handle_restart (argv[1], argv[2]); + +- int tcfd = xopen (_PATH_TTY, O_RDONLY, 0600); ++ int tcfd = open64 (_PATH_TTY, O_RDONLY, 0600); ++ if (tcfd == -1) ++ { ++ if (errno == ENXIO) ++ FAIL_UNSUPPORTED ("terminal not available, skipping test"); ++ FAIL_EXIT1 ("open64 (\"%s\", 0x%x, 0600): %m", _PATH_TTY, O_RDONLY); ++ } + +- /* Check getters and setters. */ ++ /* Check setting the controlling terminal without changing the group. */ + { + posix_spawnattr_t attr; + TEST_COMPARE (posix_spawnattr_init (&attr), 0); +- TEST_COMPARE (posix_spawnattr_tcsetpgrp_np (&attr, tcfd), 0); ++ posix_spawn_file_actions_t actions; ++ TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0); ++ TEST_COMPARE (posix_spawn_file_actions_addtcsetpgrp_np (&actions, tcfd), ++ 0); + +- int fd; +- TEST_COMPARE (posix_spawnattr_tcgetpgrp_np (&attr, &fd), 0); +- TEST_COMPARE (tcfd, fd); ++ run_subprogram (argc, argv, &attr, &actions, -1, 0); + } + +- /* Check setting the controlling terminal without changing the group. */ ++ /* Check setting both the controlling terminal and the create a new process ++ group. */ + { + posix_spawnattr_t attr; + TEST_COMPARE (posix_spawnattr_init (&attr), 0); +- TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_TCSETPGROUP), ++ TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETPGROUP), 0); ++ posix_spawn_file_actions_t actions; ++ TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0); ++ TEST_COMPARE (posix_spawn_file_actions_addtcsetpgrp_np (&actions, tcfd), + 0); +- TEST_COMPARE (posix_spawnattr_tcsetpgrp_np (&attr, tcfd), 0); + +- run_subprogram (argc, argv, &attr, 0); ++ run_subprogram (argc, argv, &attr, &actions, -1, 0); + } + +- /* Check setting both the controlling terminal and the create a new process +- group. */ ++ /* Same as before, but check if the addclose file actions closes the terminal ++ file descriptor. */ + { + posix_spawnattr_t attr; + TEST_COMPARE (posix_spawnattr_init (&attr), 0); +- TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_TCSETPGROUP +- | POSIX_SPAWN_SETPGROUP), ++ TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETPGROUP), 0); ++ posix_spawn_file_actions_t actions; ++ TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0); ++ TEST_COMPARE (posix_spawn_file_actions_addtcsetpgrp_np (&actions, tcfd), + 0); +- TEST_COMPARE (posix_spawnattr_setpgroup (&attr, 0), 0); +- TEST_COMPARE (posix_spawnattr_tcsetpgrp_np (&attr, tcfd), 0); ++ TEST_COMPARE (posix_spawn_file_actions_addclose (&actions, tcfd), 0); + +- run_subprogram (argc, argv, &attr, 0); ++ run_subprogram (argc, argv, &attr, &actions, tcfd, 0); + } + + /* Trying to set the controlling terminal after a setsid incurs in a ENOTTY +@@ -159,11 +184,13 @@ do_test (int argc, char *argv[]) + { + posix_spawnattr_t attr; + TEST_COMPARE (posix_spawnattr_init (&attr), 0); +- TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_TCSETPGROUP +- | POSIX_SPAWN_SETSID), 0); +- TEST_COMPARE (posix_spawnattr_tcsetpgrp_np (&attr, tcfd), 0); ++ TEST_COMPARE (posix_spawnattr_setflags (&attr, POSIX_SPAWN_SETSID), 0); ++ posix_spawn_file_actions_t actions; ++ TEST_COMPARE (posix_spawn_file_actions_init (&actions), 0); ++ TEST_COMPARE (posix_spawn_file_actions_addtcsetpgrp_np (&actions, tcfd), ++ 0); + +- run_subprogram (argc, argv, &attr, ENOTTY); ++ run_subprogram (argc, argv, &attr, &actions, -1, ENOTTY); + } + + xclose (tcfd); +diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist +index ba6ef0924ca632cb..119fdb1c3abed349 100644 +--- a/sysdeps/mach/hurd/i386/libc.abilist ++++ b/sysdeps/mach/hurd/i386/libc.abilist +@@ -2287,6 +2287,7 @@ GLIBC_2.34 shm_unlink F + GLIBC_2.34 timespec_getres F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/mach/hurd/spawni.c b/sysdeps/mach/hurd/spawni.c +index 4d71181643c19b0d..f46bdd04683a30b6 100644 +--- a/sysdeps/mach/hurd/spawni.c ++++ b/sysdeps/mach/hurd/spawni.c +@@ -390,19 +390,6 @@ retry: + if (!err && (flags & POSIX_SPAWN_SETPGROUP) != 0) + err = __proc_setpgrp (proc, new_pid, attrp->__pgrp); + +- /* Set the controlling terminal. */ +- if (!err && (flags & POSIX_SPAWN_TCSETPGROUP) != 0) +- { +- pid_t pgrp; +- /* Check if it is possible to avoid an extra syscall. */ +- if ((attrp->__flags & POSIX_SPAWN_SETPGROUP) != 0 && attrp->__pgrp != 0) +- pgrp = attrp->__pgrp; +- else +- err = __proc_getpgrp (proc, new_pid, &pgrp); +- if (!err) +- err = __tcsetpgrp (attrp->__ctty_fd, pgrp); +- } +- + /* Set the effective user and group IDs. */ + if (!err && (flags & POSIX_SPAWN_RESETIDS) != 0) + { +@@ -643,6 +630,19 @@ retry: + case spawn_do_closefrom: + err = do_closefrom (action->action.closefrom_action.from); + break; ++ ++ case spawn_do_tcsetpgrp: ++ { ++ pid_t pgrp; ++ /* Check if it is possible to avoid an extra syscall. */ ++ if ((attrp->__flags & POSIX_SPAWN_SETPGROUP) ++ != 0 && attrp->__pgrp != 0) ++ pgrp = attrp->__pgrp; ++ else ++ err = __proc_getpgrp (proc, new_pid, &pgrp); ++ if (!err) ++ err = __tcsetpgrp (action->action.setpgrp_action.fd, pgrp); ++ } + } + + if (err) +diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist +index 9dd574d9e2a7b541..1b63d9e447f16862 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist +@@ -2615,5 +2615,4 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist +index f66704877eb43052..e7e4cf7d2afe8e37 100644 +--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist ++++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist +@@ -2712,8 +2712,7 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist +index 97aa3da1ad15a459..bc3d228e3183bbff 100644 +--- a/sysdeps/unix/sysv/linux/arc/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arc/libc.abilist +@@ -2376,5 +2376,4 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist +index 18f4364856980df6..db7039c4aba3a2cb 100644 +--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist +@@ -495,8 +495,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 + GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 +diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist +index 2c12c020b1f2f320..d2add4fb49bec9ae 100644 +--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist +@@ -492,8 +492,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 + GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 +diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist +index 7f28516feb9b3ce0..355d72a30c41b668 100644 +--- a/sysdeps/unix/sysv/linux/csky/libc.abilist ++++ b/sysdeps/unix/sysv/linux/csky/libc.abilist +@@ -2651,5 +2651,4 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist +index 9776f20763a46963..3df39bb28cc68f8a 100644 +--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist ++++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist +@@ -2600,8 +2600,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist +index 96b50d0a9bd68a19..c4da358f80c4aae3 100644 +--- a/sysdeps/unix/sysv/linux/i386/libc.abilist ++++ b/sysdeps/unix/sysv/linux/i386/libc.abilist +@@ -2784,8 +2784,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist +index 9b2eebfbf1b91f06..241bac70ea822230 100644 +--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist +@@ -2550,8 +2550,7 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +index 71cd35488e3b60ab..78bf372b729a2aa5 100644 +--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist ++++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +@@ -496,8 +496,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0x98 + GLIBC_2.4 _IO_2_1_stdin_ D 0x98 +diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +index ced01a501dd816a6..00df5c901f5a608b 100644 +--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist ++++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +@@ -2727,8 +2727,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +index 5406c01f1de62803..e8118569c30bbd1e 100644 +--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +@@ -2700,5 +2700,4 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +index 53b8ade4c3c16c9f..c0d2373e64a26700 100644 +--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +@@ -2697,5 +2697,4 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +index 919973ea46866c8a..2d0fd04f54bf3495 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +@@ -2692,8 +2692,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +index cf5a8dc1208d1f45..e39ccfb312c61434 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +@@ -2690,8 +2690,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +index 003c3bd0a6bb0595..1e900f86e43b49ec 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +@@ -2698,8 +2698,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +index 73629c2f21aed4a3..9145ba79316952fd 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +@@ -2601,8 +2601,7 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist +index 9e8645ebc0a996a7..e95d60d92622ea38 100644 +--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist ++++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist +@@ -2739,5 +2739,4 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +index 3d1ba9887c327899..3820b9f23524fb40 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +@@ -2754,8 +2754,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +index d979a3b93b98cc5a..464dc27fcd688a94 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +@@ -2787,8 +2787,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +index 44688e52cf99e9fc..2f7e58747fb9a6b1 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +@@ -2509,8 +2509,7 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +index 40682711eb73fb12..4f3043d913781166 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +@@ -2811,5 +2811,4 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +index e239d626b32e03ba..84b6ac815a622eda 100644 +--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +@@ -2378,5 +2378,4 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +index ab0c4e70927d3dc0..4d5c19c56a03175d 100644 +--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +@@ -2578,5 +2578,4 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F +diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +index 74e3a4651f215344..7c5ee8d56959fb35 100644 +--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +@@ -2752,8 +2752,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +index e5553f06b205f0cf..50de0b46cf078fda 100644 +--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +@@ -2546,8 +2546,7 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist +index 9662041cd4c5df83..66fba013caaf0200 100644 +--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist +@@ -2607,8 +2607,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist +index bf90e924a671f1c1..38703f8aa039b976 100644 +--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist +@@ -2604,8 +2604,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +index ddb0d0621f80426b..6df55eb76582a397 100644 +--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +@@ -2747,8 +2747,7 @@ GLIBC_2.35 __epoll_pwait2_time64 F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +index ca14224cb7d5740d..b90569d8814c603a 100644 +--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +@@ -2573,8 +2573,7 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/spawni.c b/sysdeps/unix/sysv/linux/spawni.c +index 601850638d538ae0..231fbef6041d15d8 100644 +--- a/sysdeps/unix/sysv/linux/spawni.c ++++ b/sysdeps/unix/sysv/linux/spawni.c +@@ -164,17 +164,6 @@ __spawni_child (void *arguments) + && __setpgid (0, attr->__pgrp) != 0) + goto fail; + +- /* Set the controlling terminal. */ +- if ((attr->__flags & POSIX_SPAWN_TCSETPGROUP) != 0) +- { +- /* Check if it is possible to avoid an extra syscall. */ +- pid_t pgrp = (attr->__flags & POSIX_SPAWN_SETPGROUP) != 0 +- && attr->__pgrp != 0 +- ? attr->__pgrp : __getpgid (0); +- if (__tcsetpgrp (attr->__ctty_fd, pgrp) != 0) +- goto fail; +- } +- + /* Set the effective user and group IDs. */ + if ((attr->__flags & POSIX_SPAWN_RESETIDS) != 0 + && (local_seteuid (__getuid ()) != 0 +@@ -279,6 +268,16 @@ __spawni_child (void *arguments) + if (r != 0 && !__closefrom_fallback (lowfd, false)) + goto fail; + } break; ++ ++ case spawn_do_tcsetpgrp: ++ { ++ /* Check if it is possible to avoid an extra syscall. */ ++ pid_t pgrp = (attr->__flags & POSIX_SPAWN_SETPGROUP) != 0 ++ && attr->__pgrp != 0 ++ ? attr->__pgrp : __getpgid (0); ++ if (__tcsetpgrp (action->action.setpgrp_action.fd, pgrp) != 0) ++ goto fail; ++ } + } + } + } +diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +index 661d928adf2ac13c..e88b0f101f788eee 100644 +--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +@@ -2524,8 +2524,7 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +index bb8058dfa4144bbc..e0755272eb62bec3 100644 +--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +@@ -2630,5 +2630,4 @@ GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F + GLIBC_2.35 _dl_find_object F + GLIBC_2.35 epoll_pwait2 F +-GLIBC_2.35 posix_spawnattr_tcgetpgrp_np F +-GLIBC_2.35 posix_spawnattr_tcsetpgrp_np F ++GLIBC_2.35 posix_spawn_file_actions_addtcsetpgrp_np F diff --git a/glibc-RHEL-93320-18.patch b/glibc-RHEL-93320-18.patch new file mode 100644 index 0000000..98225df --- /dev/null +++ b/glibc-RHEL-93320-18.patch @@ -0,0 +1,36 @@ +commit 2ff48a4025515e93d722947a9eabb114f4a65b22 +Author: Florian Weimer +Date: Fri Nov 4 07:43:59 2022 +0100 + + posix: Make posix_spawn extensions available by default + + Some sources merely include without -D_GNU_SOURCE and expect + declarations for posix_spawn_file_actions_addchdir_np to be available. + For consistency, declare posix_spawn_file_actions_addfchdir_np, + posix_spawn_file_actions_addclosefrom_np, + posix_spawn_file_actions_addtcsetpgrp_np as well. + + Reviewed-by: Adhemerval Zanella + +diff --git a/posix/spawn.h b/posix/spawn.h +index 7cf1a5b628480404..81202ef0caec031c 100644 +--- a/posix/spawn.h ++++ b/posix/spawn.h +@@ -198,7 +198,7 @@ extern int posix_spawn_file_actions_adddup2 (posix_spawn_file_actions_t * + int __fd, int __newfd) + __THROW __nonnull ((1)); + +-#ifdef __USE_GNU ++#ifdef __USE_MISC + /* Add an action changing the directory to PATH during spawn. This + affects the subsequent file actions. */ + extern int posix_spawn_file_actions_addchdir_np (posix_spawn_file_actions_t * +@@ -227,7 +227,7 @@ posix_spawn_file_actions_addtcsetpgrp_np (posix_spawn_file_actions_t *, + int __tcfd) + __THROW __nonnull ((1)); + +-#endif ++#endif /* __USE_MISC */ + + __END_DECLS + diff --git a/glibc-RHEL-93320-19.patch b/glibc-RHEL-93320-19.patch new file mode 100644 index 0000000..e2be1d6 --- /dev/null +++ b/glibc-RHEL-93320-19.patch @@ -0,0 +1,185 @@ +Downstream-only patch to avoid ABI change: + +Move _dl_find_object and _rtld_libc_freeres to GLIBC_PRIVATE ABI + +Avoid modifying rtld_global_ro as it could cause interesting issues +during glibc updates due to mismatches between running and newly +installed glibc vs. ld.so + +This means _dl_find_object() will not work from a shared object +dlopen()'d from a static binary. Th the corresponding test has to +be removed. This is considered a non-issue at this point as C++ +exceptions are broken in that case for other reasons already +(TLS not initialized properly etc...) + +Signed-off-by: Benjamin Herrenschmidt +Reviewed-by: DJ Delorie + +diff --git a/elf/Makefile b/elf/Makefile +index 7382cf6dd498ce8a..a28ea58551ffd1d7 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -62,6 +62,7 @@ dl-routines = \ + dl-find_object \ + dl-fini \ + dl-init \ ++ dl-libc_freeres \ + dl-load \ + dl-lookup \ + dl-lookup-direct \ +@@ -138,7 +139,6 @@ rtld-routines = \ + dl-hwcaps \ + dl-hwcaps-subdirs \ + dl-hwcaps_split \ +- dl-libc_freeres \ + dl-minimal \ + dl-mutex \ + dl-sysdep \ +@@ -285,7 +285,6 @@ tests-static-internal := \ + tst-stackguard1-static \ + tst-tls1-static \ + tst-tls1-static-non-pie \ +- tst-dl_find_object-static \ + # tests-static-internal + + CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o +diff --git a/elf/Versions b/elf/Versions +index 71d648a6b4bcbf5a..3e729c92d3be282d 100644 +--- a/elf/Versions ++++ b/elf/Versions +@@ -76,5 +76,11 @@ ld { + + # Check if an address range within a loaded ELF object is read-only. + _dl_readonly_area; ++ ++ # Called from __libc_shared to deallocate malloc'ed memory. ++ __rtld_libc_freeres; ++ ++ # Implementation of _dl_find_object. The public entry point is in libc ++ __dl_find_object_internal; + } + } +diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c +index 56894ca24a510b07..99797580066cdce8 100644 +--- a/elf/dl-find_object.c ++++ b/elf/dl-find_object.c +@@ -356,7 +356,7 @@ _dlfo_lookup (uintptr_t pc, struct dl_find_object_internal *first1, size_t size) + } + + int +-_dl_find_object (void *pc1, struct dl_find_object *result) ++__dl_find_object_internal (void *pc1, struct dl_find_object *result) + { + uintptr_t pc = (uintptr_t) pc1; + +@@ -463,7 +463,7 @@ _dl_find_object (void *pc1, struct dl_find_object *result) + return -1; + } /* Transaction retry loop. */ + } +-rtld_hidden_def (_dl_find_object) ++rtld_hidden_def (__dl_find_object_internal) + + /* _dlfo_process_initial is called twice. First to compute the array + sizes from the initial loaded mappings. Second to fill in the +diff --git a/elf/dl-libc_freeres.c b/elf/dl-libc_freeres.c +index 2a377fa9dfc4f1c0..1acb7a87297515b0 100644 +--- a/elf/dl-libc_freeres.c ++++ b/elf/dl-libc_freeres.c +@@ -24,3 +24,4 @@ __rtld_libc_freeres (void) + { + _dl_find_object_freeres (); + } ++rtld_hidden_def (__rtld_libc_freeres) +diff --git a/elf/libc-dl_find_object.c b/elf/libc-dl_find_object.c +index 38ea3bc94999df6e..1ce487ac3812d2a9 100644 +--- a/elf/libc-dl_find_object.c ++++ b/elf/libc-dl_find_object.c +@@ -22,5 +22,5 @@ + int + _dl_find_object (void *address, struct dl_find_object *result) + { +- return GLRO (dl_find_object) (address, result); ++ return __dl_find_object_internal (address, result); + } +diff --git a/elf/rtld.c b/elf/rtld.c +index d698a32ae120e887..667880e18ae816d8 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -381,7 +381,6 @@ struct rtld_global_ro _rtld_global_ro attribute_relro = + ._dl_catch_error = _dl_catch_error, + ._dl_error_free = _dl_error_free, + ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft, +- ._dl_libc_freeres = __rtld_libc_freeres, + #ifdef HAVE_DL_DISCOVER_OSVERSION + ._dl_discover_osversion = _dl_discover_osversion + #endif +@@ -584,10 +583,6 @@ _dl_start (void *arg) + + __rtld_malloc_init_stubs (); + +- /* Do not use an initializer for these members because it would +- intefere with __rtld_static_init. */ +- GLRO (dl_find_object) = &_dl_find_object; +- + { + #ifdef DONT_USE_BOOTSTRAP_MAP + ElfW(Addr) entry = _dl_start_final (arg); +diff --git a/elf/rtld_static_init.c b/elf/rtld_static_init.c +index 6027000d3a56e46e..3f8abb6800b401d7 100644 +--- a/elf/rtld_static_init.c ++++ b/elf/rtld_static_init.c +@@ -78,7 +78,6 @@ __rtld_static_init (struct link_map *map) + extern __typeof (dl->_dl_tls_static_size) _dl_tls_static_size + attribute_hidden; + dl->_dl_tls_static_size = _dl_tls_static_size; +- dl->_dl_find_object = _dl_find_object; + + __rtld_static_init_arch (map, dl); + } +diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c +index 856ff7831f84d07c..2a1e8971b2df218d 100644 +--- a/malloc/set-freeres.c ++++ b/malloc/set-freeres.c +@@ -69,7 +69,7 @@ __libc_freeres (void) + call_function_static_weak (__libc_dlerror_result_free); + + #ifdef SHARED +- GLRO (dl_libc_freeres) (); ++ __rtld_libc_freeres (); + #endif + + for (p = symbol_set_first_element (__libc_freeres_ptrs); +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index 29c87649f14a1b5b..e5860916345487e7 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -714,14 +714,6 @@ struct rtld_global_ro + void (*_dl_error_free) (void *); + void *(*_dl_tls_get_addr_soft) (struct link_map *); + +- /* Called from __libc_shared to deallocate malloc'ed memory. */ +- void (*_dl_libc_freeres) (void); +- +- /* Implementation of _dl_find_object. The public entry point is in +- libc, and this is patched by __rtld_static_init to support static +- dlopen. */ +- int (*_dl_find_object) (void *, struct dl_find_object *); +- + #ifdef HAVE_DL_DISCOVER_OSVERSION + int (*_dl_discover_osversion) (void); + #endif +@@ -1513,7 +1505,13 @@ __rtld_mutex_init (void) + #endif /* !PTHREAD_IN_LIBC */ + + /* Implementation of GL (dl_libc_freeres). */ +-void __rtld_libc_freeres (void) attribute_hidden; ++void __rtld_libc_freeres (void); ++rtld_hidden_proto (__rtld_libc_freeres) ++ ++/* Implementation of _dl_find_object */ ++int __dl_find_object_internal (void *, struct dl_find_object *); ++rtld_hidden_proto (__dl_find_object_internal) ++ + + #if THREAD_GSCOPE_IN_TCB + void __thread_gscope_wait (void) attribute_hidden; diff --git a/glibc-RHEL-93320-2.patch b/glibc-RHEL-93320-2.patch new file mode 100644 index 0000000..57cc896 --- /dev/null +++ b/glibc-RHEL-93320-2.patch @@ -0,0 +1,692 @@ +commit 8bd336a00a5311bf7a9e99b3b0e9f01ff5faa74b +Author: Florian Weimer +Date: Wed Nov 17 12:20:13 2021 +0100 + + nptl: Extract from pthread_cond_common.c + + And make it an installed header. This addresses a few aliasing + violations (which do not seem to result in miscompilation due to + the use of atomics), and also enables use of wide counters in other + parts of the library. + + The debug output in nptl/tst-cond22 has been adjusted to print + the 32-bit values instead because it avoids a big-endian/little-endian + difference. + + Reviewed-by: Adhemerval Zanella + +Conflicts: + nptl/Makefile + Adapt to different overall layout + nptl/tst-cond22.c + Updated for context due to + c36fc50781995e6758cae2b6927839d0157f213c + being already backported + sysdeps/nptl/bits/thread-shared-types.h + Updated for context due to + c36fc50781995e6758cae2b6927839d0157f213c + being already backported + +diff --git a/bits/atomic_wide_counter.h b/bits/atomic_wide_counter.h +new file mode 100644 +index 0000000000000000..0687eb554e5051a1 +--- /dev/null ++++ b/bits/atomic_wide_counter.h +@@ -0,0 +1,35 @@ ++/* Monotonically increasing wide counters (at least 62 bits). ++ Copyright (C) 2016-2021 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 ++ . */ ++ ++#ifndef _BITS_ATOMIC_WIDE_COUNTER_H ++#define _BITS_ATOMIC_WIDE_COUNTER_H ++ ++/* Counter that is monotonically increasing (by less than 2**31 per ++ increment), with a single writer, and an arbitrary number of ++ readers. */ ++typedef union ++{ ++ __extension__ unsigned long long int __value64; ++ struct ++ { ++ unsigned int __low; ++ unsigned int __high; ++ } __value32; ++} __atomic_wide_counter; ++ ++#endif /* _BITS_ATOMIC_WIDE_COUNTER_H */ +diff --git a/include/atomic_wide_counter.h b/include/atomic_wide_counter.h +new file mode 100644 +index 0000000000000000..31f009d5e66cb45f +--- /dev/null ++++ b/include/atomic_wide_counter.h +@@ -0,0 +1,89 @@ ++/* Monotonically increasing wide counters (at least 62 bits). ++ Copyright (C) 2016-2021 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 ++ . */ ++ ++#ifndef _ATOMIC_WIDE_COUNTER_H ++#define _ATOMIC_WIDE_COUNTER_H ++ ++#include ++#include ++ ++#if __HAVE_64B_ATOMICS ++ ++static inline uint64_t ++__atomic_wide_counter_load_relaxed (__atomic_wide_counter *c) ++{ ++ return atomic_load_relaxed (&c->__value64); ++} ++ ++static inline uint64_t ++__atomic_wide_counter_fetch_add_relaxed (__atomic_wide_counter *c, ++ unsigned int val) ++{ ++ return atomic_fetch_add_relaxed (&c->__value64, val); ++} ++ ++static inline uint64_t ++__atomic_wide_counter_fetch_add_acquire (__atomic_wide_counter *c, ++ unsigned int val) ++{ ++ return atomic_fetch_add_acquire (&c->__value64, val); ++} ++ ++static inline void ++__atomic_wide_counter_add_relaxed (__atomic_wide_counter *c, ++ unsigned int val) ++{ ++ atomic_store_relaxed (&c->__value64, ++ atomic_load_relaxed (&c->__value64) + val); ++} ++ ++static uint64_t __attribute__ ((unused)) ++__atomic_wide_counter_fetch_xor_release (__atomic_wide_counter *c, ++ unsigned int val) ++{ ++ return atomic_fetch_xor_release (&c->__value64, val); ++} ++ ++#else /* !__HAVE_64B_ATOMICS */ ++ ++uint64_t __atomic_wide_counter_load_relaxed (__atomic_wide_counter *c) ++ attribute_hidden; ++ ++uint64_t __atomic_wide_counter_fetch_add_relaxed (__atomic_wide_counter *c, ++ unsigned int op) ++ attribute_hidden; ++ ++static inline uint64_t ++__atomic_wide_counter_fetch_add_acquire (__atomic_wide_counter *c, ++ unsigned int val) ++{ ++ uint64_t r = __atomic_wide_counter_fetch_add_relaxed (c, val); ++ atomic_thread_fence_acquire (); ++ return r; ++} ++ ++static inline void ++__atomic_wide_counter_add_relaxed (__atomic_wide_counter *c, ++ unsigned int val) ++{ ++ __atomic_wide_counter_fetch_add_relaxed (c, val); ++} ++ ++#endif /* !__HAVE_64B_ATOMICS */ ++ ++#endif /* _ATOMIC_WIDE_COUNTER_H */ +diff --git a/include/bits/atomic_wide_counter.h b/include/bits/atomic_wide_counter.h +new file mode 100644 +index 0000000000000000..8fb09a529104cc1d +--- /dev/null ++++ b/include/bits/atomic_wide_counter.h +@@ -0,0 +1 @@ ++#include_next +diff --git a/misc/Makefile b/misc/Makefile +index 5d6fc0f6824a734f..6e8725309cf73293 100644 +--- a/misc/Makefile ++++ b/misc/Makefile +@@ -73,7 +73,8 @@ routines := brk sbrk sstk ioctl \ + fgetxattr flistxattr fremovexattr fsetxattr getxattr \ + listxattr lgetxattr llistxattr lremovexattr lsetxattr \ + removexattr setxattr getauxval ifunc-impl-list makedev \ +- allocate_once fd_to_filename single_threaded unwind-link ++ allocate_once fd_to_filename single_threaded unwind-link \ ++ atomic_wide_counter + + generated += tst-error1.mtrace tst-error1-mem.out \ + tst-allocate_once.mtrace tst-allocate_once-mem.out +diff --git a/misc/atomic_wide_counter.c b/misc/atomic_wide_counter.c +new file mode 100644 +index 0000000000000000..56d898192573193e +--- /dev/null ++++ b/misc/atomic_wide_counter.c +@@ -0,0 +1,127 @@ ++/* Monotonically increasing wide counters (at least 62 bits). ++ Copyright (C) 2016-2021 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 ++ . */ ++ ++#include ++ ++#if !__HAVE_64B_ATOMICS ++ ++/* Values we add or xor are less than or equal to 1<<31, so we only ++ have to make overflow-and-addition atomic wrt. to concurrent load ++ operations and xor operations. To do that, we split each counter ++ into two 32b values of which we reserve the MSB of each to ++ represent an overflow from the lower-order half to the higher-order ++ half. ++ ++ In the common case, the state is (higher-order / lower-order half, and . is ++ basically concatenation of the bits): ++ 0.h / 0.l = h.l ++ ++ When we add a value of x that overflows (i.e., 0.l + x == 1.L), we run the ++ following steps S1-S4 (the values these represent are on the right-hand ++ side): ++ S1: 0.h / 1.L == (h+1).L ++ S2: 1.(h+1) / 1.L == (h+1).L ++ S3: 1.(h+1) / 0.L == (h+1).L ++ S4: 0.(h+1) / 0.L == (h+1).L ++ If the LSB of the higher-order half is set, readers will ignore the ++ overflow bit in the lower-order half. ++ ++ To get an atomic snapshot in load operations, we exploit that the ++ higher-order half is monotonically increasing; if we load a value V from ++ it, then read the lower-order half, and then read the higher-order half ++ again and see the same value V, we know that both halves have existed in ++ the sequence of values the full counter had. This is similar to the ++ validated reads in the time-based STMs in GCC's libitm (e.g., ++ method_ml_wt). ++ ++ One benefit of this scheme is that this makes load operations ++ obstruction-free because unlike if we would just lock the counter, readers ++ can almost always interpret a snapshot of each halves. Readers can be ++ forced to read a new snapshot when the read is concurrent with an overflow. ++ However, overflows will happen infrequently, so load operations are ++ practically lock-free. */ ++ ++uint64_t ++__atomic_wide_counter_fetch_add_relaxed (__atomic_wide_counter *c, ++ unsigned int op) ++{ ++ /* S1. Note that this is an atomic read-modify-write so it extends the ++ release sequence of release MO store at S3. */ ++ unsigned int l = atomic_fetch_add_relaxed (&c->__value32.__low, op); ++ unsigned int h = atomic_load_relaxed (&c->__value32.__high); ++ uint64_t result = ((uint64_t) h << 31) | l; ++ l += op; ++ if ((l >> 31) > 0) ++ { ++ /* Overflow. Need to increment higher-order half. Note that all ++ add operations are ordered in happens-before. */ ++ h++; ++ /* S2. Release MO to synchronize with the loads of the higher-order half ++ in the load operation. See __condvar_load_64_relaxed. */ ++ atomic_store_release (&c->__value32.__high, ++ h | ((unsigned int) 1 << 31)); ++ l ^= (unsigned int) 1 << 31; ++ /* S3. See __condvar_load_64_relaxed. */ ++ atomic_store_release (&c->__value32.__low, l); ++ /* S4. Likewise. */ ++ atomic_store_release (&c->__value32.__high, h); ++ } ++ return result; ++} ++ ++uint64_t ++__atomic_wide_counter_load_relaxed (__atomic_wide_counter *c) ++{ ++ unsigned int h, l, h2; ++ do ++ { ++ /* This load and the second one below to the same location read from the ++ stores in the overflow handling of the add operation or the ++ initializing stores (which is a simple special case because ++ initialization always completely happens before further use). ++ Because no two stores to the higher-order half write the same value, ++ the loop ensures that if we continue to use the snapshot, this load ++ and the second one read from the same store operation. All candidate ++ store operations have release MO. ++ If we read from S2 in the first load, then we will see the value of ++ S1 on the next load (because we synchronize with S2), or a value ++ later in modification order. We correctly ignore the lower-half's ++ overflow bit in this case. If we read from S4, then we will see the ++ value of S3 in the next load (or a later value), which does not have ++ the overflow bit set anymore. ++ */ ++ h = atomic_load_acquire (&c->__value32.__high); ++ /* This will read from the release sequence of S3 (i.e, either the S3 ++ store or the read-modify-writes at S1 following S3 in modification ++ order). Thus, the read synchronizes with S3, and the following load ++ of the higher-order half will read from the matching S2 (or a later ++ value). ++ Thus, if we read a lower-half value here that already overflowed and ++ belongs to an increased higher-order half value, we will see the ++ latter and h and h2 will not be equal. */ ++ l = atomic_load_acquire (&c->__value32.__low); ++ /* See above. */ ++ h2 = atomic_load_relaxed (&c->__value32.__high); ++ } ++ while (h != h2); ++ if (((l >> 31) > 0) && ((h >> 31) > 0)) ++ l ^= (unsigned int) 1 << 31; ++ return ((uint64_t) (h & ~((unsigned int) 1 << 31)) << 31) + l; ++} ++ ++#endif /* !__HAVE_64B_ATOMICS */ +diff --git a/nptl/Makefile b/nptl/Makefile +index cac75eb8f5b68320..80c7587f0086677b 100644 +--- a/nptl/Makefile ++++ b/nptl/Makefile +@@ -22,8 +22,14 @@ subdir := nptl + + include ../Makeconfig + +-headers := pthread.h semaphore.h bits/semaphore.h \ +- bits/struct_mutex.h bits/struct_rwlock.h ++headers := \ ++ bits/atomic_wide_counter.h \ ++ bits/semaphore.h \ ++ bits/struct_mutex.h \ ++ bits/struct_rwlock.h \ ++ pthread.h \ ++ semaphore.h \ ++ # headers + + extra-libs := libpthread + extra-libs-others := $(extra-libs) +@@ -277,7 +283,6 @@ tests = \ + tst-cancel7 \ + tst-cancel17 \ + tst-cancel24 \ +- tst-cond22 \ + tst-cond26 \ + tst-context1 \ + tst-default-attr \ +@@ -368,6 +373,7 @@ tests-container = tst-pthread-getattr + + tests-internal := \ + tst-barrier5 \ ++ tst-cond22 \ + tst-mutex8 \ + tst-mutex8-static \ + tst-mutexpi8 \ +diff --git a/nptl/pthread_cond_common.c b/nptl/pthread_cond_common.c +index 485aca4076a372d7..9e00ebd73d4d2fba 100644 +--- a/nptl/pthread_cond_common.c ++++ b/nptl/pthread_cond_common.c +@@ -17,79 +17,52 @@ + . */ + + #include ++#include + #include + #include + +-/* We need 3 least-significant bits on __wrefs for something else. */ ++/* We need 3 least-significant bits on __wrefs for something else. ++ This also matches __atomic_wide_counter requirements: The highest ++ value we add is __PTHREAD_COND_MAX_GROUP_SIZE << 2 to __g1_start ++ (the two extra bits are for the lock in the two LSBs of ++ __g1_start). */ + #define __PTHREAD_COND_MAX_GROUP_SIZE ((unsigned) 1 << 29) + +-#if __HAVE_64B_ATOMICS == 1 +- +-static uint64_t __attribute__ ((unused)) ++static inline uint64_t + __condvar_load_wseq_relaxed (pthread_cond_t *cond) + { +- return atomic_load_relaxed (&cond->__data.__wseq); ++ return __atomic_wide_counter_load_relaxed (&cond->__data.__wseq); + } + +-static uint64_t __attribute__ ((unused)) ++static inline uint64_t + __condvar_fetch_add_wseq_acquire (pthread_cond_t *cond, unsigned int val) + { +- return atomic_fetch_add_acquire (&cond->__data.__wseq, val); ++ return __atomic_wide_counter_fetch_add_acquire (&cond->__data.__wseq, val); + } + +-static uint64_t __attribute__ ((unused)) +-__condvar_fetch_xor_wseq_release (pthread_cond_t *cond, unsigned int val) +-{ +- return atomic_fetch_xor_release (&cond->__data.__wseq, val); +-} +- +-static uint64_t __attribute__ ((unused)) ++static inline uint64_t + __condvar_load_g1_start_relaxed (pthread_cond_t *cond) + { +- return atomic_load_relaxed (&cond->__data.__g1_start); ++ return __atomic_wide_counter_load_relaxed (&cond->__data.__g1_start); + } + +-static void __attribute__ ((unused)) ++static inline void + __condvar_add_g1_start_relaxed (pthread_cond_t *cond, unsigned int val) + { +- atomic_store_relaxed (&cond->__data.__g1_start, +- atomic_load_relaxed (&cond->__data.__g1_start) + val); ++ __atomic_wide_counter_add_relaxed (&cond->__data.__g1_start, val); + } + +-#else +- +-/* We use two 64b counters: __wseq and __g1_start. They are monotonically +- increasing and single-writer-multiple-readers counters, so we can implement +- load, fetch-and-add, and fetch-and-xor operations even when we just have +- 32b atomics. Values we add or xor are less than or equal to 1<<31 (*), +- so we only have to make overflow-and-addition atomic wrt. to concurrent +- load operations and xor operations. To do that, we split each counter into +- two 32b values of which we reserve the MSB of each to represent an +- overflow from the lower-order half to the higher-order half. +- +- In the common case, the state is (higher-order / lower-order half, and . is +- basically concatenation of the bits): +- 0.h / 0.l = h.l ++#if __HAVE_64B_ATOMICS == 1 + +- When we add a value of x that overflows (i.e., 0.l + x == 1.L), we run the +- following steps S1-S4 (the values these represent are on the right-hand +- side): +- S1: 0.h / 1.L == (h+1).L +- S2: 1.(h+1) / 1.L == (h+1).L +- S3: 1.(h+1) / 0.L == (h+1).L +- S4: 0.(h+1) / 0.L == (h+1).L +- If the LSB of the higher-order half is set, readers will ignore the +- overflow bit in the lower-order half. ++static inline uint64_t ++__condvar_fetch_xor_wseq_release (pthread_cond_t *cond, unsigned int val) ++{ ++ return atomic_fetch_xor_release (&cond->__data.__wseq.__value64, val); ++} + +- To get an atomic snapshot in load operations, we exploit that the +- higher-order half is monotonically increasing; if we load a value V from +- it, then read the lower-order half, and then read the higher-order half +- again and see the same value V, we know that both halves have existed in +- the sequence of values the full counter had. This is similar to the +- validated reads in the time-based STMs in GCC's libitm (e.g., +- method_ml_wt). ++#else /* !__HAVE_64B_ATOMICS */ + +- The xor operation needs to be an atomic read-modify-write. The write ++/* The xor operation needs to be an atomic read-modify-write. The write + itself is not an issue as it affects just the lower-order half but not bits + used in the add operation. To make the full fetch-and-xor atomic, we + exploit that concurrently, the value can increase by at most 1<<31 (*): The +@@ -97,117 +70,18 @@ __condvar_add_g1_start_relaxed (pthread_cond_t *cond, unsigned int val) + than __PTHREAD_COND_MAX_GROUP_SIZE waiters can enter concurrently and thus + increment __wseq. Therefore, if the xor operation observes a value of + __wseq, then the value it applies the modification to later on can be +- derived (see below). +- +- One benefit of this scheme is that this makes load operations +- obstruction-free because unlike if we would just lock the counter, readers +- can almost always interpret a snapshot of each halves. Readers can be +- forced to read a new snapshot when the read is concurrent with an overflow. +- However, overflows will happen infrequently, so load operations are +- practically lock-free. +- +- (*) The highest value we add is __PTHREAD_COND_MAX_GROUP_SIZE << 2 to +- __g1_start (the two extra bits are for the lock in the two LSBs of +- __g1_start). */ +- +-typedef struct +-{ +- unsigned int low; +- unsigned int high; +-} _condvar_lohi; +- +-static uint64_t +-__condvar_fetch_add_64_relaxed (_condvar_lohi *lh, unsigned int op) +-{ +- /* S1. Note that this is an atomic read-modify-write so it extends the +- release sequence of release MO store at S3. */ +- unsigned int l = atomic_fetch_add_relaxed (&lh->low, op); +- unsigned int h = atomic_load_relaxed (&lh->high); +- uint64_t result = ((uint64_t) h << 31) | l; +- l += op; +- if ((l >> 31) > 0) +- { +- /* Overflow. Need to increment higher-order half. Note that all +- add operations are ordered in happens-before. */ +- h++; +- /* S2. Release MO to synchronize with the loads of the higher-order half +- in the load operation. See __condvar_load_64_relaxed. */ +- atomic_store_release (&lh->high, h | ((unsigned int) 1 << 31)); +- l ^= (unsigned int) 1 << 31; +- /* S3. See __condvar_load_64_relaxed. */ +- atomic_store_release (&lh->low, l); +- /* S4. Likewise. */ +- atomic_store_release (&lh->high, h); +- } +- return result; +-} +- +-static uint64_t +-__condvar_load_64_relaxed (_condvar_lohi *lh) +-{ +- unsigned int h, l, h2; +- do +- { +- /* This load and the second one below to the same location read from the +- stores in the overflow handling of the add operation or the +- initializing stores (which is a simple special case because +- initialization always completely happens before further use). +- Because no two stores to the higher-order half write the same value, +- the loop ensures that if we continue to use the snapshot, this load +- and the second one read from the same store operation. All candidate +- store operations have release MO. +- If we read from S2 in the first load, then we will see the value of +- S1 on the next load (because we synchronize with S2), or a value +- later in modification order. We correctly ignore the lower-half's +- overflow bit in this case. If we read from S4, then we will see the +- value of S3 in the next load (or a later value), which does not have +- the overflow bit set anymore. +- */ +- h = atomic_load_acquire (&lh->high); +- /* This will read from the release sequence of S3 (i.e, either the S3 +- store or the read-modify-writes at S1 following S3 in modification +- order). Thus, the read synchronizes with S3, and the following load +- of the higher-order half will read from the matching S2 (or a later +- value). +- Thus, if we read a lower-half value here that already overflowed and +- belongs to an increased higher-order half value, we will see the +- latter and h and h2 will not be equal. */ +- l = atomic_load_acquire (&lh->low); +- /* See above. */ +- h2 = atomic_load_relaxed (&lh->high); +- } +- while (h != h2); +- if (((l >> 31) > 0) && ((h >> 31) > 0)) +- l ^= (unsigned int) 1 << 31; +- return ((uint64_t) (h & ~((unsigned int) 1 << 31)) << 31) + l; +-} +- +-static uint64_t __attribute__ ((unused)) +-__condvar_load_wseq_relaxed (pthread_cond_t *cond) +-{ +- return __condvar_load_64_relaxed ((_condvar_lohi *) &cond->__data.__wseq32); +-} +- +-static uint64_t __attribute__ ((unused)) +-__condvar_fetch_add_wseq_acquire (pthread_cond_t *cond, unsigned int val) +-{ +- uint64_t r = __condvar_fetch_add_64_relaxed +- ((_condvar_lohi *) &cond->__data.__wseq32, val); +- atomic_thread_fence_acquire (); +- return r; +-} ++ derived. */ + + static uint64_t __attribute__ ((unused)) + __condvar_fetch_xor_wseq_release (pthread_cond_t *cond, unsigned int val) + { +- _condvar_lohi *lh = (_condvar_lohi *) &cond->__data.__wseq32; + /* First, get the current value. See __condvar_load_64_relaxed. */ + unsigned int h, l, h2; + do + { +- h = atomic_load_acquire (&lh->high); +- l = atomic_load_acquire (&lh->low); +- h2 = atomic_load_relaxed (&lh->high); ++ h = atomic_load_acquire (&cond->__data.__wseq.__value32.__high); ++ l = atomic_load_acquire (&cond->__data.__wseq.__value32.__low); ++ h2 = atomic_load_relaxed (&cond->__data.__wseq.__value32.__high); + } + while (h != h2); + if (((l >> 31) > 0) && ((h >> 31) == 0)) +@@ -219,8 +93,9 @@ __condvar_fetch_xor_wseq_release (pthread_cond_t *cond, unsigned int val) + earlier in modification order than the following fetch-xor. + This uses release MO to make the full operation have release semantics + (all other operations access the lower-order half). */ +- unsigned int l2 = atomic_fetch_xor_release (&lh->low, val) +- & ~((unsigned int) 1 << 31); ++ unsigned int l2 ++ = (atomic_fetch_xor_release (&cond->__data.__wseq.__value32.__low, val) ++ & ~((unsigned int) 1 << 31)); + if (l2 < l) + /* The lower-order half overflowed in the meantime. This happened exactly + once due to the limit on concurrent waiters (see above). */ +@@ -228,22 +103,7 @@ __condvar_fetch_xor_wseq_release (pthread_cond_t *cond, unsigned int val) + return ((uint64_t) h << 31) + l2; + } + +-static uint64_t __attribute__ ((unused)) +-__condvar_load_g1_start_relaxed (pthread_cond_t *cond) +-{ +- return __condvar_load_64_relaxed +- ((_condvar_lohi *) &cond->__data.__g1_start32); +-} +- +-static void __attribute__ ((unused)) +-__condvar_add_g1_start_relaxed (pthread_cond_t *cond, unsigned int val) +-{ +- ignore_value (__condvar_fetch_add_64_relaxed +- ((_condvar_lohi *) &cond->__data.__g1_start32, val)); +-} +- +-#endif /* !__HAVE_64B_ATOMICS */ +- ++#endif /* !__HAVE_64B_ATOMICS */ + + /* The lock that signalers use. See pthread_cond_wait_common for uses. + The lock is our normal three-state lock: not acquired (0) / acquired (1) / +diff --git a/nptl/tst-cond22.c b/nptl/tst-cond22.c +index ebeeeaf666070076..bdcb45c53674a5fd 100644 +--- a/nptl/tst-cond22.c ++++ b/nptl/tst-cond22.c +@@ -106,8 +106,11 @@ do_test (void) + status = 1; + } + +- printf ("cond = { %llu, %llu, %u/%u, %u/%u, %u, %u }\n", +- c.__data.__wseq, c.__data.__g1_start, ++ printf ("cond = { 0x%x:%x, 0x%x:%x, %u/%u, %u/%u, %u, %u }\n", ++ c.__data.__wseq.__value32.__high, ++ c.__data.__wseq.__value32.__low, ++ c.__data.__g1_start.__value32.__high, ++ c.__data.__g1_start.__value32.__low, + c.__data.__g_signals[0], c.__data.__g_size[0], + c.__data.__g_signals[1], c.__data.__g_size[1], + c.__data.__g1_orig_size, c.__data.__wrefs); +@@ -149,8 +152,11 @@ do_test (void) + status = 1; + } + +- printf ("cond = { %llu, %llu, %u/%u, %u/%u, %u, %u }\n", +- c.__data.__wseq, c.__data.__g1_start, ++ printf ("cond = { 0x%x:%x, 0x%x:%x, %u/%u, %u/%u, %u, %u }\n", ++ c.__data.__wseq.__value32.__high, ++ c.__data.__wseq.__value32.__low, ++ c.__data.__g1_start.__value32.__high, ++ c.__data.__g1_start.__value32.__low, + c.__data.__g_signals[0], c.__data.__g_size[0], + c.__data.__g_signals[1], c.__data.__g_size[1], + c.__data.__g1_orig_size, c.__data.__wrefs); +diff --git a/sysdeps/nptl/bits/thread-shared-types.h b/sysdeps/nptl/bits/thread-shared-types.h +index 5644472323fe5424..2143281eabc7016c 100644 +--- a/sysdeps/nptl/bits/thread-shared-types.h ++++ b/sysdeps/nptl/bits/thread-shared-types.h +@@ -43,6 +43,8 @@ + + #include + ++#include ++ + + /* Common definition of pthread_mutex_t. */ + +@@ -91,24 +93,8 @@ typedef struct __pthread_internal_slist + + struct __pthread_cond_s + { +- __extension__ union +- { +- __extension__ unsigned long long int __wseq; +- struct +- { +- unsigned int __low; +- unsigned int __high; +- } __wseq32; +- }; +- __extension__ union +- { +- __extension__ unsigned long long int __g1_start; +- struct +- { +- unsigned int __low; +- unsigned int __high; +- } __g1_start32; +- }; ++ __atomic_wide_counter __wseq; ++ __atomic_wide_counter __g1_start; + unsigned int __glibc_unused___g_refs[2] __LOCK_ALIGNMENT; + unsigned int __g_size[2]; + unsigned int __g1_orig_size; diff --git a/glibc-RHEL-93320-3.patch b/glibc-RHEL-93320-3.patch new file mode 100644 index 0000000..b20fce4 --- /dev/null +++ b/glibc-RHEL-93320-3.patch @@ -0,0 +1,119 @@ +commit f1d333b5bfdb3561c93feb4b5653d051c3258c59 +Author: Florian Weimer +Date: Wed Nov 17 12:20:29 2021 +0100 + + elf: Introduce GLRO (dl_libc_freeres), called from __libc_freeres + + This will be used to deallocate memory allocated using the non-minimal + malloc. + + Reviewed-by: Adhemerval Zanella + +Conflicts: + elf/Makefile + Updated for change of layout + sysdeps/generic/ldsodefs.h + Updated for minor context changes + +diff --git a/elf/Makefile b/elf/Makefile +index 3eac746d21042ec9..f5c9b6df9a8f9acd 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -133,6 +133,7 @@ rtld-routines = \ + dl-hwcaps \ + dl-hwcaps-subdirs \ + dl-hwcaps_split \ ++ dl-libc_freeres \ + dl-minimal \ + dl-mutex \ + dl-sysdep \ +diff --git a/elf/dl-libc_freeres.c b/elf/dl-libc_freeres.c +new file mode 100644 +index 0000000000000000..68f305a6f98aac0c +--- /dev/null ++++ b/elf/dl-libc_freeres.c +@@ -0,0 +1,24 @@ ++/* Deallocating malloc'ed memory from the dynamic loader. ++ Copyright (C) 2021 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 ++ . */ ++ ++#include ++ ++void ++__rtld_libc_freeres (void) ++{ ++} +diff --git a/elf/rtld.c b/elf/rtld.c +index dac827e249b2fe14..fd70c4c3528cda2d 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -380,6 +380,7 @@ struct rtld_global_ro _rtld_global_ro attribute_relro = + ._dl_catch_error = _dl_catch_error, + ._dl_error_free = _dl_error_free, + ._dl_tls_get_addr_soft = _dl_tls_get_addr_soft, ++ ._dl_libc_freeres = __rtld_libc_freeres, + #ifdef HAVE_DL_DISCOVER_OSVERSION + ._dl_discover_osversion = _dl_discover_osversion + #endif +diff --git a/malloc/set-freeres.c b/malloc/set-freeres.c +index 5c19a2725cdb61e7..856ff7831f84d07c 100644 +--- a/malloc/set-freeres.c ++++ b/malloc/set-freeres.c +@@ -21,6 +21,7 @@ + #include + #include + #include ++#include + + #include "../nss/nsswitch.h" + #include "../libio/libioP.h" +@@ -67,6 +68,10 @@ __libc_freeres (void) + + call_function_static_weak (__libc_dlerror_result_free); + ++#ifdef SHARED ++ GLRO (dl_libc_freeres) (); ++#endif ++ + for (p = symbol_set_first_element (__libc_freeres_ptrs); + !symbol_set_end_p (__libc_freeres_ptrs, p); ++p) + free (*p); +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index 484893c2928db8e7..9142bc8f493bce64 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -713,6 +713,10 @@ struct rtld_global_ro + namespace. */ + void (*_dl_error_free) (void *); + void *(*_dl_tls_get_addr_soft) (struct link_map *); ++ ++ /* Called from __libc_shared to deallocate malloc'ed memory. */ ++ void (*_dl_libc_freeres) (void); ++ + #ifdef HAVE_DL_DISCOVER_OSVERSION + int (*_dl_discover_osversion) (void); + #endif +@@ -1503,6 +1507,9 @@ __rtld_mutex_init (void) + } + #endif /* !PTHREAD_IN_LIBC */ + ++/* Implementation of GL (dl_libc_freeres). */ ++void __rtld_libc_freeres (void) attribute_hidden; ++ + #if THREAD_GSCOPE_IN_TCB + void __thread_gscope_wait (void) attribute_hidden; + # define THREAD_GSCOPE_WAIT() __thread_gscope_wait () diff --git a/glibc-RHEL-93320-4.patch b/glibc-RHEL-93320-4.patch new file mode 100644 index 0000000..2d7117b --- /dev/null +++ b/glibc-RHEL-93320-4.patch @@ -0,0 +1,2764 @@ +commit 5d28a8962dcb6ec056b81d730e3c6fb57185a210 +Author: Florian Weimer +Date: Tue Dec 28 22:52:56 2021 +0100 + + elf: Add _dl_find_object function + + It can be used to speed up the libgcc unwinder, and the internal + _dl_find_dso_for_object function (which is used for caller + identification in dlopen and related functions, and in dladdr). + + _dl_find_object is in the internal namespace due to bug 28503. + If libgcc switches to _dl_find_object, this namespace issue will + be fixed. It is located in libc for two reasons: it is necessary + to forward the call to the static libc after static dlopen, and + there is a link ordering issue with -static-libgcc and libgcc_eh.a + because libc.so is not a linker script that includes ld.so in the + glibc build tree (so that GCC's internal -lc after libgcc_eh.a does + not pick up ld.so). + + It is necessary to do the i386 customization in the + sysdeps/x86/bits/dl_find_object.h header shared with x86-64 because + otherwise, multilib installations are broken. + + The implementation uses software transactional memory, as suggested + by Torvald Riegel. Two copies of the supporting data structures are + used, also achieving full async-signal-safety. + + Reviewed-by: Adhemerval Zanella + +Conflicts: + elf/Makefile + Updated for change of layout + elf/dl-support.c + Updated for minor context changes + elf/rtld.c + Updated for context changes caused by the existing + backport of upstream 706209867f1ba89c458033408d419e92d8055f58 + "elf: Second ld.so relocation only if libc.so has been loaded" + include/link.h + Updated for minor context changes + manual/dynlink.texi + Already added by glibc-RHEL-22165-1.patch, folded in the + typo fix from upstream + 6cf4ebe10c6f0f60cdfce98f5a0ec7c5ceb987df + sysdeps/mach/hurd/i386/libc.abilist + Updated for minor context changes + +diff --git a/bits/dl_find_object.h b/bits/dl_find_object.h +new file mode 100644 +index 0000000000000000..5d652c9c7144b678 +--- /dev/null ++++ b/bits/dl_find_object.h +@@ -0,0 +1,32 @@ ++/* System dependent definitions for finding objects by address. ++ Copyright (C) 2021 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 ++ . */ ++ ++#ifndef _DLFCN_H ++# error "Never use directly; include instead." ++#endif ++ ++/* This implementation does not have a dlfo_eh_dbase member in struct ++ dl_find_object. */ ++#define DLFO_STRUCT_HAS_EH_DBASE 0 ++ ++/* This implementation does not have a dlfo_eh_count member in struct ++ dl_find_object. */ ++#define DLFO_STRUCT_HAS_EH_COUNT 0 ++ ++/* The ELF segment which contains the exception handling data. */ ++#define DLFO_EH_SEGMENT_TYPE PT_GNU_EH_FRAME +diff --git a/dlfcn/Makefile b/dlfcn/Makefile +index d3965427dabed898..3466669f18577b2e 100644 +--- a/dlfcn/Makefile ++++ b/dlfcn/Makefile +@@ -19,7 +19,7 @@ subdir := dlfcn + + include ../Makeconfig + +-headers := bits/dlfcn.h dlfcn.h ++headers := bits/dlfcn.h bits/dl_find_object.h dlfcn.h + extra-libs := libdl + libdl-routines := libdl-compat + routines = \ +diff --git a/dlfcn/dlfcn.h b/dlfcn/dlfcn.h +index 24388cfedae4dd67..8168a71dbfe90e75 100644 +--- a/dlfcn/dlfcn.h ++++ b/dlfcn/dlfcn.h +@@ -28,6 +28,8 @@ + + + #ifdef __USE_GNU ++#include ++ + /* If the first argument of `dlsym' or `dlvsym' is set to RTLD_NEXT + the run-time address of the symbol called NAME in the next shared + object is returned. The "next" relation is defined by the order +@@ -199,6 +201,31 @@ typedef struct + Dl_serpath dls_serpath[1]; /* Actually longer, dls_cnt elements. */ + # endif + } Dl_serinfo; ++ ++struct dl_find_object ++{ ++ __extension__ unsigned long long int dlfo_flags; ++ void *dlfo_map_start; /* Beginning of mapping containing address. */ ++ void *dlfo_map_end; /* End of mapping. */ ++ struct link_map *dlfo_link_map; ++ void *dlfo_eh_frame; /* Exception handling data of the object. */ ++# if DLFO_STRUCT_HAS_EH_DBASE ++ void *dlfo_eh_dbase; /* Base address for DW_EH_PE_datarel. */ ++# if __WORDSIZE == 32 ++ unsigned int __dlfo_eh_dbase_pad; ++# endif ++# endif ++# if DLFO_STRUCT_HAS_EH_COUNT ++ int dlfo_eh_count; /* Number of exception handling entries. */ ++ unsigned int __dlfo_eh_count_pad; ++# endif ++ __extension__ unsigned long long int __dflo_reserved[7]; ++}; ++ ++/* If ADDRESS is found in an object, fill in *RESULT and return 0. ++ Otherwise, return -1. */ ++int _dl_find_object (void *__address, struct dl_find_object *__result) __THROW; ++ + #endif /* __USE_GNU */ + + +diff --git a/elf/Makefile b/elf/Makefile +index f5c9b6df9a8f9acd..e1e6107c277f5f76 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -43,6 +43,7 @@ routines = \ + dl-sym \ + dl-sysdep \ + enbl-secure \ ++ libc-dl_find_object \ + libc_early_init \ + rtld_static_init \ + # routines +@@ -58,6 +59,7 @@ dl-routines = \ + dl-deps \ + dl-exception \ + dl-execstack \ ++ dl-find_object \ + dl-fini \ + dl-init \ + dl-load \ +@@ -119,6 +121,9 @@ elide-routines.os = \ + thread_gscope_wait \ + # elide-routines.os + ++# These object files are only included in the dynamically-linked libc. ++shared-only-routines = libc-dl_find_object ++ + # ld.so uses those routines, plus some special stuff for being the program + # interpreter and operating independent of libc. + rtld-routines = \ +@@ -280,6 +285,7 @@ tests-static-internal := \ + tst-stackguard1-static \ + tst-tls1-static \ + tst-tls1-static-non-pie \ ++ tst-dl_find_object-static \ + # tests-static-internal + + CRT-tst-tls1-static-non-pie := $(csu-objpfx)crt1.o +@@ -520,6 +526,7 @@ tests-internal += \ + tst-tls8 \ + unload \ + unload2 \ ++ tst-dl_find_object tst-dl_find_object-threads \ + # tests-internal + + tests-container += \ +@@ -781,6 +788,16 @@ modules-names = \ + tst-deep1mod1 \ + tst-deep1mod2 \ + tst-deep1mod3 \ ++ tst-dl_find_object-mod1 \ ++ tst-dl_find_object-mod2 \ ++ tst-dl_find_object-mod3 \ ++ tst-dl_find_object-mod4 \ ++ tst-dl_find_object-mod5 \ ++ tst-dl_find_object-mod6 \ ++ tst-dl_find_object-mod7 \ ++ tst-dl_find_object-mod8 \ ++ tst-dl_find_object-mod9 \ ++ tst-dlmopen1mod \ + tst-dlclose-lazy-mod1 \ + tst-dlclose-lazy-mod2 \ + tst-dlmopen-dlerror-mod \ +@@ -2772,6 +2789,37 @@ $(objpfx)tst-ro-dynamic-mod.so: $(objpfx)tst-ro-dynamic-mod.os \ + + $(objpfx)tst-rtld-run-static.out: $(objpfx)ldconfig + ++$(objpfx)tst-dl_find_object.out: \ ++ $(objpfx)tst-dl_find_object-mod1.so $(objpfx)tst-dl_find_object-mod2.so ++$(objpfx)tst-dl_find_object-static.out: \ ++ $(objpfx)tst-dl_find_object-mod1.so $(objpfx)tst-dl_find_object-mod2.so ++tst-dl_find_object-static-ENV = $(static-dlopen-environment) ++CFLAGS-tst-dl_find_object.c += -funwind-tables ++CFLAGS-tst-dl_find_object-static.c += -funwind-tables ++LDFLAGS-tst-dl_find_object-static += -Wl,--eh-frame-hdr ++CFLAGS-tst-dl_find_object-mod1.c += -funwind-tables ++CFLAGS-tst-dl_find_object-mod2.c += -funwind-tables ++LDFLAGS-tst-dl_find_object-mod2.so += -Wl,--enable-new-dtags,-z,nodelete ++$(objpfx)tst-dl_find_object-threads: $(shared-thread-library) ++CFLAGS-tst-dl_find_object-threads.c += -funwind-tables ++$(objpfx)tst-dl_find_object-threads.out: \ ++ $(objpfx)tst-dl_find_object-mod1.so \ ++ $(objpfx)tst-dl_find_object-mod2.so \ ++ $(objpfx)tst-dl_find_object-mod3.so \ ++ $(objpfx)tst-dl_find_object-mod4.so \ ++ $(objpfx)tst-dl_find_object-mod5.so \ ++ $(objpfx)tst-dl_find_object-mod6.so \ ++ $(objpfx)tst-dl_find_object-mod7.so \ ++ $(objpfx)tst-dl_find_object-mod8.so \ ++ $(objpfx)tst-dl_find_object-mod9.so ++CFLAGS-tst-dl_find_object-mod3.c += -funwind-tables ++CFLAGS-tst-dl_find_object-mod4.c += -funwind-tables ++CFLAGS-tst-dl_find_object-mod5.c += -funwind-tables ++CFLAGS-tst-dl_find_object-mod6.c += -funwind-tables ++CFLAGS-tst-dl_find_object-mod7.c += -funwind-tables ++CFLAGS-tst-dl_find_object-mod8.c += -funwind-tables ++CFLAGS-tst-dl_find_object-mod9.c += -funwind-tables ++ + $(objpfx)tst-tls-allocation-failure-static-patched: \ + $(objpfx)tst-tls-allocation-failure-static $(..)scripts/tst-elf-edit.py + cp $< $@ +diff --git a/elf/Versions b/elf/Versions +index 17834c7d1c371e1d..71d648a6b4bcbf5a 100644 +--- a/elf/Versions ++++ b/elf/Versions +@@ -20,6 +20,9 @@ libc { + __register_frame_info_table_bases; _Unwind_Find_FDE; + } + %endif ++ GLIBC_2.35 { ++ _dl_find_object; ++ } + GLIBC_PRIVATE { + # functions used in other libraries + __libc_early_init; +diff --git a/elf/dl-close.c b/elf/dl-close.c +index fa3974afba798073..4514c53d2db261c1 100644 +--- a/elf/dl-close.c ++++ b/elf/dl-close.c +@@ -32,6 +32,7 @@ + #include + #include + #include ++#include + + #include + +@@ -694,6 +695,9 @@ _dl_close_worker (struct link_map *map, bool force) + if (imap->l_next != NULL) + imap->l_next->l_prev = imap->l_prev; + ++ /* Update the data used by _dl_find_object. */ ++ _dl_find_object_dlclose (imap); ++ + free (imap->l_versions); + if (imap->l_origin != (char *) -1) + free ((char *) imap->l_origin); +diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c +new file mode 100644 +index 0000000000000000..324f40742d59b4dc +--- /dev/null ++++ b/elf/dl-find_object.c +@@ -0,0 +1,842 @@ ++/* Locating objects in the process image. ld.so implementation. ++ Copyright (C) 2021 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Fallback implementation of _dl_find_object. It uses a linear ++ search, needs locking, and is not async-signal-safe. It is used in ++ _dl_find_object prior to initialization, when called from audit ++ modules. It also serves as the reference implementation for ++ _dl_find_object. */ ++static int ++_dl_find_object_slow (void *pc, struct dl_find_object *result) ++{ ++ ElfW(Addr) addr = (ElfW(Addr)) pc; ++ for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns) ++ for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL; ++ l = l->l_next) ++ if (addr >= l->l_map_start && addr < l->l_map_end ++ && (l->l_contiguous || _dl_addr_inside_object (l, addr))) ++ { ++ assert (ns == l->l_ns); ++ struct dl_find_object_internal internal; ++ _dl_find_object_from_map (l, &internal); ++ _dl_find_object_to_external (&internal, result); ++ return 1; ++ } ++ ++ /* Object not found. */ ++ return -1; ++} ++ ++/* Data for the main executable. There is usually a large gap between ++ the main executable and initially loaded shared objects. Record ++ the main executable separately, to increase the chance that the ++ range for the non-closeable mappings below covers only the shared ++ objects (and not also the gap between main executable and shared ++ objects). */ ++static struct dl_find_object_internal _dlfo_main attribute_relro; ++ ++/* Data for initially loaded shared objects that cannot be unloaded. ++ (This may also contain non-contiguous mappings from the main ++ executable.) The mappings are stored in address order in the ++ _dlfo_nodelete_mappings array (containing ++ _dlfo_nodelete_mappings_size elements). It is not modified after ++ initialization. */ ++static uintptr_t _dlfo_nodelete_mappings_end attribute_relro; ++static size_t _dlfo_nodelete_mappings_size attribute_relro; ++static struct dl_find_object_internal *_dlfo_nodelete_mappings ++ attribute_relro; ++ ++/* Mappings created by dlopen can go away with dlclose, so a dynamic ++ data structure with some synchronization is needed. Individual ++ segments are similar to the _dlfo_nodelete_mappings array above. ++ The previous segment contains lower addresses and is at most half ++ as long. Checking the address of the base address of the first ++ element during a lookup can therefore approximate a binary search ++ over all segments, even though the data is not stored in one ++ contiguous array. ++ ++ During updates, the segments are overwritten in place, and a ++ software transactional memory construct (involving the ++ _dlfo_loaded_mappings_version variable) is used to detect ++ concurrent modification, and retry as necessary. The memory ++ allocations are never deallocated, but slots used for objects that ++ have been dlclose'd can be reused by dlopen. The memory can live ++ in the regular C malloc heap. ++ ++ The segments are populated from the start of the list, with the ++ mappings with the highest address. Only if this segment is full, ++ previous segments are used for mappings at lower addresses. The ++ remaining segments are populated as needed, but after allocating ++ further segments, some of the initial segments (at the end of the ++ linked list) can be empty (with size 0). ++ ++ Adding new elements to this data structure is another source of ++ quadratic behavior for dlopen. If the other causes of quadratic ++ behavior are eliminated, a more complicated data structure will be ++ needed. */ ++struct dlfo_mappings_segment ++{ ++ /* The previous segment has lower base addresses. */ ++ struct dlfo_mappings_segment *previous; ++ ++ /* Used by __libc_freeres to deallocate malloc'ed memory. */ ++ void *to_free; ++ ++ /* Count of array elements in use and allocated. */ ++ size_t size; ++ size_t allocated; ++ ++ struct dl_find_object_internal objects[]; ++}; ++ ++/* To achieve async-signal-safety, two copies of the data structure ++ are used, so that a signal handler can still use this data even if ++ dlopen or dlclose modify the other copy. The the MSB in ++ _dlfo_loaded_mappings_version determines which array element is the ++ currently active region. */ ++static struct dlfo_mappings_segment *_dlfo_loaded_mappings[2]; ++ ++/* Returns the number of actually used elements in all segments ++ starting at SEG. */ ++static inline size_t ++_dlfo_mappings_segment_count_used (struct dlfo_mappings_segment *seg) ++{ ++ size_t count = 0; ++ for (; seg != NULL && seg->size > 0; seg = seg->previous) ++ for (size_t i = 0; i < seg->size; ++i) ++ /* Exclude elements which have been dlclose'd. */ ++ count += seg->objects[i].map != NULL; ++ return count; ++} ++ ++/* Compute the total number of available allocated segments linked ++ from SEG. */ ++static inline size_t ++_dlfo_mappings_segment_count_allocated (struct dlfo_mappings_segment *seg) ++{ ++ size_t count = 0; ++ for (; seg != NULL; seg = seg->previous) ++ count += seg->allocated; ++ return count; ++} ++ ++/* This is essentially an arbitrary value. dlopen allocates plenty of ++ memory anyway, so over-allocated a bit does not hurt. Not having ++ many small-ish segments helps to avoid many small binary searches. ++ Not using a power of 2 means that we do not waste an extra page ++ just for the malloc header if a mapped allocation is used in the ++ glibc allocator. */ ++enum { dlfo_mappings_initial_segment_size = 63 }; ++ ++/* Allocate an empty segment. This used for the first ever ++ allocation. */ ++static struct dlfo_mappings_segment * ++_dlfo_mappings_segment_allocate_unpadded (size_t size) ++{ ++ if (size < dlfo_mappings_initial_segment_size) ++ size = dlfo_mappings_initial_segment_size; ++ /* No overflow checks here because the size is a mapping count, and ++ struct link_map is larger than what we allocate here. */ ++ enum ++ { ++ element_size = sizeof ((struct dlfo_mappings_segment) {}.objects[0]) ++ }; ++ size_t to_allocate = (sizeof (struct dlfo_mappings_segment) ++ + size * element_size); ++ struct dlfo_mappings_segment *result = malloc (to_allocate); ++ if (result != NULL) ++ { ++ result->previous = NULL; ++ result->to_free = NULL; /* Minimal malloc memory cannot be freed. */ ++ result->size = 0; ++ result->allocated = size; ++ } ++ return result; ++} ++ ++/* Allocate an empty segment that is at least SIZE large. PREVIOUS ++ points to the chain of previously allocated segments and can be ++ NULL. */ ++static struct dlfo_mappings_segment * ++_dlfo_mappings_segment_allocate (size_t size, ++ struct dlfo_mappings_segment * previous) ++{ ++ /* Exponential sizing policies, so that lookup approximates a binary ++ search. */ ++ { ++ size_t minimum_growth; ++ if (previous == NULL) ++ minimum_growth = dlfo_mappings_initial_segment_size; ++ else ++ minimum_growth = 2* previous->allocated; ++ if (size < minimum_growth) ++ size = minimum_growth; ++ } ++ enum { cache_line_size_estimate = 128 }; ++ /* No overflow checks here because the size is a mapping count, and ++ struct link_map is larger than what we allocate here. */ ++ enum ++ { ++ element_size = sizeof ((struct dlfo_mappings_segment) {}.objects[0]) ++ }; ++ size_t to_allocate = (sizeof (struct dlfo_mappings_segment) ++ + size * element_size ++ + 2 * cache_line_size_estimate); ++ char *ptr = malloc (to_allocate); ++ if (ptr == NULL) ++ return NULL; ++ char *original_ptr = ptr; ++ /* Start and end at a (conservative) 128-byte cache line boundary. ++ Do not use memalign for compatibility with partially interposing ++ malloc implementations. */ ++ char *end = PTR_ALIGN_DOWN (ptr + to_allocate, cache_line_size_estimate); ++ ptr = PTR_ALIGN_UP (ptr, cache_line_size_estimate); ++ struct dlfo_mappings_segment *result ++ = (struct dlfo_mappings_segment *) ptr; ++ result->previous = previous; ++ result->to_free = original_ptr; ++ result->size = 0; ++ /* We may have obtained slightly more space if malloc happened ++ to provide an over-aligned pointer. */ ++ result->allocated = (((uintptr_t) (end - ptr) ++ - sizeof (struct dlfo_mappings_segment)) ++ / element_size); ++ assert (result->allocated >= size); ++ return result; ++} ++ ++/* Monotonic counter for software transactional memory. The lowest ++ bit indicates which element of the _dlfo_loaded_mappings contains ++ up-to-date data. */ ++static __atomic_wide_counter _dlfo_loaded_mappings_version; ++ ++/* TM version at the start of the read operation. */ ++static inline uint64_t ++_dlfo_read_start_version (void) ++{ ++ /* Acquire MO load synchronizes with the fences at the beginning and ++ end of the TM update region. */ ++ return __atomic_wide_counter_load_acquire (&_dlfo_loaded_mappings_version); ++} ++ ++/* Optimized variant of _dlfo_read_start_version which can be called ++ when the loader is write-locked. */ ++static inline uint64_t ++_dlfo_read_version_locked (void) ++{ ++ return __atomic_wide_counter_load_relaxed (&_dlfo_loaded_mappings_version); ++} ++ ++/* Update the version to reflect that an update is happening. This ++ does not change the bit that controls the active segment chain. ++ Returns the index of the currently active segment chain. */ ++static inline unsigned int ++_dlfo_mappings_begin_update (void) ++{ ++ unsigned int v ++ = __atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version, ++ 2); ++ /* Subsequent stores to the TM data must not be reordered before the ++ store above with the version update. */ ++ atomic_thread_fence_release (); ++ return v & 1; ++} ++ ++/* Installs the just-updated version as the active version. */ ++static inline void ++_dlfo_mappings_end_update (void) ++{ ++ /* The previous writes to the TM data must not be reordered after ++ the version update below. */ ++ atomic_thread_fence_release (); ++ __atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version, ++ 1); ++} ++/* Completes an in-place update without switching versions. */ ++static inline void ++_dlfo_mappings_end_update_no_switch (void) ++{ ++ /* The previous writes to the TM data must not be reordered after ++ the version update below. */ ++ atomic_thread_fence_release (); ++ __atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version, ++ 2); ++} ++ ++/* Return true if the read was successful, given the start ++ version. */ ++static inline bool ++_dlfo_read_success (uint64_t start_version) ++{ ++ return _dlfo_read_start_version () == start_version; ++} ++ ++/* Returns the active segment identified by the specified start ++ version. */ ++static struct dlfo_mappings_segment * ++_dlfo_mappings_active_segment (uint64_t start_version) ++{ ++ return _dlfo_loaded_mappings[start_version & 1]; ++} ++ ++/* Searches PC amoung the address-sorted array [FIRST1, FIRST1 + ++ SIZE). Assumes PC >= FIRST1->map_start. Returns a pointer to the ++ element that contains PC, or NULL if there is no such element. */ ++static inline struct dl_find_object_internal * ++_dlfo_lookup (uintptr_t pc, struct dl_find_object_internal *first1, size_t size) ++{ ++ struct dl_find_object_internal *end = first1 + size; ++ ++ /* Search for a lower bound in first. */ ++ struct dl_find_object_internal *first = first1; ++ while (size > 0) ++ { ++ size_t half = size >> 1; ++ struct dl_find_object_internal *middle = first + half; ++ if (middle->map_start < pc) ++ { ++ first = middle + 1; ++ size -= half + 1; ++ } ++ else ++ size = half; ++ } ++ ++ if (first != end && pc == first->map_start) ++ { ++ if (pc < first->map_end) ++ return first; ++ else ++ /* Zero-length mapping after dlclose. */ ++ return NULL; ++ } ++ else ++ { ++ /* Check to see if PC is in the previous mapping. */ ++ --first; ++ if (pc < first->map_end) ++ /* pc >= first->map_start implied by the search above. */ ++ return first; ++ else ++ return NULL; ++ } ++} ++ ++int ++_dl_find_object (void *pc1, struct dl_find_object *result) ++{ ++ uintptr_t pc = (uintptr_t) pc1; ++ ++ if (__glibc_unlikely (_dlfo_main.map_end == 0)) ++ { ++ /* Not initialized. No locking is needed here because this can ++ only be called from audit modules, which cannot create ++ threads. */ ++ return _dl_find_object_slow (pc1, result); ++ } ++ ++ /* Main executable. */ ++ if (pc >= _dlfo_main.map_start && pc < _dlfo_main.map_end) ++ { ++ _dl_find_object_to_external (&_dlfo_main, result); ++ return 0; ++ } ++ ++ /* Other initially loaded objects. */ ++ if (pc >= _dlfo_nodelete_mappings->map_start ++ && pc < _dlfo_nodelete_mappings_end) ++ { ++ struct dl_find_object_internal *obj ++ = _dlfo_lookup (pc, _dlfo_nodelete_mappings, ++ _dlfo_nodelete_mappings_size); ++ if (obj != NULL) ++ { ++ _dl_find_object_to_external (obj, result); ++ return 0; ++ } ++ /* Fall through to the full search. The kernel may have mapped ++ the initial mappings with gaps that are later filled by ++ dlopen with other mappings. */ ++ } ++ ++ /* Handle audit modules, dlopen, dlopen objects. This uses software ++ transactional memory, with a retry loop in case the version ++ changes during execution. */ ++ while (true) ++ { ++ retry: ++ ; ++ uint64_t start_version = _dlfo_read_start_version (); ++ ++ /* The read through seg->previous assumes that the CPU ++ recognizes the load dependency, so that no invalid size ++ values is read. Furthermore, the code assumes that no ++ out-of-thin-air value for seg->size is observed. Together, ++ this ensures that the observed seg->size value is always less ++ than seg->allocated, so that _dlfo_mappings_index does not ++ read out-of-bounds. (This avoids intermediate TM version ++ verification. A concurrent version update will lead to ++ invalid lookup results, but not to out-of-memory access.) ++ ++ Either seg == NULL or seg->size == 0 terminates the segment ++ list. _dl_find_object_update does not bother to clear the ++ size on earlier unused segments. */ ++ for (struct dlfo_mappings_segment *seg ++ = _dlfo_mappings_active_segment (start_version); ++ seg != NULL && seg->size > 0; seg = seg->previous) ++ if (pc >= seg->objects[0].map_start) ++ { ++ /* PC may lie within this segment. If it is less than the ++ segment start address, it can only lie in a previous ++ segment, due to the base address sorting. */ ++ struct dl_find_object_internal *obj ++ = _dlfo_lookup (pc, seg->objects, seg->size); ++ ++ if (obj != NULL) ++ { ++ /* Found the right mapping. Copy out the data prior to ++ checking if the read transaction was successful. */ ++ struct dl_find_object_internal copy = *obj; ++ if (_dlfo_read_success (start_version)) ++ { ++ _dl_find_object_to_external (©, result); ++ return 0; ++ } ++ else ++ /* Read transaction failure. */ ++ goto retry; ++ } ++ else ++ { ++ /* PC is not covered by this mapping. */ ++ if (_dlfo_read_success (start_version)) ++ return -1; ++ else ++ /* Read transaction failure. */ ++ goto retry; ++ } ++ } /* if: PC might lie within the current seg. */ ++ ++ /* PC is not covered by any segment. */ ++ if (_dlfo_read_success (start_version)) ++ return -1; ++ } /* Transaction retry loop. */ ++} ++rtld_hidden_def (_dl_find_object) ++ ++/* _dlfo_process_initial is called twice. First to compute the array ++ sizes from the initial loaded mappings. Second to fill in the ++ bases and infos arrays with the (still unsorted) data. Returns the ++ number of loaded (non-nodelete) mappings. */ ++static size_t ++_dlfo_process_initial (void) ++{ ++ struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; ++ ++ size_t nodelete = 0; ++ if (!main_map->l_contiguous) ++ { ++ struct dl_find_object_internal dlfo; ++ _dl_find_object_from_map (main_map, &dlfo); ++ ++ /* PT_LOAD segments for a non-contiguous are added to the ++ non-closeable mappings. */ ++ for (const ElfW(Phdr) *ph = main_map->l_phdr, ++ *ph_end = main_map->l_phdr + main_map->l_phnum; ++ ph < ph_end; ++ph) ++ if (ph->p_type == PT_LOAD) ++ { ++ if (_dlfo_nodelete_mappings != NULL) ++ { ++ /* Second pass only. */ ++ _dlfo_nodelete_mappings[nodelete] = dlfo; ++ _dlfo_nodelete_mappings[nodelete].map_start ++ = ph->p_vaddr + main_map->l_addr; ++ _dlfo_nodelete_mappings[nodelete].map_end ++ = _dlfo_nodelete_mappings[nodelete].map_start + ph->p_memsz; ++ } ++ ++nodelete; ++ } ++ } ++ ++ size_t loaded = 0; ++ for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns) ++ for (struct link_map *l = GL(dl_ns)[ns]._ns_loaded; l != NULL; ++ l = l->l_next) ++ /* Skip the main map processed above, and proxy maps. */ ++ if (l != main_map && l == l->l_real) ++ { ++ /* lt_library link maps are implicitly NODELETE. */ ++ if (l->l_type == lt_library || l->l_nodelete_active) ++ { ++ if (_dlfo_nodelete_mappings != NULL) ++ /* Second pass only. */ ++ _dl_find_object_from_map ++ (l, _dlfo_nodelete_mappings + nodelete); ++ ++nodelete; ++ } ++ else if (l->l_type == lt_loaded) ++ { ++ if (_dlfo_loaded_mappings[0] != NULL) ++ /* Second pass only. */ ++ _dl_find_object_from_map ++ (l, &_dlfo_loaded_mappings[0]->objects[loaded]); ++ ++loaded; ++ } ++ } ++ ++ _dlfo_nodelete_mappings_size = nodelete; ++ return loaded; ++} ++ ++/* Selection sort based on mapping start address. */ ++void ++_dlfo_sort_mappings (struct dl_find_object_internal *objects, size_t size) ++{ ++ if (size < 2) ++ return; ++ ++ for (size_t i = 0; i < size - 1; ++i) ++ { ++ /* Find minimum. */ ++ size_t min_idx = i; ++ uintptr_t min_val = objects[i].map_start; ++ for (size_t j = i + 1; j < size; ++j) ++ if (objects[j].map_start < min_val) ++ { ++ min_idx = j; ++ min_val = objects[j].map_start; ++ } ++ ++ /* Swap into place. */ ++ struct dl_find_object_internal tmp = objects[min_idx]; ++ objects[min_idx] = objects[i]; ++ objects[i] = tmp; ++ } ++} ++ ++void ++_dl_find_object_init (void) ++{ ++ /* Cover the main mapping. */ ++ { ++ struct link_map *main_map = GL(dl_ns)[LM_ID_BASE]._ns_loaded; ++ ++ if (main_map->l_contiguous) ++ _dl_find_object_from_map (main_map, &_dlfo_main); ++ else ++ { ++ /* Non-contiguous main maps are handled in ++ _dlfo_process_initial. Mark as initialized, but not ++ coverying any valid PC. */ ++ _dlfo_main.map_start = -1; ++ _dlfo_main.map_end = -1; ++ } ++ } ++ ++ /* Allocate the data structures. */ ++ size_t loaded_size = _dlfo_process_initial (); ++ _dlfo_nodelete_mappings = malloc (_dlfo_nodelete_mappings_size ++ * sizeof (*_dlfo_nodelete_mappings)); ++ if (loaded_size > 0) ++ _dlfo_loaded_mappings[0] ++ = _dlfo_mappings_segment_allocate_unpadded (loaded_size); ++ if (_dlfo_nodelete_mappings == NULL ++ || (loaded_size > 0 && _dlfo_loaded_mappings[0] == NULL)) ++ _dl_fatal_printf ("\ ++Fatal glibc error: cannot allocate memory for find-object data\n"); ++ /* Fill in the data with the second call. */ ++ _dlfo_nodelete_mappings_size = 0; ++ _dlfo_process_initial (); ++ ++ /* Sort both arrays. */ ++ if (_dlfo_nodelete_mappings_size > 0) ++ { ++ _dlfo_sort_mappings (_dlfo_nodelete_mappings, ++ _dlfo_nodelete_mappings_size); ++ size_t last_idx = _dlfo_nodelete_mappings_size - 1; ++ _dlfo_nodelete_mappings_end = _dlfo_nodelete_mappings[last_idx].map_end; ++ } ++ if (loaded_size > 0) ++ _dlfo_sort_mappings (_dlfo_loaded_mappings[0]->objects, ++ _dlfo_loaded_mappings[0]->size); ++} ++ ++static void ++_dl_find_object_link_map_sort (struct link_map **loaded, size_t size) ++{ ++ /* Selection sort based on map_start. */ ++ if (size < 2) ++ return; ++ for (size_t i = 0; i < size - 1; ++i) ++ { ++ /* Find minimum. */ ++ size_t min_idx = i; ++ ElfW(Addr) min_val = loaded[i]->l_map_start; ++ for (size_t j = i + 1; j < size; ++j) ++ if (loaded[j]->l_map_start < min_val) ++ { ++ min_idx = j; ++ min_val = loaded[j]->l_map_start; ++ } ++ ++ /* Swap into place. */ ++ struct link_map *tmp = loaded[min_idx]; ++ loaded[min_idx] = loaded[i]; ++ loaded[i] = tmp; ++ } ++} ++ ++/* Initializes the segment for writing. Returns the target write ++ index (plus 1) in this segment. The index is chosen so that a ++ partially filled segment still has data at index 0. */ ++static inline size_t ++_dlfo_update_init_seg (struct dlfo_mappings_segment *seg, ++ size_t remaining_to_add) ++{ ++ if (remaining_to_add < seg->allocated) ++ /* Partially filled segment. */ ++ seg->size = remaining_to_add; ++ else ++ seg->size = seg->allocated; ++ return seg->size; ++} ++ ++/* Invoked from _dl_find_object_update after sorting. */ ++static bool ++_dl_find_object_update_1 (struct link_map **loaded, size_t count) ++{ ++ int active_idx = _dlfo_read_version_locked () & 1; ++ ++ struct dlfo_mappings_segment *current_seg ++ = _dlfo_loaded_mappings[active_idx]; ++ size_t current_used = _dlfo_mappings_segment_count_used (current_seg); ++ ++ struct dlfo_mappings_segment *target_seg ++ = _dlfo_loaded_mappings[!active_idx]; ++ size_t remaining_to_add = current_used + count; ++ ++ /* Ensure that the new segment chain has enough space. */ ++ { ++ size_t new_allocated ++ = _dlfo_mappings_segment_count_allocated (target_seg); ++ if (new_allocated < remaining_to_add) ++ { ++ size_t more = remaining_to_add - new_allocated; ++ target_seg = _dlfo_mappings_segment_allocate (more, target_seg); ++ if (target_seg == NULL) ++ /* Out of memory. Do not end the update and keep the ++ current version unchanged. */ ++ return false; ++ ++ /* Start update cycle. */ ++ _dlfo_mappings_begin_update (); ++ ++ /* The barrier ensures that a concurrent TM read or fork does ++ not see a partially initialized segment. */ ++ atomic_store_release (&_dlfo_loaded_mappings[!active_idx], target_seg); ++ } ++ else ++ /* Start update cycle without allocation. */ ++ _dlfo_mappings_begin_update (); ++ } ++ ++ size_t target_seg_index1 = _dlfo_update_init_seg (target_seg, ++ remaining_to_add); ++ ++ /* Merge the current_seg segment list with the loaded array into the ++ target_set. Merging occurs backwards, in decreasing l_map_start ++ order. */ ++ size_t loaded_index1 = count; ++ size_t current_seg_index1; ++ if (current_seg == NULL) ++ current_seg_index1 = 0; ++ else ++ current_seg_index1 = current_seg->size; ++ while (true) ++ { ++ if (current_seg_index1 == 0) ++ { ++ /* Switch to the previous segment. */ ++ if (current_seg != NULL) ++ current_seg = current_seg->previous; ++ if (current_seg != NULL) ++ { ++ current_seg_index1 = current_seg->size; ++ if (current_seg_index1 == 0) ++ /* No more data in previous segments. */ ++ current_seg = NULL; ++ } ++ } ++ ++ if (current_seg != NULL ++ && (current_seg->objects[current_seg_index1 - 1].map == NULL)) ++ { ++ /* This mapping has been dlclose'd. Do not copy it. */ ++ --current_seg_index1; ++ continue; ++ } ++ ++ if (loaded_index1 == 0 && current_seg == NULL) ++ /* No more data in either source. */ ++ break; ++ ++ /* Make room for another mapping. */ ++ assert (remaining_to_add > 0); ++ if (target_seg_index1 == 0) ++ { ++ /* Switch segments and set the size of the segment. */ ++ target_seg = target_seg->previous; ++ target_seg_index1 = _dlfo_update_init_seg (target_seg, ++ remaining_to_add); ++ } ++ ++ /* Determine where to store the data. */ ++ struct dl_find_object_internal *dlfo ++ = &target_seg->objects[target_seg_index1 - 1]; ++ ++ if (loaded_index1 == 0 ++ || (current_seg != NULL ++ && (loaded[loaded_index1 - 1]->l_map_start ++ < current_seg->objects[current_seg_index1 - 1].map_start))) ++ { ++ /* Prefer mapping in current_seg. */ ++ assert (current_seg_index1 > 0); ++ *dlfo = current_seg->objects[current_seg_index1 - 1]; ++ --current_seg_index1; ++ } ++ else ++ { ++ /* Prefer newly loaded link map. */ ++ assert (loaded_index1 > 0); ++ _dl_find_object_from_map (loaded[loaded_index1 - 1], dlfo); ++ loaded[loaded_index1 - 1]->l_find_object_processed = 1; ++ --loaded_index1; ++ } ++ ++ /* Consume space in target segment. */ ++ --target_seg_index1; ++ ++ --remaining_to_add; ++ } ++ ++ /* Everything has been added. */ ++ assert (remaining_to_add == 0); ++ ++ /* The segment must have been filled up to the beginning. */ ++ assert (target_seg_index1 == 0); ++ ++ /* Prevent searching further into unused segments. */ ++ if (target_seg->previous != NULL) ++ target_seg->previous->size = 0; ++ ++ _dlfo_mappings_end_update (); ++ return true; ++} ++ ++bool ++_dl_find_object_update (struct link_map *new_map) ++{ ++ /* Copy the newly-loaded link maps into an array for sorting. */ ++ size_t count = 0; ++ for (struct link_map *l = new_map; l != NULL; l = l->l_next) ++ /* Skip proxy maps and already-processed maps. */ ++ count += l == l->l_real && !l->l_find_object_processed; ++ struct link_map **map_array = malloc (count * sizeof (*map_array)); ++ if (map_array == NULL) ++ return false; ++ { ++ size_t i = 0; ++ for (struct link_map *l = new_map; l != NULL; l = l->l_next) ++ if (l == l->l_real && !l->l_find_object_processed) ++ map_array[i++] = l; ++ } ++ if (count == 0) ++ return true; ++ ++ _dl_find_object_link_map_sort (map_array, count); ++ bool ok = _dl_find_object_update_1 (map_array, count); ++ free (map_array); ++ return ok; ++} ++ ++void ++_dl_find_object_dlclose (struct link_map *map) ++{ ++ uint64_t start_version = _dlfo_read_version_locked (); ++ uintptr_t map_start = map->l_map_start; ++ ++ ++ /* Directly patch the size information in the mapping to mark it as ++ unused. See the parallel lookup logic in _dl_find_object. Do ++ not check for previous dlclose at the same mapping address ++ because that cannot happen (there would have to be an ++ intermediate dlopen, which drops size-zero mappings). */ ++ for (struct dlfo_mappings_segment *seg ++ = _dlfo_mappings_active_segment (start_version); ++ seg != NULL && seg->size > 0; seg = seg->previous) ++ if (map_start >= seg->objects[0].map_start) ++ { ++ struct dl_find_object_internal *obj ++ = _dlfo_lookup (map_start, seg->objects, seg->size); ++ if (obj == NULL) ++ /* Ignore missing link maps because of potential shutdown ++ issues around __libc_freeres. */ ++ return; ++ ++ /* The update happens in-place, but given that we do not use ++ atomic accesses on the read side, update the version around ++ the update to trigger re-validation in concurrent ++ readers. */ ++ _dlfo_mappings_begin_update (); ++ ++ /* Mark as closed. */ ++ obj->map_end = obj->map_start; ++ obj->map = NULL; ++ ++ _dlfo_mappings_end_update_no_switch (); ++ return; ++ } ++} ++ ++void ++_dl_find_object_freeres (void) ++{ ++ for (int idx = 0; idx < 2; ++idx) ++ { ++ for (struct dlfo_mappings_segment *seg = _dlfo_loaded_mappings[idx]; ++ seg != NULL; ) ++ { ++ struct dlfo_mappings_segment *previous = seg->previous; ++ free (seg->to_free); ++ seg = previous; ++ } ++ /* Stop searching in shared objects. */ ++ _dlfo_loaded_mappings[idx] = 0; ++ } ++} +diff --git a/elf/dl-find_object.h b/elf/dl-find_object.h +new file mode 100644 +index 0000000000000000..f899905e09427a0d +--- /dev/null ++++ b/elf/dl-find_object.h +@@ -0,0 +1,115 @@ ++/* Locating objects in the process image. ld.so implementation. ++ Copyright (C) 2021 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 ++ . */ ++ ++#ifndef _DL_FIND_EH_FRAME_H ++#define _DL_FIND_EH_FRAME_H ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* Internal version of struct dl_find_object. Does not include the ++ (yet unused) flags member. We need to make a copy of data also in ++ struct link_map to support non-contiguous mappings, and to support ++ software transactional memory (the link map is not covered by ++ transactions). */ ++struct dl_find_object_internal ++{ ++ uintptr_t map_start; ++ uintptr_t map_end; /* Set to map_start by dlclose. */ ++ struct link_map *map; /* Set to NULL by dlclose. */ ++ void *eh_frame; ++#if DLFO_STRUCT_HAS_EH_DBASE ++ void *eh_dbase; ++#endif ++#if DLFO_STRUCT_HAS_EH_COUNT ++ int eh_count; ++#endif ++}; ++ ++static inline void ++_dl_find_object_to_external (struct dl_find_object_internal *internal, ++ struct dl_find_object *external) ++{ ++ external->dlfo_flags = 0; ++ external->dlfo_map_start = (void *) internal->map_start; ++ external->dlfo_map_end = (void *) internal->map_end; ++ external->dlfo_link_map = internal->map; ++ external->dlfo_eh_frame = internal->eh_frame; ++# if DLFO_STRUCT_HAS_EH_DBASE ++ external->dlfo_eh_dbase = internal->eh_dbase; ++# endif ++# if DLFO_STRUCT_HAS_EH_COUNT ++ external->dlfo_eh_count = internal->eh_count; ++# endif ++} ++ ++/* Extract the object location data from a link map and writes it to ++ *RESULT. */ ++static void __attribute__ ((unused)) ++_dl_find_object_from_map (struct link_map *l, ++ struct dl_find_object_internal *result) ++{ ++ result->map_start = (uintptr_t) l->l_map_start; ++ result->map_end = (uintptr_t) l->l_map_end; ++ result->map = l; ++ ++#if DLFO_STRUCT_HAS_EH_DBASE ++ result->eh_dbase = (void *) l->l_info[DT_PLTGOT]; ++#endif ++ ++ for (const ElfW(Phdr) *ph = l->l_phdr, *ph_end = l->l_phdr + l->l_phnum; ++ ph < ph_end; ++ph) ++ if (ph->p_type == DLFO_EH_SEGMENT_TYPE) ++ { ++ result->eh_frame = (void *) (ph->p_vaddr + l->l_addr); ++#if DLFO_STRUCT_HAS_EH_COUNT ++ result->eh_count = ph->p_memsz / 8; ++#endif ++ return; ++ } ++ ++ /* Object has no exception handling segment. */ ++ result->eh_frame = NULL; ++#if DLFO_STRUCT_HAS_EH_COUNT ++ result->eh_count = 0; ++#endif ++} ++ ++/* Called by the dynamic linker to set up the data structures for the ++ initially loaded objects. This creates a few persistent ++ allocations, so it should be called with the minimal malloc. */ ++void _dl_find_object_init (void) attribute_hidden; ++ ++/* Called by dlopen/dlmopen to add new objects to the DWARF EH frame ++ data structures. NEW_MAP is the dlopen'ed link map. Link maps on ++ the l_next list are added if l_object_processed is 0. Needs to ++ be protected by loader write lock. Returns true on success, false ++ on malloc failure. */ ++bool _dl_find_object_update (struct link_map *new_map) attribute_hidden; ++ ++/* Called by dlclose to remove the link map from the DWARF EH frame ++ data structures. Needs to be protected by loader write lock. */ ++void _dl_find_object_dlclose (struct link_map *l) attribute_hidden; ++ ++/* Called from __libc_freeres to deallocate malloc'ed memory. */ ++void _dl_find_object_freeres (void) attribute_hidden; ++ ++#endif /* _DL_FIND_OBJECT_H */ +diff --git a/elf/dl-libc_freeres.c b/elf/dl-libc_freeres.c +index 68f305a6f98aac0c..2a377fa9dfc4f1c0 100644 +--- a/elf/dl-libc_freeres.c ++++ b/elf/dl-libc_freeres.c +@@ -17,8 +17,10 @@ + . */ + + #include ++#include + + void + __rtld_libc_freeres (void) + { ++ _dl_find_object_freeres (); + } +diff --git a/elf/dl-open.c b/elf/dl-open.c +index df6aa55a8842ee62..1e659e02882b42c1 100644 +--- a/elf/dl-open.c ++++ b/elf/dl-open.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -731,6 +732,10 @@ dl_open_worker_begin (void *a) + objects. */ + update_scopes (new); + ++ if (!_dl_find_object_update (new)) ++ _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? +diff --git a/elf/dl-support.c b/elf/dl-support.c +index 00abc2d8056c78b0..b80bdfc257f5fee0 100644 +--- a/elf/dl-support.c ++++ b/elf/dl-support.c +@@ -44,6 +44,7 @@ + #include + #include + #include ++#include + + extern char *__progname; + char **_dl_argv = &__progname; /* This is checked for some error messages. */ +@@ -352,6 +353,8 @@ _dl_non_dynamic_init (void) + break; + } + ++ call_function_static_weak (_dl_find_object_init); ++ + /* Setup relro on the binary itself. */ + _dl_protect_relro (&_dl_main_map); + } +diff --git a/elf/libc-dl_find_object.c b/elf/libc-dl_find_object.c +new file mode 100644 +index 0000000000000000..38ea3bc94999df6e +--- /dev/null ++++ b/elf/libc-dl_find_object.c +@@ -0,0 +1,26 @@ ++/* Locating objects in the process image. libc forwarder. ++ Copyright (C) 2021 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 ++ . */ ++ ++#include ++#include ++ ++int ++_dl_find_object (void *address, struct dl_find_object *result) ++{ ++ return GLRO (dl_find_object) (address, result); ++} +diff --git a/elf/rtld.c b/elf/rtld.c +index fd70c4c3528cda2d..b5be7674dc1dbf40 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -52,6 +52,7 @@ + #include + #include + #include ++#include + + #include + +@@ -583,6 +584,10 @@ _dl_start (void *arg) + + __rtld_malloc_init_stubs (); + ++ /* Do not use an initializer for these members because it would ++ intefere with __rtld_static_init. */ ++ GLRO (dl_find_object) = &_dl_find_object; ++ + { + #ifdef DONT_USE_BOOTSTRAP_MAP + ElfW(Addr) entry = _dl_start_final (arg); +@@ -2388,6 +2393,9 @@ dl_main (const ElfW(Phdr) *phdr, + rtld_timer_stop (&relocate_time, start); + } + ++ /* Set up the object lookup structures. */ ++ _dl_find_object_init (); ++ + /* The library defining malloc has already been relocated due to + prelinking. Resolve the malloc symbols for the dynamic + loader. */ +@@ -2497,6 +2505,9 @@ dl_main (const ElfW(Phdr) *phdr, + + if (! prelinked) + { ++ /* Set up the object lookup structures. */ ++ _dl_find_object_init (); ++ + /* If libc.so was loaded, relocate ld.so against it. Complete ld.so + initialization with mutex symbols from libc.so and malloc symbols + from the global scope. */ +diff --git a/elf/rtld_static_init.c b/elf/rtld_static_init.c +index 3f8abb6800b401d7..6027000d3a56e46e 100644 +--- a/elf/rtld_static_init.c ++++ b/elf/rtld_static_init.c +@@ -78,6 +78,7 @@ __rtld_static_init (struct link_map *map) + extern __typeof (dl->_dl_tls_static_size) _dl_tls_static_size + attribute_hidden; + dl->_dl_tls_static_size = _dl_tls_static_size; ++ dl->_dl_find_object = _dl_find_object; + + __rtld_static_init_arch (map, dl); + } +diff --git a/elf/tst-dl_find_object-mod1.c b/elf/tst-dl_find_object-mod1.c +new file mode 100644 +index 0000000000000000..d33ef56efddc1c2b +--- /dev/null ++++ b/elf/tst-dl_find_object-mod1.c +@@ -0,0 +1,10 @@ ++char mod1_data; ++ ++void ++mod1_function (void (*f) (void)) ++{ ++ /* Make sure this is not a tail call and unwind information is ++ therefore needed. */ ++ f (); ++ f (); ++} +diff --git a/elf/tst-dl_find_object-mod2.c b/elf/tst-dl_find_object-mod2.c +new file mode 100644 +index 0000000000000000..3dad31c97c906baf +--- /dev/null ++++ b/elf/tst-dl_find_object-mod2.c +@@ -0,0 +1,15 @@ ++#include ++ ++char mod2_data; ++ ++void ++mod2_function (void (*f) (void)) ++{ ++ /* Make sure this is not a tail call and unwind information is ++ therefore needed. */ ++ f (); ++ f (); ++} ++ ++/* Used to verify that _dl_find_object after static dlopen works. */ ++void *find_object = _dl_find_object; +diff --git a/elf/tst-dl_find_object-mod3.c b/elf/tst-dl_find_object-mod3.c +new file mode 100644 +index 0000000000000000..c1fc20ff9ce34503 +--- /dev/null ++++ b/elf/tst-dl_find_object-mod3.c +@@ -0,0 +1,10 @@ ++char mod3_data[4096]; ++ ++void ++mod3_function (void (*f) (void)) ++{ ++ /* Make sure this is not a tail call and unwind information is ++ therefore needed. */ ++ f (); ++ f (); ++} +diff --git a/elf/tst-dl_find_object-mod4.c b/elf/tst-dl_find_object-mod4.c +new file mode 100644 +index 0000000000000000..27934e60113b61b9 +--- /dev/null ++++ b/elf/tst-dl_find_object-mod4.c +@@ -0,0 +1,10 @@ ++char mod4_data; ++ ++void ++mod4_function (void (*f) (void)) ++{ ++ /* Make sure this is not a tail call and unwind information is ++ therefore needed. */ ++ f (); ++ f (); ++} +diff --git a/elf/tst-dl_find_object-mod5.c b/elf/tst-dl_find_object-mod5.c +new file mode 100644 +index 0000000000000000..3bdbda8ccd662376 +--- /dev/null ++++ b/elf/tst-dl_find_object-mod5.c +@@ -0,0 +1,11 @@ ++/* Slightly larger to get different layouts. */ ++char mod5_data[4096]; ++ ++void ++mod5_function (void (*f) (void)) ++{ ++ /* Make sure this is not a tail call and unwind information is ++ therefore needed. */ ++ f (); ++ f (); ++} +diff --git a/elf/tst-dl_find_object-mod6.c b/elf/tst-dl_find_object-mod6.c +new file mode 100644 +index 0000000000000000..f78acffb9eb5dfac +--- /dev/null ++++ b/elf/tst-dl_find_object-mod6.c +@@ -0,0 +1,11 @@ ++/* Large to get different layouts. */ ++char mod6_data[4096]; ++ ++void ++mod6_function (void (*f) (void)) ++{ ++ /* Make sure this is not a tail call and unwind information is ++ therefore needed. */ ++ f (); ++ f (); ++} +diff --git a/elf/tst-dl_find_object-mod7.c b/elf/tst-dl_find_object-mod7.c +new file mode 100644 +index 0000000000000000..71353880da7270df +--- /dev/null ++++ b/elf/tst-dl_find_object-mod7.c +@@ -0,0 +1,10 @@ ++char mod7_data; ++ ++void ++mod7_function (void (*f) (void)) ++{ ++ /* Make sure this is not a tail call and unwind information is ++ therefore needed. */ ++ f (); ++ f (); ++} +diff --git a/elf/tst-dl_find_object-mod8.c b/elf/tst-dl_find_object-mod8.c +new file mode 100644 +index 0000000000000000..41f8f1ea092fbaca +--- /dev/null ++++ b/elf/tst-dl_find_object-mod8.c +@@ -0,0 +1,10 @@ ++char mod8_data; ++ ++void ++mod8_function (void (*f) (void)) ++{ ++ /* Make sure this is not a tail call and unwind information is ++ therefore needed. */ ++ f (); ++ f (); ++} +diff --git a/elf/tst-dl_find_object-mod9.c b/elf/tst-dl_find_object-mod9.c +new file mode 100644 +index 0000000000000000..dc2e7a20cb8a42d8 +--- /dev/null ++++ b/elf/tst-dl_find_object-mod9.c +@@ -0,0 +1,10 @@ ++char mod9_data; ++ ++void ++mod9_function (void (*f) (void)) ++{ ++ /* Make sure this is not a tail call and unwind information is ++ therefore needed. */ ++ f (); ++ f (); ++} +diff --git a/elf/tst-dl_find_object-static.c b/elf/tst-dl_find_object-static.c +new file mode 100644 +index 0000000000000000..a95ebeb84723fe42 +--- /dev/null ++++ b/elf/tst-dl_find_object-static.c +@@ -0,0 +1,22 @@ ++/* Basic tests for _dl_find_object. Static version. ++ Copyright (C) 2021 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 ++ . */ ++ ++/* Disable tests around _r_debug and libc symbols that do not work in ++ the static case. */ ++#define FOR_STATIC ++#include "tst-dl_find_object.c" +diff --git a/elf/tst-dl_find_object-threads.c b/elf/tst-dl_find_object-threads.c +new file mode 100644 +index 0000000000000000..472deeec57da0560 +--- /dev/null ++++ b/elf/tst-dl_find_object-threads.c +@@ -0,0 +1,275 @@ ++/* _dl_find_object test with parallelism. ++ Copyright (C) 2021 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Computes the expected _dl_find_object result directly from the ++ map. */ ++static void ++from_map (struct link_map *l, struct dl_find_object *expected) ++{ ++ struct dl_find_object_internal internal; ++ _dl_find_object_from_map (l, &internal); ++ _dl_find_object_to_external (&internal, expected); ++} ++ ++/* Returns the soname for the test object NUMBER. */ ++static char * ++soname (int number) ++{ ++ return xasprintf ("tst-dl_find_object-mod%d.so", number); ++} ++ ++/* Returns the data symbol name for the test object NUMBER. */ ++static char * ++symbol (int number) ++{ ++ return xasprintf ("mod%d_data", number); ++} ++ ++struct verify_data ++{ ++ char *soname; ++ void *address; /* Address in the shared object. */ ++ struct dl_find_object dlfo; ++ pthread_t thr; ++}; ++ ++/* Compare _dl_find_object result at ADDRESS with *EXPECTED. */ ++static void ++check (void *address, struct dl_find_object *expected, int line) ++{ ++ struct dl_find_object actual; ++ int ret = _dl_find_object (address, &actual); ++ if (expected == NULL) ++ { ++ if (ret != -1) ++ { ++ support_record_failure (); ++ printf ("%s:%d: unexpected success for %p\n", ++ __FILE__, line, address); ++ } ++ return; ++ } ++ if (ret != 0) ++ { ++ support_record_failure (); ++ printf ("%s:%d: unexpected failure for %p\n", ++ __FILE__, line, address); ++ return; ++ } ++ ++ if (actual.dlfo_flags != expected->dlfo_flags) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: flags is %llu, expected %llu\n", ++ __FILE__, line, address, ++ actual.dlfo_flags, expected->dlfo_flags); ++ } ++ if (actual.dlfo_flags != expected->dlfo_flags) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: map start is %p, expected %p\n", ++ __FILE__, line, ++ address, actual.dlfo_map_start, expected->dlfo_map_start); ++ } ++ if (actual.dlfo_map_end != expected->dlfo_map_end) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: map end is %p, expected %p\n", ++ __FILE__, line, ++ address, actual.dlfo_map_end, expected->dlfo_map_end); ++ } ++ if (actual.dlfo_link_map != expected->dlfo_link_map) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: link map is %p, expected %p\n", ++ __FILE__, line, ++ address, actual.dlfo_link_map, expected->dlfo_link_map); ++ } ++ if (actual.dlfo_eh_frame != expected->dlfo_eh_frame) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: EH frame is %p, expected %p\n", ++ __FILE__, line, ++ address, actual.dlfo_eh_frame, expected->dlfo_eh_frame); ++ } ++#if DLFO_STRUCT_HAS_EH_DBASE ++ if (actual.dlfo_eh_dbase != expected->dlfo_eh_dbase) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: data base is %p, expected %p\n", ++ __FILE__, line, ++ address, actual.dlfo_eh_dbase, expected->dlfo_eh_dbase); ++ } ++#endif ++#if DLFO_STRUCT_HAS_EH_COUNT ++ if (actual.dlfo_eh_count != expected->dlfo_eh_count) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: count is %d, expected %d\n", ++ __FILE__, line, ++ address, actual.dlfo_eh_count, expected->dlfo_eh_count); ++ } ++#endif ++} ++ ++/* Request process termination after 3 seconds. */ ++static bool exit_requested; ++static void * ++exit_thread (void *ignored) ++{ ++ usleep (3 * 100 * 1000); ++ __atomic_store_n (&exit_requested, true, __ATOMIC_RELAXED); ++ return NULL; ++} ++ ++static void * ++verify_thread (void *closure) ++{ ++ struct verify_data *data = closure; ++ ++ while (!__atomic_load_n (&exit_requested, __ATOMIC_RELAXED)) ++ { ++ check (data->address, &data->dlfo, __LINE__); ++ check (data->dlfo.dlfo_map_start, &data->dlfo, __LINE__); ++ check (data->dlfo.dlfo_map_end - 1, &data->dlfo, __LINE__); ++ } ++ ++ return NULL; ++} ++ ++/* Sets up the verification data, dlopen'ing shared object NUMBER, and ++ launches a verification thread. */ ++static void ++start_verify (int number, struct verify_data *data) ++{ ++ data->soname = soname (number); ++ struct link_map *l = xdlopen (data->soname, RTLD_NOW); ++ from_map (l, &data->dlfo); ++ TEST_VERIFY_EXIT (data->dlfo.dlfo_link_map == l); ++ char *sym = symbol (number); ++ data->address = xdlsym (data->dlfo.dlfo_link_map, sym); ++ free (sym); ++ data->thr = xpthread_create (NULL, verify_thread, data); ++} ++ ++ ++static int ++do_test (void) ++{ ++ struct verify_data data_mod2; ++ struct verify_data data_mod4; ++ struct verify_data data_mod7; ++ ++ /* Load the modules with gaps. */ ++ { ++ void *mod1 = xdlopen ("tst-dl_find_object-mod1.so", RTLD_NOW); ++ start_verify (2, &data_mod2); ++ void *mod3 = xdlopen ("tst-dl_find_object-mod3.so", RTLD_NOW); ++ start_verify (4, &data_mod4); ++ void *mod5 = xdlopen ("tst-dl_find_object-mod5.so", RTLD_NOW); ++ void *mod6 = xdlopen ("tst-dl_find_object-mod6.so", RTLD_NOW); ++ start_verify (7, &data_mod7); ++ xdlclose (mod6); ++ xdlclose (mod5); ++ xdlclose (mod3); ++ xdlclose (mod1); ++ } ++ ++ /* Objects that continuously opened and closed. */ ++ struct temp_object ++ { ++ char *soname; ++ char *symbol; ++ struct link_map *link_map; ++ void *address; ++ } temp_objects[] = ++ { ++ { soname (1), symbol (1), }, ++ { soname (3), symbol (3), }, ++ { soname (5), symbol (5), }, ++ { soname (6), symbol (6), }, ++ { soname (8), symbol (8), }, ++ { soname (9), symbol (9), }, ++ }; ++ ++ pthread_t exit_thr = xpthread_create (NULL, exit_thread, NULL); ++ ++ struct drand48_data state; ++ srand48_r (1, &state); ++ while (!__atomic_load_n (&exit_requested, __ATOMIC_RELAXED)) ++ { ++ long int idx; ++ lrand48_r (&state, &idx); ++ idx %= array_length (temp_objects); ++ if (temp_objects[idx].link_map == NULL) ++ { ++ temp_objects[idx].link_map = xdlopen (temp_objects[idx].soname, ++ RTLD_NOW); ++ temp_objects[idx].address = xdlsym (temp_objects[idx].link_map, ++ temp_objects[idx].symbol); ++ } ++ else ++ { ++ xdlclose (temp_objects[idx].link_map); ++ temp_objects[idx].link_map = NULL; ++ struct dl_find_object dlfo; ++ int ret = _dl_find_object (temp_objects[idx].address, &dlfo); ++ if (ret != -1) ++ { ++ TEST_VERIFY_EXIT (ret == 0); ++ support_record_failure (); ++ printf ("%s: error: %s EH found after dlclose, link map %p\n", ++ __FILE__, temp_objects[idx].soname, dlfo.dlfo_link_map); ++ } ++ } ++ } ++ ++ xpthread_join (data_mod2.thr); ++ xpthread_join (data_mod4.thr); ++ xpthread_join (data_mod7.thr); ++ xpthread_join (exit_thr); ++ ++ for (size_t i = 0; i < array_length (temp_objects); ++i) ++ { ++ free (temp_objects[i].soname); ++ free (temp_objects[i].symbol); ++ if (temp_objects[i].link_map != NULL) ++ xdlclose (temp_objects[i].link_map); ++ } ++ ++ free (data_mod2.soname); ++ free (data_mod4.soname); ++ xdlclose (data_mod4.dlfo.dlfo_link_map); ++ free (data_mod7.soname); ++ xdlclose (data_mod7.dlfo.dlfo_link_map); ++ ++ return 0; ++} ++ ++#include +diff --git a/elf/tst-dl_find_object.c b/elf/tst-dl_find_object.c +new file mode 100644 +index 0000000000000000..9abffa35d479c8fc +--- /dev/null ++++ b/elf/tst-dl_find_object.c +@@ -0,0 +1,240 @@ ++/* Basic tests for _dl_find_object. ++ Copyright (C) 2021 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Use data objects for testing, so that it is not necessary to decode ++ function descriptors on architectures that have them. */ ++static char main_program_data; ++ ++/* Computes the expected _dl_find_object result directly from the ++ map. */ ++static void ++from_map (struct link_map *l, struct dl_find_object *expected) ++{ ++ struct dl_find_object_internal internal; ++ _dl_find_object_from_map (l, &internal); ++ _dl_find_object_to_external (&internal, expected); ++} ++ ++/* Compare _dl_find_object result at ADDRESS with *EXPECTED. */ ++static void ++check (void *address, ++ struct dl_find_object *expected, int line) ++{ ++ struct dl_find_object actual; ++ int ret = _dl_find_object (address, &actual); ++ if (expected == NULL) ++ { ++ if (ret != -1) ++ { ++ support_record_failure (); ++ printf ("%s:%d: unexpected success for %p\n", ++ __FILE__, line, address); ++ } ++ return; ++ } ++ if (ret != 0) ++ { ++ support_record_failure (); ++ printf ("%s:%d: unexpected failure for %p\n", ++ __FILE__, line, address); ++ return; ++ } ++ ++ if (actual.dlfo_flags != expected->dlfo_flags) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: flags is %llu, expected %llu\n", ++ __FILE__, line, address, ++ actual.dlfo_flags, expected->dlfo_flags); ++ } ++ if (actual.dlfo_flags != expected->dlfo_flags) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: map start is %p, expected %p\n", ++ __FILE__, line, ++ address, actual.dlfo_map_start, expected->dlfo_map_start); ++ } ++ if (actual.dlfo_map_end != expected->dlfo_map_end) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: map end is %p, expected %p\n", ++ __FILE__, line, ++ address, actual.dlfo_map_end, expected->dlfo_map_end); ++ } ++ if (actual.dlfo_link_map != expected->dlfo_link_map) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: link map is %p, expected %p\n", ++ __FILE__, line, ++ address, actual.dlfo_link_map, expected->dlfo_link_map); ++ } ++ if (actual.dlfo_eh_frame != expected->dlfo_eh_frame) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: EH frame is %p, expected %p\n", ++ __FILE__, line, ++ address, actual.dlfo_eh_frame, expected->dlfo_eh_frame); ++ } ++#if DLFO_STRUCT_HAS_EH_DBASE ++ if (actual.dlfo_eh_dbase != expected->dlfo_eh_dbase) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: data base is %p, expected %p\n", ++ __FILE__, line, ++ address, actual.dlfo_eh_dbase, expected->dlfo_eh_dbase); ++ } ++#endif ++#if DLFO_STRUCT_HAS_EH_COUNT ++ if (actual.dlfo_eh_count != expected->dlfo_eh_count) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: count is %d, expected %d\n", ++ __FILE__, line, ++ address, actual.dlfo_eh_count, expected->dlfo_eh_count); ++ } ++#endif ++} ++ ++/* Check that unwind data for the main executable and the dynamic ++ linker can be found. */ ++static void ++check_initial (void) ++{ ++#ifndef FOR_STATIC ++ /* Avoid direct reference, which could lead to copy relocations. */ ++ struct r_debug *debug = xdlsym (NULL, "_r_debug"); ++ TEST_VERIFY_EXIT (debug != NULL); ++ char **tzname = xdlsym (NULL, "tzname"); ++ ++ /* The main executable has an unnamed link map. */ ++ struct link_map *main_map = (struct link_map *) debug->r_map; ++ TEST_COMPARE_STRING (main_map->l_name, ""); ++ ++ /* The link map of the dynamic linker. */ ++ struct link_map *rtld_map = xdlopen (LD_SO, RTLD_LAZY | RTLD_NOLOAD); ++ TEST_VERIFY_EXIT (rtld_map != NULL); ++ ++ /* The link map of libc.so. */ ++ struct link_map *libc_map = xdlopen (LIBC_SO, RTLD_LAZY | RTLD_NOLOAD); ++ TEST_VERIFY_EXIT (libc_map != NULL); ++ ++ struct dl_find_object expected; ++ ++ /* Data in the main program. */ ++ from_map (main_map, &expected); ++ check (&main_program_data, &expected, __LINE__); ++ /* Corner cases for the mapping. */ ++ check ((void *) main_map->l_map_start, &expected, __LINE__); ++ check ((void *) (main_map->l_map_end - 1), &expected, __LINE__); ++ ++ /* Data in the dynamic loader. */ ++ from_map (rtld_map, &expected); ++ check (debug, &expected, __LINE__); ++ check ((void *) rtld_map->l_map_start, &expected, __LINE__); ++ check ((void *) (rtld_map->l_map_end - 1), &expected, __LINE__); ++ ++ /* Data in libc. */ ++ from_map (libc_map, &expected); ++ check (tzname, &expected, __LINE__); ++ check ((void *) libc_map->l_map_start, &expected, __LINE__); ++ check ((void *) (libc_map->l_map_end - 1), &expected, __LINE__); ++#endif ++} ++ ++static int ++do_test (void) ++{ ++ { ++ struct dl_find_object dlfo = { }; ++ int ret = _dl_find_object (&main_program_data, &dlfo); ++ printf ("info: main program unwind data: %p (%d)\n", ++ dlfo.dlfo_eh_frame, ret); ++ TEST_COMPARE (ret, 0); ++ TEST_VERIFY (dlfo.dlfo_eh_frame != NULL); ++ } ++ ++ check_initial (); ++ ++ /* dlopen-based test. First an object that can be dlclosed. */ ++ struct link_map *mod1 = xdlopen ("tst-dl_find_object-mod1.so", RTLD_NOW); ++ void *mod1_data = xdlsym (mod1, "mod1_data"); ++ void *map_start = (void *) mod1->l_map_start; ++ void *map_end = (void *) (mod1->l_map_end - 1); ++ check_initial (); ++ ++ struct dl_find_object expected; ++ from_map (mod1, &expected); ++ check (mod1_data, &expected, __LINE__); ++ check (map_start, &expected, __LINE__); ++ check (map_end, &expected, __LINE__); ++ ++ /* Unloading must make the data unavailable. */ ++ xdlclose (mod1); ++ check_initial (); ++ check (mod1_data, NULL, __LINE__); ++ check (map_start, NULL, __LINE__); ++ check (map_end, NULL, __LINE__); ++ ++ /* Now try a NODELETE load. */ ++ struct link_map *mod2 = xdlopen ("tst-dl_find_object-mod2.so", RTLD_NOW); ++ void *mod2_data = xdlsym (mod1, "mod2_data"); ++ map_start = (void *) mod2->l_map_start; ++ map_end = (void *) (mod2->l_map_end - 1); ++ check_initial (); ++ from_map (mod2, &expected); ++ check (mod2_data, &expected, __LINE__); ++ check (map_start, &expected, __LINE__); ++ check (map_end, &expected, __LINE__); ++ dlclose (mod2); /* Does nothing due to NODELETE. */ ++ check_initial (); ++ check (mod2_data, &expected, __LINE__); ++ check (map_start, &expected, __LINE__); ++ check (map_end, &expected, __LINE__); ++ ++ /* Now load again the first module. */ ++ mod1 = xdlopen ("tst-dl_find_object-mod1.so", RTLD_NOW); ++ mod1_data = xdlsym (mod1, "mod1_data"); ++ map_start = (void *) mod1->l_map_start; ++ map_end = (void *) (mod1->l_map_end - 1); ++ check_initial (); ++ from_map (mod1, &expected); ++ check (mod1_data, &expected, __LINE__); ++ check (map_start, &expected, __LINE__); ++ check (map_end, &expected, __LINE__); ++ ++ /* Check that _dl_find_object works from a shared object (mostly for ++ static dlopen). */ ++ __typeof (_dl_find_object) *find_object ++ = *(void **) xdlsym (mod2, "find_object"); ++ struct dl_find_object actual; ++ TEST_COMPARE (find_object (&main_program_data, &actual), 0); ++ check (&main_program_data, &actual, __LINE__); /* Reversed check. */ ++ ++ return 0; ++} ++ ++#include +diff --git a/include/atomic_wide_counter.h b/include/atomic_wide_counter.h +index 31f009d5e66cb45f..cf526ed2ea715000 100644 +--- a/include/atomic_wide_counter.h ++++ b/include/atomic_wide_counter.h +@@ -31,6 +31,14 @@ __atomic_wide_counter_load_relaxed (__atomic_wide_counter *c) + } + + static inline uint64_t ++ ++__atomic_wide_counter_load_acquire (__atomic_wide_counter *c) ++{ ++ return atomic_load_acquire (&c->__value64); ++} ++ ++static inline uint64_t ++ + __atomic_wide_counter_fetch_add_relaxed (__atomic_wide_counter *c, + unsigned int val) + { +@@ -64,6 +72,14 @@ __atomic_wide_counter_fetch_xor_release (__atomic_wide_counter *c, + uint64_t __atomic_wide_counter_load_relaxed (__atomic_wide_counter *c) + attribute_hidden; + ++static inline uint64_t ++__atomic_wide_counter_load_acquire (__atomic_wide_counter *c) ++{ ++ uint64_t r = __atomic_wide_counter_load_relaxed (c); ++ atomic_thread_fence_acquire (); ++ return r; ++} ++ + uint64_t __atomic_wide_counter_fetch_add_relaxed (__atomic_wide_counter *c, + unsigned int op) + attribute_hidden; +diff --git a/include/bits/dl_find_object.h b/include/bits/dl_find_object.h +new file mode 100644 +index 0000000000000000..7a323d7d4fac65ed +--- /dev/null ++++ b/include/bits/dl_find_object.h +@@ -0,0 +1 @@ ++#include_next +diff --git a/include/dlfcn.h b/include/dlfcn.h +index e73294b0af587913..ae25f05303b18044 100644 +--- a/include/dlfcn.h ++++ b/include/dlfcn.h +@@ -4,6 +4,8 @@ + #include /* For ElfW. */ + #include + ++rtld_hidden_proto (_dl_find_object) ++ + /* Internally used flag. */ + #define __RTLD_DLOPEN 0x80000000 + #define __RTLD_SPROF 0x40000000 +diff --git a/include/link.h b/include/link.h +index bafac6c9628b183c..9e3a14b2996292fe 100644 +--- a/include/link.h ++++ b/include/link.h +@@ -212,6 +212,9 @@ struct link_map + the dummy malloc in ld.so. */ + unsigned int l_ld_readonly:1; /* Nonzero if dynamic section is readonly. */ + unsigned int l_tls_in_slotinfo:1; /* TLS slotinfo updated in dlopen. */ ++ unsigned int l_find_object_processed:1; /* Zero if _dl_find_object_update ++ needs to process this ++ lt_library map. */ + + /* NODELETE status of the map. Only valid for maps of type + lt_loaded. Lazy binding sets l_nodelete_active directly, +diff --git a/manual/dynlink.texi b/manual/dynlink.texi +index d59dca2b7a916889..f97f13444f1c5946 100644 +--- a/manual/dynlink.texi ++++ b/manual/dynlink.texi +@@ -533,6 +533,111 @@ array. + The @code{dlinfo} function is a GNU extension. + @end deftypefun + ++The remainder of this section documents the @code{_dl_find_object} ++function and supporting types and constants. ++ ++@deftp {Data Type} {struct dl_find_object} ++@standards{GNU, dlfcn.h} ++This structure contains information about a main program or loaded ++object. The @code{_dl_find_object} function uses it to return ++result data to the caller. ++ ++@table @code ++@item unsigned long long int dlfo_flags ++Currently unused and always 0. ++ ++@item void *dlfo_map_start ++The start address of the inspected mapping. This information comes from ++the program header, so it follows its convention, and the address is not ++necessarily page-aligned. ++ ++@item void *dlfo_map_end ++The end address of the mapping. ++ ++@item struct link_map *dlfo_link_map ++This member contains a pointer to the link map of the object. ++ ++@item void *dlfo_eh_frame ++This member contains a pointer to the exception handling data of the ++object. See @code{DLFO_EH_SEGMENT_TYPE} below. ++ ++@end table ++ ++This structure is a GNU extension. ++@end deftp ++ ++@deftypevr Macro int DLFO_STRUCT_HAS_EH_DBASE ++@standards{GNU, dlfcn.h} ++On most targets, this macro is defined as @code{0}. If it is defined to ++@code{1}, @code{struct dl_find_object} contains an additional member ++@code{dlfo_eh_dbase} of type @code{void *}. It is the base address for ++@code{DW_EH_PE_datarel} DWARF encodings to this location. ++ ++This macro is a GNU extension. ++@end deftypevr ++ ++@deftypevr Macro int DLFO_STRUCT_HAS_EH_COUNT ++@standards{GNU, dlfcn.h} ++On most targets, this macro is defined as @code{0}. If it is defined to ++@code{1}, @code{struct dl_find_object} contains an additional member ++@code{dlfo_eh_count} of type @code{int}. It is the number of exception ++handling entries in the EH frame segment identified by the ++@code{dlfo_eh_frame} member. ++ ++This macro is a GNU extension. ++@end deftypevr ++ ++@deftypevr Macro int DLFO_EH_SEGMENT_TYPE ++@standards{GNU, dlfcn.h} ++On targets using DWARF-based exception unwinding, this macro expands to ++@code{PT_GNU_EH_FRAME}. This indicates that @code{dlfo_eh_frame} in ++@code{struct dl_find_object} points to the @code{PT_GNU_EH_FRAME} ++segment of the object. On targets that use other unwinding formats, the ++macro expands to the program header type for the unwinding data. ++ ++This macro is a GNU extension. ++@end deftypevr ++ ++@deftypefun {int} _dl_find_object (void *@var{address}, struct dl_find_object *@var{result}) ++@standards{GNU, dlfcn.h} ++@safety{@mtsafe{}@assafe{}@acsafe{}} ++On success, this function returns 0 and writes about the object ++surrounding the address to @code{*@var{result}}. On failure, -1 is ++returned. ++ ++The @var{address} can be a code address or data address. On ++architectures using function descriptors, no attempt is made to decode ++the function descriptor. Depending on how these descriptors are ++implemented, @code{_dl_find_object} may return the object that defines ++the function descriptor (and not the object that contains the code ++implementing the function), or fail to find any object at all. ++ ++On success @var{address} is greater than or equal to ++@code{@var{result}->dlfo_map_start} and less than ++@code{@var{result}->dlfo_map_end}, that is, the supplied code address is ++located within the reported mapping. ++ ++This function returns a pointer to the unwinding information for the ++object that contains the program code @var{address} in ++@code{@var{result}->dlfo_eh_frame}. If the platform uses DWARF ++unwinding information, this is the in-memory address of the ++@code{PT_GNU_EH_FRAME} segment. See @code{DLFO_EH_SEGMENT_TYPE} above. ++In case @var{address} resides in an object that lacks unwinding information, ++the function still returns 0, but sets @code{@var{result}->dlfo_eh_frame} ++to a null pointer. ++ ++@code{_dl_find_object} itself is thread-safe. However, if the ++application invokes @code{dlclose} for the object that contains ++@var{address} concurrently with @code{_dl_find_object} or after the call ++returns, accessing the unwinding data for that object or the link map ++(through @code{@var{result}->dlfo_link_map}) is not safe. Therefore, the ++application needs to ensure by other means (e.g., by convention) that ++@var{address} remains a valid code address while the unwinding ++information is processed. ++ ++This function is a GNU extension. ++@end deftypefun ++ + @c FIXME these are undocumented: + @c dladdr + @c dladdr1 +diff --git a/sysdeps/arm/bits/dl_find_object.h b/sysdeps/arm/bits/dl_find_object.h +new file mode 100644 +index 0000000000000000..d0204f361fff4152 +--- /dev/null ++++ b/sysdeps/arm/bits/dl_find_object.h +@@ -0,0 +1,25 @@ ++/* arm definitions for finding objects. ++ Copyright (C) 2021 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 ++ . */ ++ ++#ifndef _DLFCN_H ++# error "Never use directly; include instead." ++#endif ++ ++#define DLFO_STRUCT_HAS_EH_DBASE 0 ++#define DLFO_STRUCT_HAS_EH_COUNT 1 ++#define DLFO_EH_SEGMENT_TYPE PT_ARM_EXIDX +diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h +index 9142bc8f493bce64..29c87649f14a1b5b 100644 +--- a/sysdeps/generic/ldsodefs.h ++++ b/sysdeps/generic/ldsodefs.h +@@ -717,6 +717,11 @@ struct rtld_global_ro + /* Called from __libc_shared to deallocate malloc'ed memory. */ + void (*_dl_libc_freeres) (void); + ++ /* Implementation of _dl_find_object. The public entry point is in ++ libc, and this is patched by __rtld_static_init to support static ++ dlopen. */ ++ int (*_dl_find_object) (void *, struct dl_find_object *); ++ + #ifdef HAVE_DL_DISCOVER_OSVERSION + int (*_dl_discover_osversion) (void); + #endif +diff --git a/sysdeps/mach/hurd/i386/libc.abilist b/sysdeps/mach/hurd/i386/libc.abilist +index e849d6fa35456b4b..ba6ef0924ca632cb 100644 +--- a/sysdeps/mach/hurd/i386/libc.abilist ++++ b/sysdeps/mach/hurd/i386/libc.abilist +@@ -2286,6 +2286,7 @@ GLIBC_2.34 shm_open F + GLIBC_2.34 shm_unlink F + GLIBC_2.34 timespec_getres F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/nios2/bits/dl_find_object.h b/sysdeps/nios2/bits/dl_find_object.h +new file mode 100644 +index 0000000000000000..1195cb9f8a1caed0 +--- /dev/null ++++ b/sysdeps/nios2/bits/dl_find_object.h +@@ -0,0 +1,25 @@ ++/* nios2 definitions for finding objects. ++ Copyright (C) 2021 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 ++ . */ ++ ++#ifndef _DLFCN_H ++# error "Never use directly; include instead." ++#endif ++ ++#define DLFO_STRUCT_HAS_EH_DBASE 1 ++#define DLFO_STRUCT_HAS_EH_COUNT 0 ++#define DLFO_EH_SEGMENT_TYPE PT_GNU_EH_FRAME +diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist +index f227ae6ceec97c73..fed942ed4b9ef469 100644 +--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist +@@ -2613,3 +2613,4 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F +diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist +index 0ccc3fc73ecc0e4d..28679327043ff0f2 100644 +--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist ++++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist +@@ -2710,6 +2710,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/arc/libc.abilist b/sysdeps/unix/sysv/linux/arc/libc.abilist +index fd80704787f4ef41..239db7bab0a18a87 100644 +--- a/sysdeps/unix/sysv/linux/arc/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arc/libc.abilist +@@ -2374,3 +2374,4 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F +diff --git a/sysdeps/unix/sysv/linux/arm/be/libc.abilist b/sysdeps/unix/sysv/linux/arm/be/libc.abilist +index 2ae6c58b8ad6fc01..bc79dcfe8ab4c723 100644 +--- a/sysdeps/unix/sysv/linux/arm/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arm/be/libc.abilist +@@ -492,6 +492,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 + GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 +diff --git a/sysdeps/unix/sysv/linux/arm/le/libc.abilist b/sysdeps/unix/sysv/linux/arm/le/libc.abilist +index fcfd1e8594d80aa1..614607fd6baa8c6f 100644 +--- a/sysdeps/unix/sysv/linux/arm/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/arm/le/libc.abilist +@@ -489,6 +489,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0xa0 + GLIBC_2.4 _IO_2_1_stdin_ D 0xa0 +diff --git a/sysdeps/unix/sysv/linux/csky/libc.abilist b/sysdeps/unix/sysv/linux/csky/libc.abilist +index ba034b85414a2b55..2b61543f0dd47034 100644 +--- a/sysdeps/unix/sysv/linux/csky/libc.abilist ++++ b/sysdeps/unix/sysv/linux/csky/libc.abilist +@@ -2648,3 +2648,4 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F +diff --git a/sysdeps/unix/sysv/linux/hppa/libc.abilist b/sysdeps/unix/sysv/linux/hppa/libc.abilist +index b7460bec8ace47c2..6b3cb1adb40c0a01 100644 +--- a/sysdeps/unix/sysv/linux/hppa/libc.abilist ++++ b/sysdeps/unix/sysv/linux/hppa/libc.abilist +@@ -2597,6 +2597,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist +index a4dc341dededdc3b..7f608c1b64454bff 100644 +--- a/sysdeps/unix/sysv/linux/i386/libc.abilist ++++ b/sysdeps/unix/sysv/linux/i386/libc.abilist +@@ -2781,6 +2781,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/ia64/libc.abilist b/sysdeps/unix/sysv/linux/ia64/libc.abilist +index 94b222dbc7ffbb81..865deec43f99a036 100644 +--- a/sysdeps/unix/sysv/linux/ia64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/ia64/libc.abilist +@@ -2548,6 +2548,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +index 12fd3b63103c3e6e..a172d746328ddeeb 100644 +--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist ++++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist +@@ -493,6 +493,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 _Exit F + GLIBC_2.4 _IO_2_1_stderr_ D 0x98 + GLIBC_2.4 _IO_2_1_stdin_ D 0x98 +diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +index 4d2296007ab1d922..174e9c7739f966ac 100644 +--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist ++++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist +@@ -2724,6 +2724,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +index a223278a3d4a33d8..d042be1369b7d264 100644 +--- a/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/microblaze/be/libc.abilist +@@ -2697,3 +2697,4 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F +diff --git a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +index 780a4f5b0bf5518c..332da62de23b88b8 100644 +--- a/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/microblaze/le/libc.abilist +@@ -2694,3 +2694,4 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F +diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +index cd65136062a6a876..2d6ec0d0e8f3c797 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist +@@ -2689,6 +2689,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +index b5b9902db56a4c79..6c5befa72bb5602b 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist +@@ -2687,6 +2687,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +index 57593d5f94a184f1..5fb24c97e1e6f05d 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist +@@ -2695,6 +2695,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +index e944d76bed0bfe06..f4f29fc15ee8f1a2 100644 +--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist +@@ -2599,6 +2599,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/nios2/libc.abilist b/sysdeps/unix/sysv/linux/nios2/libc.abilist +index 8af5a3a90dfe4089..2e7300cd0512ad78 100644 +--- a/sysdeps/unix/sysv/linux/nios2/libc.abilist ++++ b/sysdeps/unix/sysv/linux/nios2/libc.abilist +@@ -2736,3 +2736,4 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +index 3a0213b39f8f7abd..129a2f16a7b731d3 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist +@@ -2751,6 +2751,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +index f57df0234b8bdee3..7e232267791057a3 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist +@@ -2784,6 +2784,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +index 259a0cfc5126ca9e..6f97392b7030ee95 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/be/libc.abilist +@@ -2507,6 +2507,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +index 126541daf152e1ad..29058a041a9cdba1 100644 +--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/le/libc.abilist +@@ -2809,3 +2809,4 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F +diff --git a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +index 05df4d13d2c35ad1..d2924766d2f33039 100644 +--- a/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/riscv/rv32/libc.abilist +@@ -2376,3 +2376,4 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F +diff --git a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +index 8e349cbff8cc0507..b770e05da3ae0350 100644 +--- a/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/riscv/rv64/libc.abilist +@@ -2576,3 +2576,4 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F +diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +index e9de402766af0d8a..bed3433a2b75d3b4 100644 +--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist +@@ -2749,6 +2749,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +index 1a010c745d78a07e..4f1a143da504d98c 100644 +--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist +@@ -2544,6 +2544,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/sh/be/libc.abilist b/sysdeps/unix/sysv/linux/sh/be/libc.abilist +index 22ce530975944ff6..92c8dec8ec28c54a 100644 +--- a/sysdeps/unix/sysv/linux/sh/be/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sh/be/libc.abilist +@@ -2604,6 +2604,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/sh/le/libc.abilist b/sysdeps/unix/sysv/linux/sh/le/libc.abilist +index 960df07b83bd2cbf..263da58cb7dada2c 100644 +--- a/sysdeps/unix/sysv/linux/sh/le/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sh/le/libc.abilist +@@ -2601,6 +2601,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +index eedb376f3dfeb8fb..0171efe7db109153 100644 +--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist +@@ -2744,6 +2744,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 _IO_fprintf F + GLIBC_2.4 _IO_printf F + GLIBC_2.4 _IO_sprintf F +diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +index 86e0c92bef9255ab..7f8d45f362e63584 100644 +--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist +@@ -2571,6 +2571,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +index 5e59d90623c2bcba..c2f1a8ecc6d04dcf 100644 +--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist ++++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist +@@ -2522,6 +2522,7 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F + GLIBC_2.4 __confstr_chk F + GLIBC_2.4 __fgets_chk F + GLIBC_2.4 __fgets_unlocked_chk F +diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +index 94412dc134af283a..8b43acf1005f1790 100644 +--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist ++++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist +@@ -2628,3 +2628,4 @@ GLIBC_2.34 tss_delete F + GLIBC_2.34 tss_get F + GLIBC_2.34 tss_set F + GLIBC_2.35 __memcmpeq F ++GLIBC_2.35 _dl_find_object F +diff --git a/sysdeps/x86/bits/dl_find_object.h b/sysdeps/x86/bits/dl_find_object.h +new file mode 100644 +index 0000000000000000..d9852ecb02fca036 +--- /dev/null ++++ b/sysdeps/x86/bits/dl_find_object.h +@@ -0,0 +1,29 @@ ++/* x86 definitions for finding objects. ++ Copyright (C) 2021 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 ++ . */ ++ ++#ifndef _DLFCN_H ++# error "Never use directly; include instead." ++#endif ++ ++#ifdef __x86_64__ ++# define DLFO_STRUCT_HAS_EH_DBASE 0 ++#else ++# define DLFO_STRUCT_HAS_EH_DBASE 1 ++#endif ++#define DLFO_STRUCT_HAS_EH_COUNT 0 ++#define DLFO_EH_SEGMENT_TYPE PT_GNU_EH_FRAME diff --git a/glibc-RHEL-93320-5.patch b/glibc-RHEL-93320-5.patch new file mode 100644 index 0000000..9a0c5de --- /dev/null +++ b/glibc-RHEL-93320-5.patch @@ -0,0 +1,420 @@ +commit acbaad31e8ea10fce8b9c0aef58afb388bf7489d +Author: Florian Weimer +Date: Fri Jan 7 13:21:57 2022 +0100 + + elf: Fix fences in _dl_find_object_update (bug 28745) + + As explained in Hans Boehm, Can Seqlocks Get Along with Programming + Language Memory Models?, an acquire fence is needed in + _dlfo_read_success. The lack of a fence resulted in an observable + bug on powerpc64le compile-time load reordering. + + The fence in _dlfo_mappings_begin_update has been reordered, turning + the fence/store sequence into a release MO store equivalent. + + Relaxed MO loads are used on the reader side, and relaxed MO stores + on the writer side for the shared data, to avoid formal data races. + This is just to be conservative; it should not actually be necessary + given how the data is used. + + This commit also fixes the test run time. The intent was to run it + for 3 seconds, but 0.3 seconds was enough to uncover the bug very + occasionally (while 3 seconds did not reliably show the bug on every + test run). + + Reviewed-by: Szabolcs Nagy + +diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c +index 324f40742d59b4dc..b23bcaf0a15e82cd 100644 +--- a/elf/dl-find_object.c ++++ b/elf/dl-find_object.c +@@ -17,6 +17,7 @@ + . */ + + #include ++#include + #include + #include + #include +@@ -80,13 +81,18 @@ static struct dl_find_object_internal *_dlfo_nodelete_mappings + over all segments, even though the data is not stored in one + contiguous array. + +- During updates, the segments are overwritten in place, and a +- software transactional memory construct (involving the ++ During updates, the segments are overwritten in place. A software ++ transactional memory construct (involving the + _dlfo_loaded_mappings_version variable) is used to detect +- concurrent modification, and retry as necessary. The memory +- allocations are never deallocated, but slots used for objects that +- have been dlclose'd can be reused by dlopen. The memory can live +- in the regular C malloc heap. ++ concurrent modification, and retry as necessary. (This approach is ++ similar to seqlocks, except that two copies are used, and there is ++ only one writer, ever, due to the loader lock.) Technically, ++ relaxed MO loads and stores need to be used for the shared TM data, ++ to avoid data races. ++ ++ The memory allocations are never deallocated, but slots used for ++ objects that have been dlclose'd can be reused by dlopen. The ++ memory can live in the regular C malloc heap. + + The segments are populated from the start of the list, with the + mappings with the highest address. Only if this segment is full, +@@ -101,17 +107,18 @@ static struct dl_find_object_internal *_dlfo_nodelete_mappings + needed. */ + struct dlfo_mappings_segment + { +- /* The previous segment has lower base addresses. */ ++ /* The previous segment has lower base addresses. Constant after ++ initialization; read in the TM region. */ + struct dlfo_mappings_segment *previous; + + /* Used by __libc_freeres to deallocate malloc'ed memory. */ + void *to_free; + + /* Count of array elements in use and allocated. */ +- size_t size; ++ size_t size; /* Read in the TM region. */ + size_t allocated; + +- struct dl_find_object_internal objects[]; ++ struct dl_find_object_internal objects[]; /* Read in the TM region. */ + }; + + /* To achieve async-signal-safety, two copies of the data structure +@@ -240,7 +247,8 @@ static inline uint64_t + _dlfo_read_start_version (void) + { + /* Acquire MO load synchronizes with the fences at the beginning and +- end of the TM update region. */ ++ end of the TM update region in _dlfo_mappings_begin_update, ++ _dlfo_mappings_end_update, _dlfo_mappings_end_update_no_switch. */ + return __atomic_wide_counter_load_acquire (&_dlfo_loaded_mappings_version); + } + +@@ -258,34 +266,30 @@ _dlfo_read_version_locked (void) + static inline unsigned int + _dlfo_mappings_begin_update (void) + { +- unsigned int v +- = __atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version, +- 2); +- /* Subsequent stores to the TM data must not be reordered before the +- store above with the version update. */ ++ /* The store synchronizes with loads in _dlfo_read_start_version ++ (also called from _dlfo_read_success). */ + atomic_thread_fence_release (); +- return v & 1; ++ return __atomic_wide_counter_fetch_add_relaxed ++ (&_dlfo_loaded_mappings_version, 2); + } + + /* Installs the just-updated version as the active version. */ + static inline void + _dlfo_mappings_end_update (void) + { +- /* The previous writes to the TM data must not be reordered after +- the version update below. */ ++ /* The store synchronizes with loads in _dlfo_read_start_version ++ (also called from _dlfo_read_success). */ + atomic_thread_fence_release (); +- __atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version, +- 1); ++ __atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version, 1); + } + /* Completes an in-place update without switching versions. */ + static inline void + _dlfo_mappings_end_update_no_switch (void) + { +- /* The previous writes to the TM data must not be reordered after +- the version update below. */ ++ /* The store synchronizes with loads in _dlfo_read_start_version ++ (also called from _dlfo_read_success). */ + atomic_thread_fence_release (); +- __atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version, +- 2); ++ __atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version, 2); + } + + /* Return true if the read was successful, given the start +@@ -293,6 +297,19 @@ _dlfo_mappings_end_update_no_switch (void) + static inline bool + _dlfo_read_success (uint64_t start_version) + { ++ /* See Hans Boehm, Can Seqlocks Get Along with Programming Language ++ Memory Models?, Section 4. This is necessary so that loads in ++ the TM region are not ordered past the version check below. */ ++ atomic_thread_fence_acquire (); ++ ++ /* Synchronizes with stores in _dlfo_mappings_begin_update, ++ _dlfo_mappings_end_update, _dlfo_mappings_end_update_no_switch. ++ It is important that all stores from the last update have been ++ visible, and stores from the next updates are not. ++ ++ Unlike with seqlocks, there is no check for odd versions here ++ because we have read the unmodified copy (confirmed to be ++ unmodified by the unchanged version). */ + return _dlfo_read_start_version () == start_version; + } + +@@ -318,7 +335,7 @@ _dlfo_lookup (uintptr_t pc, struct dl_find_object_internal *first1, size_t size) + { + size_t half = size >> 1; + struct dl_find_object_internal *middle = first + half; +- if (middle->map_start < pc) ++ if (atomic_load_relaxed (&middle->map_start) < pc) + { + first = middle + 1; + size -= half + 1; +@@ -327,9 +344,9 @@ _dlfo_lookup (uintptr_t pc, struct dl_find_object_internal *first1, size_t size) + size = half; + } + +- if (first != end && pc == first->map_start) ++ if (first != end && pc == atomic_load_relaxed (&first->map_start)) + { +- if (pc < first->map_end) ++ if (pc < atomic_load_relaxed (&first->map_end)) + return first; + else + /* Zero-length mapping after dlclose. */ +@@ -339,7 +356,7 @@ _dlfo_lookup (uintptr_t pc, struct dl_find_object_internal *first1, size_t size) + { + /* Check to see if PC is in the previous mapping. */ + --first; +- if (pc < first->map_end) ++ if (pc < atomic_load_relaxed (&first->map_end)) + /* pc >= first->map_start implied by the search above. */ + return first; + else +@@ -408,39 +425,47 @@ _dl_find_object (void *pc1, struct dl_find_object *result) + size on earlier unused segments. */ + for (struct dlfo_mappings_segment *seg + = _dlfo_mappings_active_segment (start_version); +- seg != NULL && seg->size > 0; seg = seg->previous) +- if (pc >= seg->objects[0].map_start) +- { +- /* PC may lie within this segment. If it is less than the +- segment start address, it can only lie in a previous +- segment, due to the base address sorting. */ +- struct dl_find_object_internal *obj +- = _dlfo_lookup (pc, seg->objects, seg->size); ++ seg != NULL; ++ seg = atomic_load_acquire (&seg->previous)) ++ { ++ size_t seg_size = atomic_load_relaxed (&seg->size); ++ if (seg_size == 0) ++ break; + +- if (obj != NULL) +- { +- /* Found the right mapping. Copy out the data prior to +- checking if the read transaction was successful. */ +- struct dl_find_object_internal copy = *obj; +- if (_dlfo_read_success (start_version)) +- { +- _dl_find_object_to_external (©, result); +- return 0; +- } +- else +- /* Read transaction failure. */ +- goto retry; +- } +- else +- { +- /* PC is not covered by this mapping. */ +- if (_dlfo_read_success (start_version)) +- return -1; +- else +- /* Read transaction failure. */ +- goto retry; +- } +- } /* if: PC might lie within the current seg. */ ++ if (pc >= atomic_load_relaxed (&seg->objects[0].map_start)) ++ { ++ /* PC may lie within this segment. If it is less than the ++ segment start address, it can only lie in a previous ++ segment, due to the base address sorting. */ ++ struct dl_find_object_internal *obj ++ = _dlfo_lookup (pc, seg->objects, seg_size); ++ ++ if (obj != NULL) ++ { ++ /* Found the right mapping. Copy out the data prior to ++ checking if the read transaction was successful. */ ++ struct dl_find_object_internal copy; ++ _dl_find_object_internal_copy (obj, ©); ++ if (_dlfo_read_success (start_version)) ++ { ++ _dl_find_object_to_external (©, result); ++ return 0; ++ } ++ else ++ /* Read transaction failure. */ ++ goto retry; ++ } ++ else ++ { ++ /* PC is not covered by this mapping. */ ++ if (_dlfo_read_success (start_version)) ++ return -1; ++ else ++ /* Read transaction failure. */ ++ goto retry; ++ } ++ } /* if: PC might lie within the current seg. */ ++ } + + /* PC is not covered by any segment. */ + if (_dlfo_read_success (start_version)) +@@ -619,15 +644,19 @@ static inline size_t + _dlfo_update_init_seg (struct dlfo_mappings_segment *seg, + size_t remaining_to_add) + { ++ size_t new_seg_size; + if (remaining_to_add < seg->allocated) + /* Partially filled segment. */ +- seg->size = remaining_to_add; ++ new_seg_size = remaining_to_add; + else +- seg->size = seg->allocated; +- return seg->size; ++ new_seg_size = seg->allocated; ++ atomic_store_relaxed (&seg->size, new_seg_size); ++ return new_seg_size; + } + +-/* Invoked from _dl_find_object_update after sorting. */ ++/* Invoked from _dl_find_object_update after sorting. Stores to the ++ shared data need to use relaxed MO. But plain loads can be used ++ because the loader lock prevents concurrent stores. */ + static bool + _dl_find_object_update_1 (struct link_map **loaded, size_t count) + { +@@ -727,7 +756,8 @@ _dl_find_object_update_1 (struct link_map **loaded, size_t count) + { + /* Prefer mapping in current_seg. */ + assert (current_seg_index1 > 0); +- *dlfo = current_seg->objects[current_seg_index1 - 1]; ++ _dl_find_object_internal_copy ++ (¤t_seg->objects[current_seg_index1 - 1], dlfo); + --current_seg_index1; + } + else +@@ -753,7 +783,7 @@ _dl_find_object_update_1 (struct link_map **loaded, size_t count) + + /* Prevent searching further into unused segments. */ + if (target_seg->previous != NULL) +- target_seg->previous->size = 0; ++ atomic_store_relaxed (&target_seg->previous->size, 0); + + _dlfo_mappings_end_update (); + return true; +diff --git a/elf/dl-find_object.h b/elf/dl-find_object.h +index f899905e09427a0d..11569efc9b7daf9c 100644 +--- a/elf/dl-find_object.h ++++ b/elf/dl-find_object.h +@@ -20,6 +20,7 @@ + #define _DL_FIND_EH_FRAME_H + + #include ++#include + #include + #include + #include +@@ -44,6 +45,30 @@ struct dl_find_object_internal + #endif + }; + ++/* Create a copy of *SOURCE in *COPY using relaxed MO loads and ++ stores. */ ++static inline void ++_dl_find_object_internal_copy (const struct dl_find_object_internal *source, ++ struct dl_find_object_internal *copy) ++{ ++ atomic_store_relaxed (©->map_start, ++ atomic_load_relaxed (&source->map_start)); ++ atomic_store_relaxed (©->map_end, ++ atomic_load_relaxed (&source->map_end)); ++ atomic_store_relaxed (©->map, ++ atomic_load_relaxed (&source->map)); ++ atomic_store_relaxed (©->eh_frame, ++ atomic_load_relaxed (&source->eh_frame)); ++#if DLFO_STRUCT_HAS_EH_DBASE ++ atomic_store_relaxed (©->eh_dbase, ++ atomic_load_relaxed (&source->eh_dbase)); ++#endif ++#if DLFO_STRUCT_HAS_EH_COUNT ++ atomic_store_relaxed (©->eh_count, ++ atomic_load_relaxed (&source->eh_count)); ++#endif ++} ++ + static inline void + _dl_find_object_to_external (struct dl_find_object_internal *internal, + struct dl_find_object *external) +@@ -62,34 +87,35 @@ _dl_find_object_to_external (struct dl_find_object_internal *internal, + } + + /* Extract the object location data from a link map and writes it to +- *RESULT. */ ++ *RESULT using relaxed MO stores. */ + static void __attribute__ ((unused)) + _dl_find_object_from_map (struct link_map *l, + struct dl_find_object_internal *result) + { +- result->map_start = (uintptr_t) l->l_map_start; +- result->map_end = (uintptr_t) l->l_map_end; +- result->map = l; ++ atomic_store_relaxed (&result->map_start, (uintptr_t) l->l_map_start); ++ atomic_store_relaxed (&result->map_end, (uintptr_t) l->l_map_end); ++ atomic_store_relaxed (&result->map, l); + + #if DLFO_STRUCT_HAS_EH_DBASE +- result->eh_dbase = (void *) l->l_info[DT_PLTGOT]; ++ atomic_store_relaxed (&result->eh_dbase, (void *) l->l_info[DT_PLTGOT]); + #endif + + for (const ElfW(Phdr) *ph = l->l_phdr, *ph_end = l->l_phdr + l->l_phnum; + ph < ph_end; ++ph) + if (ph->p_type == DLFO_EH_SEGMENT_TYPE) + { +- result->eh_frame = (void *) (ph->p_vaddr + l->l_addr); ++ atomic_store_relaxed (&result->eh_frame, ++ (void *) (ph->p_vaddr + l->l_addr)); + #if DLFO_STRUCT_HAS_EH_COUNT +- result->eh_count = ph->p_memsz / 8; ++ atomic_store_relaxed (&result->eh_count, ph->p_memsz / 8); + #endif + return; + } + + /* Object has no exception handling segment. */ +- result->eh_frame = NULL; ++ atomic_store_relaxed (&result->eh_frame, NULL); + #if DLFO_STRUCT_HAS_EH_COUNT +- result->eh_count = 0; ++ atomic_store_relaxed (&result->eh_count, 0); + #endif + } + +diff --git a/elf/tst-dl_find_object-threads.c b/elf/tst-dl_find_object-threads.c +index 472deeec57da0560..453ba684024b5069 100644 +--- a/elf/tst-dl_find_object-threads.c ++++ b/elf/tst-dl_find_object-threads.c +@@ -138,12 +138,12 @@ check (void *address, struct dl_find_object *expected, int line) + #endif + } + +-/* Request process termination after 3 seconds. */ ++/* Request process termination after 0.3 seconds. */ + static bool exit_requested; + static void * + exit_thread (void *ignored) + { +- usleep (3 * 100 * 1000); ++ usleep (300 * 1000); + __atomic_store_n (&exit_requested, true, __ATOMIC_RELAXED); + return NULL; + } diff --git a/glibc-RHEL-93320-6.patch b/glibc-RHEL-93320-6.patch new file mode 100644 index 0000000..bd529ad --- /dev/null +++ b/glibc-RHEL-93320-6.patch @@ -0,0 +1,124 @@ +commit e72ef23ee88187284b4b1ca9b2e314e618429d35 +Author: Florian Weimer +Date: Mon Jan 10 13:31:39 2022 +0100 + + elf: Simplify software TM implementation in _dl_find_object + + With the current set of fences, the version update at the start + of the TM write operation is redundant, and the version update + at the end does not need to use an atomic read-modify-write + operation. + + Also use relaxed MO stores during the dlclose update, and skip any + version changes there. + + Suggested-by: Szabolcs Nagy + Reviewed-by: Szabolcs Nagy + +diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c +index b23bcaf0a15e82cd..2952c651ff26db04 100644 +--- a/elf/dl-find_object.c ++++ b/elf/dl-find_object.c +@@ -123,9 +123,9 @@ struct dlfo_mappings_segment + + /* To achieve async-signal-safety, two copies of the data structure + are used, so that a signal handler can still use this data even if +- dlopen or dlclose modify the other copy. The the MSB in +- _dlfo_loaded_mappings_version determines which array element is the +- currently active region. */ ++ dlopen or dlclose modify the other copy. The the least significant ++ bit in _dlfo_loaded_mappings_version determines which array element ++ is the currently active region. */ + static struct dlfo_mappings_segment *_dlfo_loaded_mappings[2]; + + /* Returns the number of actually used elements in all segments +@@ -248,7 +248,7 @@ _dlfo_read_start_version (void) + { + /* Acquire MO load synchronizes with the fences at the beginning and + end of the TM update region in _dlfo_mappings_begin_update, +- _dlfo_mappings_end_update, _dlfo_mappings_end_update_no_switch. */ ++ _dlfo_mappings_end_update. */ + return __atomic_wide_counter_load_acquire (&_dlfo_loaded_mappings_version); + } + +@@ -261,35 +261,25 @@ _dlfo_read_version_locked (void) + } + + /* Update the version to reflect that an update is happening. This +- does not change the bit that controls the active segment chain. +- Returns the index of the currently active segment chain. */ +-static inline unsigned int ++ does not change the bit that controls the active segment chain. */ ++static inline void + _dlfo_mappings_begin_update (void) + { +- /* The store synchronizes with loads in _dlfo_read_start_version ++ /* The fence synchronizes with loads in _dlfo_read_start_version + (also called from _dlfo_read_success). */ + atomic_thread_fence_release (); +- return __atomic_wide_counter_fetch_add_relaxed +- (&_dlfo_loaded_mappings_version, 2); + } + + /* Installs the just-updated version as the active version. */ + static inline void + _dlfo_mappings_end_update (void) + { +- /* The store synchronizes with loads in _dlfo_read_start_version ++ /* The fence synchronizes with loads in _dlfo_read_start_version + (also called from _dlfo_read_success). */ + atomic_thread_fence_release (); +- __atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version, 1); +-} +-/* Completes an in-place update without switching versions. */ +-static inline void +-_dlfo_mappings_end_update_no_switch (void) +-{ +- /* The store synchronizes with loads in _dlfo_read_start_version +- (also called from _dlfo_read_success). */ +- atomic_thread_fence_release (); +- __atomic_wide_counter_fetch_add_relaxed (&_dlfo_loaded_mappings_version, 2); ++ /* No atomic read-modify-write update needed because of the loader ++ lock. */ ++ __atomic_wide_counter_add_relaxed (&_dlfo_loaded_mappings_version, 1); + } + + /* Return true if the read was successful, given the start +@@ -302,10 +292,11 @@ _dlfo_read_success (uint64_t start_version) + the TM region are not ordered past the version check below. */ + atomic_thread_fence_acquire (); + +- /* Synchronizes with stores in _dlfo_mappings_begin_update, +- _dlfo_mappings_end_update, _dlfo_mappings_end_update_no_switch. +- It is important that all stores from the last update have been +- visible, and stores from the next updates are not. ++ /* Synchronizes with the fences in _dlfo_mappings_begin_update, ++ _dlfo_mappings_end_update. It is important that all stores from ++ the last update have become visible, and stores from the next ++ update to this version are not before the version number is ++ updated. + + Unlike with seqlocks, there is no check for odd versions here + because we have read the unmodified copy (confirmed to be +@@ -839,17 +830,10 @@ _dl_find_object_dlclose (struct link_map *map) + issues around __libc_freeres. */ + return; + +- /* The update happens in-place, but given that we do not use +- atomic accesses on the read side, update the version around +- the update to trigger re-validation in concurrent +- readers. */ +- _dlfo_mappings_begin_update (); +- +- /* Mark as closed. */ +- obj->map_end = obj->map_start; +- obj->map = NULL; +- +- _dlfo_mappings_end_update_no_switch (); ++ /* Mark as closed. This does not change the overall data ++ structure, so no TM cycle is needed. */ ++ atomic_store_relaxed (&obj->map_end, obj->map_start); ++ atomic_store_relaxed (&obj->map, NULL); + return; + } + } diff --git a/glibc-RHEL-93320-7.patch b/glibc-RHEL-93320-7.patch new file mode 100644 index 0000000..3f0ba2f --- /dev/null +++ b/glibc-RHEL-93320-7.patch @@ -0,0 +1,353 @@ +commit b4d4ff8963866367ba861681ef3b1251e122014a +Author: Florian Weimer +Date: Mon Jan 17 09:57:19 2022 +0100 + + elf: Introduce rtld_setup_main_map + + This function collects most of the processing needed to initialize + the link map for the main executable. + + Reviewed-by: H.J. Lu + +Conflicts: + elf/rtld.c + Updated for minor context changes + +diff --git a/elf/rtld.c b/elf/rtld.c +index b5be7674dc1dbf40..a47e105987d87add 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -1129,6 +1129,163 @@ rtld_chain_load (struct link_map *main_map, char *argv0) + rtld_soname, pathname, errcode); + } + ++/* Called to complete the initialization of the link map for the main ++ executable. Returns true if there is a PT_INTERP segment. */ ++static bool ++rtld_setup_main_map (struct link_map *main_map) ++{ ++ /* This have already been filled in right after _dl_new_object, or ++ as part of _dl_map_object. */ ++ const ElfW(Phdr) *phdr = main_map->l_phdr; ++ ElfW(Word) phnum = main_map->l_phnum; ++ ++ bool has_interp = false; ++ ++ main_map->l_map_end = 0; ++ main_map->l_text_end = 0; ++ /* Perhaps the executable has no PT_LOAD header entries at all. */ ++ main_map->l_map_start = ~0; ++ /* And it was opened directly. */ ++ ++main_map->l_direct_opencount; ++ ++ /* Scan the program header table for the dynamic section. */ ++ for (const ElfW(Phdr) *ph = phdr; ph < &phdr[phnum]; ++ph) ++ switch (ph->p_type) ++ { ++ case PT_PHDR: ++ /* Find out the load address. */ ++ main_map->l_addr = (ElfW(Addr)) phdr - ph->p_vaddr; ++ break; ++ case PT_DYNAMIC: ++ /* This tells us where to find the dynamic section, ++ which tells us everything we need to do. */ ++ main_map->l_ld = (void *) main_map->l_addr + ph->p_vaddr; ++ main_map->l_ld_readonly = (ph->p_flags & PF_W) == 0; ++ break; ++ case PT_INTERP: ++ /* This "interpreter segment" was used by the program loader to ++ find the program interpreter, which is this program itself, the ++ dynamic linker. We note what name finds us, so that a future ++ dlopen call or DT_NEEDED entry, for something that wants to link ++ against the dynamic linker as a shared library, will know that ++ the shared object is already loaded. */ ++ _dl_rtld_libname.name = ((const char *) main_map->l_addr ++ + ph->p_vaddr); ++ /* _dl_rtld_libname.next = NULL; Already zero. */ ++ GL(dl_rtld_map).l_libname = &_dl_rtld_libname; ++ ++ /* Ordinarilly, we would get additional names for the loader from ++ our DT_SONAME. This can't happen if we were actually linked as ++ a static executable (detect this case when we have no DYNAMIC). ++ If so, assume the filename component of the interpreter path to ++ be our SONAME, and add it to our name list. */ ++ if (GL(dl_rtld_map).l_ld == NULL) ++ { ++ const char *p = NULL; ++ const char *cp = _dl_rtld_libname.name; ++ ++ /* Find the filename part of the path. */ ++ while (*cp != '\0') ++ if (*cp++ == '/') ++ p = cp; ++ ++ if (p != NULL) ++ { ++ _dl_rtld_libname2.name = p; ++ /* _dl_rtld_libname2.next = NULL; Already zero. */ ++ _dl_rtld_libname.next = &_dl_rtld_libname2; ++ } ++ } ++ ++ has_interp = true; ++ break; ++ case PT_LOAD: ++ { ++ ElfW(Addr) mapstart; ++ ElfW(Addr) allocend; ++ ++ /* Remember where the main program starts in memory. */ ++ mapstart = (main_map->l_addr ++ + (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1))); ++ if (main_map->l_map_start > mapstart) ++ main_map->l_map_start = mapstart; ++ ++ /* Also where it ends. */ ++ allocend = main_map->l_addr + ph->p_vaddr + ph->p_memsz; ++ if (main_map->l_map_end < allocend) ++ main_map->l_map_end = allocend; ++ if ((ph->p_flags & PF_X) && allocend > main_map->l_text_end) ++ main_map->l_text_end = allocend; ++ } ++ break; ++ ++ case PT_TLS: ++ if (ph->p_memsz > 0) ++ { ++ /* Note that in the case the dynamic linker we duplicate work ++ here since we read the PT_TLS entry already in ++ _dl_start_final. But the result is repeatable so do not ++ check for this special but unimportant case. */ ++ main_map->l_tls_blocksize = ph->p_memsz; ++ main_map->l_tls_align = ph->p_align; ++ if (ph->p_align == 0) ++ main_map->l_tls_firstbyte_offset = 0; ++ else ++ main_map->l_tls_firstbyte_offset = (ph->p_vaddr ++ & (ph->p_align - 1)); ++ main_map->l_tls_initimage_size = ph->p_filesz; ++ main_map->l_tls_initimage = (void *) ph->p_vaddr; ++ ++ /* This image gets the ID one. */ ++ GL(dl_tls_max_dtv_idx) = main_map->l_tls_modid = 1; ++ } ++ break; ++ ++ case PT_GNU_STACK: ++ GL(dl_stack_flags) = ph->p_flags; ++ break; ++ ++ case PT_GNU_RELRO: ++ main_map->l_relro_addr = ph->p_vaddr; ++ main_map->l_relro_size = ph->p_memsz; ++ break; ++ } ++ /* Process program headers again, but scan them backwards so ++ that PT_NOTE can be skipped if PT_GNU_PROPERTY exits. */ ++ for (const ElfW(Phdr) *ph = &phdr[phnum]; ph != phdr; --ph) ++ switch (ph[-1].p_type) ++ { ++ case PT_NOTE: ++ _dl_process_pt_note (main_map, -1, &ph[-1]); ++ break; ++ case PT_GNU_PROPERTY: ++ _dl_process_pt_gnu_property (main_map, -1, &ph[-1]); ++ break; ++ } ++ ++ /* Adjust the address of the TLS initialization image in case ++ the executable is actually an ET_DYN object. */ ++ if (main_map->l_tls_initimage != NULL) ++ main_map->l_tls_initimage ++ = (char *) main_map->l_tls_initimage + main_map->l_addr; ++ if (! main_map->l_map_end) ++ main_map->l_map_end = ~0; ++ if (! main_map->l_text_end) ++ main_map->l_text_end = ~0; ++ if (! GL(dl_rtld_map).l_libname && GL(dl_rtld_map).l_name) ++ { ++ /* We were invoked directly, so the program might not have a ++ PT_INTERP. */ ++ _dl_rtld_libname.name = GL(dl_rtld_map).l_name; ++ /* _dl_rtld_libname.next = NULL; Already zero. */ ++ GL(dl_rtld_map).l_libname = &_dl_rtld_libname; ++ } ++ else ++ assert (GL(dl_rtld_map).l_libname); /* How else did we get here? */ ++ ++ return has_interp; ++} ++ + /* Adjusts the contents of the stack and related globals for the user + entry point. The ld.so processed skip_args arguments and bumped + _dl_argv and _dl_argc accordingly. Those arguments are removed from +@@ -1191,11 +1348,9 @@ dl_main (const ElfW(Phdr) *phdr, + ElfW(Addr) *user_entry, + ElfW(auxv_t) *auxv) + { +- const ElfW(Phdr) *ph; + struct link_map *main_map; + size_t file_size; + char *file; +- bool has_interp = false; + unsigned int i; + bool prelinked = false; + bool rtld_is_main = false; +@@ -1397,7 +1552,7 @@ dl_main (const ElfW(Phdr) *phdr, + load the program below unless it has a PT_GNU_STACK indicating + nonexecutable stack is ok. */ + +- for (ph = phdr; ph < &phdr[phnum]; ++ph) ++ for (const ElfW(Phdr) *ph = phdr; ph < &phdr[phnum]; ++ph) + if (ph->p_type == PT_GNU_STACK) + { + GL(dl_stack_flags) = ph->p_flags; +@@ -1519,147 +1674,7 @@ dl_main (const ElfW(Phdr) *phdr, + information for the program. */ + } + +- main_map->l_map_end = 0; +- main_map->l_text_end = 0; +- /* Perhaps the executable has no PT_LOAD header entries at all. */ +- main_map->l_map_start = ~0; +- /* And it was opened directly. */ +- ++main_map->l_direct_opencount; +- +- /* Scan the program header table for the dynamic section. */ +- for (ph = phdr; ph < &phdr[phnum]; ++ph) +- switch (ph->p_type) +- { +- case PT_PHDR: +- /* Find out the load address. */ +- main_map->l_addr = (ElfW(Addr)) phdr - ph->p_vaddr; +- break; +- case PT_DYNAMIC: +- /* This tells us where to find the dynamic section, +- which tells us everything we need to do. */ +- main_map->l_ld = (void *) main_map->l_addr + ph->p_vaddr; +- main_map->l_ld_readonly = (ph->p_flags & PF_W) == 0; +- break; +- case PT_INTERP: +- /* This "interpreter segment" was used by the program loader to +- find the program interpreter, which is this program itself, the +- dynamic linker. We note what name finds us, so that a future +- dlopen call or DT_NEEDED entry, for something that wants to link +- against the dynamic linker as a shared library, will know that +- the shared object is already loaded. */ +- _dl_rtld_libname.name = ((const char *) main_map->l_addr +- + ph->p_vaddr); +- /* _dl_rtld_libname.next = NULL; Already zero. */ +- GL(dl_rtld_map).l_libname = &_dl_rtld_libname; +- +- /* Ordinarilly, we would get additional names for the loader from +- our DT_SONAME. This can't happen if we were actually linked as +- a static executable (detect this case when we have no DYNAMIC). +- If so, assume the filename component of the interpreter path to +- be our SONAME, and add it to our name list. */ +- if (GL(dl_rtld_map).l_ld == NULL) +- { +- const char *p = NULL; +- const char *cp = _dl_rtld_libname.name; +- +- /* Find the filename part of the path. */ +- while (*cp != '\0') +- if (*cp++ == '/') +- p = cp; +- +- if (p != NULL) +- { +- _dl_rtld_libname2.name = p; +- /* _dl_rtld_libname2.next = NULL; Already zero. */ +- _dl_rtld_libname.next = &_dl_rtld_libname2; +- } +- } +- +- has_interp = true; +- break; +- case PT_LOAD: +- { +- ElfW(Addr) mapstart; +- ElfW(Addr) allocend; +- +- /* Remember where the main program starts in memory. */ +- mapstart = (main_map->l_addr +- + (ph->p_vaddr & ~(GLRO(dl_pagesize) - 1))); +- if (main_map->l_map_start > mapstart) +- main_map->l_map_start = mapstart; +- +- /* Also where it ends. */ +- allocend = main_map->l_addr + ph->p_vaddr + ph->p_memsz; +- if (main_map->l_map_end < allocend) +- main_map->l_map_end = allocend; +- if ((ph->p_flags & PF_X) && allocend > main_map->l_text_end) +- main_map->l_text_end = allocend; +- } +- break; +- +- case PT_TLS: +- if (ph->p_memsz > 0) +- { +- /* Note that in the case the dynamic linker we duplicate work +- here since we read the PT_TLS entry already in +- _dl_start_final. But the result is repeatable so do not +- check for this special but unimportant case. */ +- main_map->l_tls_blocksize = ph->p_memsz; +- main_map->l_tls_align = ph->p_align; +- if (ph->p_align == 0) +- main_map->l_tls_firstbyte_offset = 0; +- else +- main_map->l_tls_firstbyte_offset = (ph->p_vaddr +- & (ph->p_align - 1)); +- main_map->l_tls_initimage_size = ph->p_filesz; +- main_map->l_tls_initimage = (void *) ph->p_vaddr; +- +- /* This image gets the ID one. */ +- GL(dl_tls_max_dtv_idx) = main_map->l_tls_modid = 1; +- } +- break; +- +- case PT_GNU_STACK: +- GL(dl_stack_flags) = ph->p_flags; +- break; +- +- case PT_GNU_RELRO: +- main_map->l_relro_addr = ph->p_vaddr; +- main_map->l_relro_size = ph->p_memsz; +- break; +- } +- /* Process program headers again, but scan them backwards so +- that PT_NOTE can be skipped if PT_GNU_PROPERTY exits. */ +- for (ph = &phdr[phnum]; ph != phdr; --ph) +- switch (ph[-1].p_type) +- { +- case PT_NOTE: +- _dl_process_pt_note (main_map, -1, &ph[-1]); +- break; +- case PT_GNU_PROPERTY: +- _dl_process_pt_gnu_property (main_map, -1, &ph[-1]); +- break; +- } +- +- /* Adjust the address of the TLS initialization image in case +- the executable is actually an ET_DYN object. */ +- if (main_map->l_tls_initimage != NULL) +- main_map->l_tls_initimage +- = (char *) main_map->l_tls_initimage + main_map->l_addr; +- if (! main_map->l_map_end) +- main_map->l_map_end = ~0; +- if (! main_map->l_text_end) +- main_map->l_text_end = ~0; +- if (! GL(dl_rtld_map).l_libname && GL(dl_rtld_map).l_name) +- { +- /* We were invoked directly, so the program might not have a +- PT_INTERP. */ +- _dl_rtld_libname.name = GL(dl_rtld_map).l_name; +- /* _dl_rtld_libname.next = NULL; Already zero. */ +- GL(dl_rtld_map).l_libname = &_dl_rtld_libname; +- } +- else +- assert (GL(dl_rtld_map).l_libname); /* How else did we get here? */ ++ bool has_interp = rtld_setup_main_map (main_map); + + /* If the current libname is different from the SONAME, add the + latter as well. */ diff --git a/glibc-RHEL-93320-8.patch b/glibc-RHEL-93320-8.patch new file mode 100644 index 0000000..6715ac4 --- /dev/null +++ b/glibc-RHEL-93320-8.patch @@ -0,0 +1,70 @@ +commit 8eb2510d38226ce10a3a15109be948f052585106 +Author: Florian Weimer +Date: Mon Jan 17 09:57:19 2022 +0100 + + elf: Set l_contiguous to 1 for the main map in more cases + + l_contiguous was not initialized at all for the main map and + always 0. This commit adds code to check if the LOAD segments + are adjacent to each other, and sets l_contiguous accordingly. + This helps _dl_find_object because it is more efficient if the + main mapping is contiguous. + + Note that not all (PIE or non-PIE) binaries are contiguous in this + way because BFD ld creates executables with LOAD holes: + + ELF LOAD segments creating holes in the process image on GNU/Linux + https://sourceware.org/pipermail/binutils/2022-January/119082.html + https://sourceware.org/bugzilla/show_bug.cgi?id=28743 + + Reviewed-by: H.J. Lu + +diff --git a/elf/rtld.c b/elf/rtld.c +index a47e105987d87add..d698a32ae120e887 100644 +--- a/elf/rtld.c ++++ b/elf/rtld.c +@@ -1147,6 +1147,22 @@ rtld_setup_main_map (struct link_map *main_map) + main_map->l_map_start = ~0; + /* And it was opened directly. */ + ++main_map->l_direct_opencount; ++ main_map->l_contiguous = 1; ++ ++ /* A PT_LOAD segment at an unexpected address will clear the ++ l_contiguous flag. The ELF specification says that PT_LOAD ++ segments need to be sorted in in increasing order, but perhaps ++ not all executables follow this requirement. Having l_contiguous ++ equal to 1 is just an optimization, so the code below does not ++ try to sort the segments in case they are unordered. ++ ++ There is one corner case in which l_contiguous is not set to 1, ++ but where it could be set: If a PIE (ET_DYN) binary is loaded by ++ glibc itself (not the kernel), it is always contiguous due to the ++ way the glibc loader works. However, the kernel loader may still ++ create holes in this case, and the code here still uses 0 ++ conservatively for the glibc-loaded case, too. */ ++ ElfW(Addr) expected_load_address = 0; + + /* Scan the program header table for the dynamic section. */ + for (const ElfW(Phdr) *ph = phdr; ph < &phdr[phnum]; ++ph) +@@ -1210,12 +1226,21 @@ rtld_setup_main_map (struct link_map *main_map) + if (main_map->l_map_start > mapstart) + main_map->l_map_start = mapstart; + ++ if (main_map->l_contiguous && expected_load_address != 0 ++ && expected_load_address != mapstart) ++ main_map->l_contiguous = 0; ++ + /* Also where it ends. */ + allocend = main_map->l_addr + ph->p_vaddr + ph->p_memsz; + if (main_map->l_map_end < allocend) + main_map->l_map_end = allocend; + if ((ph->p_flags & PF_X) && allocend > main_map->l_text_end) + main_map->l_text_end = allocend; ++ ++ /* The next expected address is the page following this load ++ segment. */ ++ expected_load_address = ((allocend + GLRO(dl_pagesize) - 1) ++ & ~(GLRO(dl_pagesize) - 1)); + } + break; + diff --git a/glibc-RHEL-93320-9.patch b/glibc-RHEL-93320-9.patch new file mode 100644 index 0000000..86d2780 --- /dev/null +++ b/glibc-RHEL-93320-9.patch @@ -0,0 +1,49 @@ +commit 06200aac9bec34dbcac28b8c60e49a77e7851c1f +Author: Florian Weimer +Date: Mon Jan 17 09:57:19 2022 +0100 + + elf/tst-dl_find_object: Disable subtests for non-contiguous maps (bug 28732) + + Reviewed-by: H.J. Lu + +diff --git a/elf/tst-dl_find_object.c b/elf/tst-dl_find_object.c +index 9abffa35d479c8fc..d8c217545d116453 100644 +--- a/elf/tst-dl_find_object.c ++++ b/elf/tst-dl_find_object.c +@@ -71,19 +71,24 @@ check (void *address, + __FILE__, line, address, + actual.dlfo_flags, expected->dlfo_flags); + } +- if (actual.dlfo_flags != expected->dlfo_flags) ++ if (expected->dlfo_link_map->l_contiguous) + { +- support_record_failure (); +- printf ("%s:%d: error: %p: map start is %p, expected %p\n", +- __FILE__, line, +- address, actual.dlfo_map_start, expected->dlfo_map_start); +- } +- if (actual.dlfo_map_end != expected->dlfo_map_end) +- { +- support_record_failure (); +- printf ("%s:%d: error: %p: map end is %p, expected %p\n", +- __FILE__, line, +- address, actual.dlfo_map_end, expected->dlfo_map_end); ++ /* If the mappings are not contiguous, the actual and execpted ++ mappings may differ, so this subtest will not work. */ ++ if (actual.dlfo_flags != expected->dlfo_flags) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: map start is %p, expected %p\n", ++ __FILE__, line, ++ address, actual.dlfo_map_start, expected->dlfo_map_start); ++ } ++ if (actual.dlfo_map_end != expected->dlfo_map_end) ++ { ++ support_record_failure (); ++ printf ("%s:%d: error: %p: map end is %p, expected %p\n", ++ __FILE__, line, ++ address, actual.dlfo_map_end, expected->dlfo_map_end); ++ } + } + if (actual.dlfo_link_map != expected->dlfo_link_map) + { diff --git a/glibc.abignore b/glibc.abignore index 57168e3..3a06461 100644 --- a/glibc.abignore +++ b/glibc.abignore @@ -15,3 +15,31 @@ name = _dl_readonly_area parameter = '0 void * parameter = '1 size_t + +# Changes in glibc-2.34-216. +[suppress_function] + symbol_name = epoll_pwait2 +[suppress_function] + symbol_name = __epoll_pwait2_time64 +[suppress_function] + # Use symbol name to ignore the (unexported) __-prefixed symbol as well. + symbol_name = posix_spawn_file_actions_addtcsetpgrp_np +[suppress_function] + # Use symbol_name to ignore bcmp, memcmp aliases of the new symbol. + symbol_name = __memcmpeq +[suppress_function] + symbol_name = _dl_find_object +[suppress_function] + name = __dl_find_object_internal + parameter = '0 void * + parameter = '1 dl_find_object * +[suppress_function] + name = __rtld_libc_freeres +[suppress_type] + type_kind = struct + name = link_map + has_data_member_inserted_between = {offset_of(l_tls_in_slotinfo), offset_of(l_nodelete_active)} +[suppress_type] + type_kind = struct + name = __pthread_cond_s + has_data_member = {__wseq, __wseq32, __g1_start, __g1_start32} diff --git a/glibc.spec b/glibc.spec index c8d5513..c68393a 100644 --- a/glibc.spec +++ b/glibc.spec @@ -157,7 +157,7 @@ end \ Summary: The GNU libc libraries Name: glibc Version: %{glibcversion} -Release: 215%{?dist} +Release: 216%{?dist} # In general, GPLv2+ is used by programs, LGPLv2+ is used for # libraries. @@ -1302,6 +1302,25 @@ Patch992: glibc-RHEL-49549-8.patch Patch993: glibc-RHEL-49549-9.patch Patch994: glibc-RHEL-101986-1.patch Patch995: glibc-RHEL-101986-2.patch +Patch996: glibc-RHEL-93320-1.patch +Patch997: glibc-RHEL-93320-2.patch +Patch998: glibc-RHEL-93320-3.patch +Patch999: glibc-RHEL-93320-4.patch +Patch1000: glibc-RHEL-93320-5.patch +Patch1001: glibc-RHEL-93320-6.patch +Patch1002: glibc-RHEL-93320-7.patch +Patch1003: glibc-RHEL-93320-8.patch +Patch1004: glibc-RHEL-93320-9.patch +Patch1005: glibc-RHEL-93320-10.patch +Patch1006: glibc-RHEL-93320-11.patch +Patch1007: glibc-RHEL-93320-12.patch +Patch1008: glibc-RHEL-93320-13.patch +Patch1009: glibc-RHEL-93320-14.patch +Patch1010: glibc-RHEL-93320-15.patch +Patch1011: glibc-RHEL-93320-16.patch +Patch1012: glibc-RHEL-93320-17.patch +Patch1013: glibc-RHEL-93320-18.patch +Patch1014: glibc-RHEL-93320-19.patch ############################################################################## # Continued list of core "glibc" package information: @@ -3299,6 +3318,9 @@ update_gconv_modules_cache () %endif %changelog +* Mon Jul 14 2025 Benjamin Herrenschmidt - 2.34-216 +- Backport GLIBC_2.35 libc symbols incl. _dl_find_object (RHEL-93320) + * Thu Jul 10 2025 Arjun Shankar - 2.34-215 - Extend struct r_debug to support multiple namespaces (RHEL-101986)