import UBI glibc-2.34-231.el9_7.2
This commit is contained in:
parent
1e4fd33b14
commit
d92826cafc
148
SOURCES/glibc-RHEL-101986-1.patch
Normal file
148
SOURCES/glibc-RHEL-101986-1.patch
Normal file
@ -0,0 +1,148 @@
|
||||
commit 8329939a37f483a16013dd8af8303cbcb86d92cb
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Jul 4 21:46:16 2025 +0200
|
||||
|
||||
elf: Introduce _dl_debug_change_state
|
||||
|
||||
It combines updating r_state with the debugger notification.
|
||||
|
||||
The second change to _dl_open introduces an additional debugger
|
||||
notification for dlmopen, but debuggers are expected to ignore it.
|
||||
|
||||
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
|
||||
|
||||
diff --git a/elf/dl-close.c b/elf/dl-close.c
|
||||
index 236d89f67f3bf410..fa3974afba798073 100644
|
||||
--- a/elf/dl-close.c
|
||||
+++ b/elf/dl-close.c
|
||||
@@ -472,8 +472,7 @@ _dl_close_worker (struct link_map *map, bool force)
|
||||
/* Notify the debugger we are about to remove some loaded objects.
|
||||
LA_ACT_DELETE has already been signalled above for !unload_any. */
|
||||
struct r_debug *r = _dl_debug_update (nsid);
|
||||
- r->r_state = RT_DELETE;
|
||||
- _dl_debug_state ();
|
||||
+ _dl_debug_change_state (r, RT_DELETE);
|
||||
LIBC_PROBE (unmap_start, 2, nsid, r);
|
||||
|
||||
if (unload_global)
|
||||
@@ -762,8 +761,7 @@ _dl_close_worker (struct link_map *map, bool force)
|
||||
__rtld_lock_unlock_recursive (GL(dl_load_tls_lock));
|
||||
|
||||
/* Notify the debugger those objects are finalized and gone. */
|
||||
- r->r_state = RT_CONSISTENT;
|
||||
- _dl_debug_state ();
|
||||
+ _dl_debug_change_state (r, RT_CONSISTENT);
|
||||
LIBC_PROBE (unmap_complete, 2, nsid, r);
|
||||
|
||||
#ifdef SHARED
|
||||
diff --git a/elf/dl-debug.c b/elf/dl-debug.c
|
||||
index 649386d5a6b885ed..f840a1b92292968d 100644
|
||||
--- a/elf/dl-debug.c
|
||||
+++ b/elf/dl-debug.c
|
||||
@@ -67,6 +67,13 @@ _dl_debug_update (Lmid_t ns)
|
||||
return &r->base;
|
||||
}
|
||||
|
||||
+void
|
||||
+_dl_debug_change_state (struct r_debug *r, int state)
|
||||
+{
|
||||
+ atomic_store_release (&r->r_state, state);
|
||||
+ _dl_debug_state ();
|
||||
+}
|
||||
+
|
||||
/* Initialize _r_debug_extended for the namespace NS. LDBASE is the
|
||||
run-time load address of the dynamic linker, to be put in
|
||||
_r_debug_extended.r_ldbase. Return the address of _r_debug. */
|
||||
diff --git a/elf/dl-load.c b/elf/dl-load.c
|
||||
index 6714807946b60188..c118db811d8899f6 100644
|
||||
--- a/elf/dl-load.c
|
||||
+++ b/elf/dl-load.c
|
||||
@@ -946,8 +946,7 @@ _dl_notify_new_object (int mode, Lmid_t nsid, struct link_map *l)
|
||||
/* Notify the debugger we have added some objects. We need to
|
||||
call _dl_debug_initialize in a static program in case dynamic
|
||||
linking has not been used before. */
|
||||
- r->r_state = RT_ADD;
|
||||
- _dl_debug_state ();
|
||||
+ _dl_debug_change_state (r, RT_ADD);
|
||||
LIBC_PROBE (map_start, 2, nsid, r);
|
||||
}
|
||||
else
|
||||
diff --git a/elf/dl-open.c b/elf/dl-open.c
|
||||
index 1e61e402455da666..df6aa55a8842ee62 100644
|
||||
--- a/elf/dl-open.c
|
||||
+++ b/elf/dl-open.c
|
||||
@@ -787,8 +787,7 @@ dl_open_worker (void *a)
|
||||
#ifdef SHARED
|
||||
bool was_not_consistent = r->r_state != RT_CONSISTENT;
|
||||
#endif
|
||||
- r->r_state = RT_CONSISTENT;
|
||||
- _dl_debug_state ();
|
||||
+ _dl_debug_change_state (r, RT_CONSISTENT);
|
||||
LIBC_PROBE (map_complete, 3, nsid, r, args->map);
|
||||
|
||||
#ifdef SHARED
|
||||
@@ -866,7 +865,7 @@ no more namespaces available for dlmopen()"));
|
||||
}
|
||||
|
||||
GL(dl_ns)[nsid].libc_map = NULL;
|
||||
- _dl_debug_update (nsid)->r_state = RT_CONSISTENT;
|
||||
+ _dl_debug_change_state (_dl_debug_update (nsid), RT_CONSISTENT);
|
||||
}
|
||||
/* Never allow loading a DSO in a namespace which is empty. Such
|
||||
direct placements is only causing problems. Also don't allow
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index cd233174c9d944b2..0fe9986e4c7ed830 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -1842,8 +1842,7 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
#endif
|
||||
|
||||
/* We start adding objects. */
|
||||
- r->r_state = RT_ADD;
|
||||
- _dl_debug_state ();
|
||||
+ _dl_debug_change_state (r, RT_ADD);
|
||||
LIBC_PROBE (init_start, 2, LM_ID_BASE, r);
|
||||
|
||||
/* Auditing checkpoint: we are ready to signal that the initial map
|
||||
@@ -2527,8 +2526,7 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
/* Notify the debugger all new objects are now ready to go. We must re-get
|
||||
the address since by now the variable might be in another object. */
|
||||
r = _dl_debug_update (LM_ID_BASE);
|
||||
- r->r_state = RT_CONSISTENT;
|
||||
- _dl_debug_state ();
|
||||
+ _dl_debug_change_state (r, RT_CONSISTENT);
|
||||
LIBC_PROBE (init_complete, 2, LM_ID_BASE, r);
|
||||
|
||||
#ifdef SHARED
|
||||
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
||||
index 21dbe2d21ed8e605..371c32dd79c3ea2b 100644
|
||||
--- a/sysdeps/generic/ldsodefs.h
|
||||
+++ b/sysdeps/generic/ldsodefs.h
|
||||
@@ -1129,8 +1129,14 @@ extern void _dl_debug_state (void);
|
||||
rtld_hidden_proto (_dl_debug_state)
|
||||
|
||||
/* Initialize `struct r_debug_extended' for the namespace NS. LDBASE
|
||||
- is the run-time load address of the dynamic linker, to be put in the
|
||||
- `r_ldbase' member. Return the address of the structure. */
|
||||
+ is the run-time load address of the dynamic linker, to be put in
|
||||
+ the `r_ldbase' member.
|
||||
+
|
||||
+ Return the address of the r_debug structure for the namespace.
|
||||
+ This is not merely a convenience or optimization, but it is
|
||||
+ necessary for the LIBC_PROBE Systemtap/debugger probes to work
|
||||
+ reliably: direct variable access can create probes that tools
|
||||
+ cannot consume. */
|
||||
extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
|
||||
attribute_hidden;
|
||||
|
||||
@@ -1138,6 +1144,10 @@ extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
|
||||
of the namespace NS. */
|
||||
extern struct r_debug *_dl_debug_update (Lmid_t ns) attribute_hidden;
|
||||
|
||||
+/* Update R->r_state to STATE and notify the debugger by calling
|
||||
+ _dl_debug_state. */
|
||||
+void _dl_debug_change_state (struct r_debug *r, int state) attribute_hidden;
|
||||
+
|
||||
/* Initialize the basic data structure for the search paths. SOURCE
|
||||
is either "LD_LIBRARY_PATH" or "--library-path".
|
||||
GLIBC_HWCAPS_PREPEND adds additional glibc-hwcaps subdirectories to
|
||||
264
SOURCES/glibc-RHEL-101986-2.patch
Normal file
264
SOURCES/glibc-RHEL-101986-2.patch
Normal file
@ -0,0 +1,264 @@
|
||||
commit ea85e7d55087075376a29261e722e4fae14ecbe7
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Jul 4 21:46:30 2025 +0200
|
||||
|
||||
elf: Restore support for _r_debug interpositions and copy relocations
|
||||
|
||||
The changes in commit a93d9e03a31ec14405cb3a09aa95413b67067380
|
||||
("Extend struct r_debug to support multiple namespaces [BZ #15971]")
|
||||
break the dyninst dynamic instrumentation tool. It brings its
|
||||
own definition of _r_debug (rather than a declaration).
|
||||
|
||||
Furthermore, it turns out it is rather hard to use the proposed
|
||||
handshake for accessing _r_debug via DT_DEBUG. If applications want
|
||||
to access _r_debug, they can do so directly if the relevant code has
|
||||
been built as PIC. To protect against harm from accidental copy
|
||||
relocations due to linker relaxations, this commit restores copy
|
||||
relocation support by adjusting both copies if interposition or
|
||||
copy relocations are in play. Therefore, it is possible to
|
||||
use a hidden reference in ld.so to access _r_debug.
|
||||
|
||||
Only perform the copy relocation initialization if libc has been
|
||||
loaded. Otherwise, the ld.so search scope can be empty, and the
|
||||
lookup of the _r_debug symbol mail fail.
|
||||
|
||||
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
|
||||
|
||||
Conflicts:
|
||||
elf/rtld.c: Adjust for prelink.
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index 721f254d121118c0..3eac746d21042ec9 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -404,6 +404,8 @@ tests += \
|
||||
tst-dlmopen1 \
|
||||
tst-dlmopen3 \
|
||||
tst-dlmopen4 \
|
||||
+ tst-dlmopen4-nonpic \
|
||||
+ tst-dlmopen4-pic \
|
||||
tst-dlopen-auditdup \
|
||||
tst-dlopen-constructor-null \
|
||||
tst-dlopen-self \
|
||||
@@ -1986,6 +1988,13 @@ $(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so
|
||||
|
||||
$(objpfx)tst-dlmopen4.out: $(objpfx)tst-dlmopen1mod.so
|
||||
|
||||
+CFLAGS-tst-dlmopen4-pic.c += -fPIC
|
||||
+$(objpfx)tst-dlmopen4-pic.out: $(objpfx)tst-dlmopen1mod.so
|
||||
+
|
||||
+CFLAGS-tst-dlmopen4-nonpic.c += -fno-pie
|
||||
+tst-dlmopen4-nonpic-no-pie = yes
|
||||
+$(objpfx)tst-dlmopen4-nonpic.out: $(objpfx)tst-dlmopen1mod.so
|
||||
+
|
||||
$(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so
|
||||
tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
|
||||
|
||||
diff --git a/elf/dl-debug-symbols.S b/elf/dl-debug-symbols.S
|
||||
index 28456ab1f237ea87..629b0c0c6b2cd9e1 100644
|
||||
--- a/elf/dl-debug-symbols.S
|
||||
+++ b/elf/dl-debug-symbols.S
|
||||
@@ -38,3 +38,4 @@
|
||||
_r_debug:
|
||||
_r_debug_extended:
|
||||
.zero R_DEBUG_EXTENDED_SIZE
|
||||
+rtld_hidden_def (_r_debug)
|
||||
diff --git a/elf/dl-debug.c b/elf/dl-debug.c
|
||||
index f840a1b92292968d..4388a04cdf828898 100644
|
||||
--- a/elf/dl-debug.c
|
||||
+++ b/elf/dl-debug.c
|
||||
@@ -16,6 +16,7 @@
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
+#include <assert.h>
|
||||
#include <ldsodefs.h>
|
||||
|
||||
|
||||
@@ -37,6 +38,37 @@ extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr)
|
||||
to LM_ID_BASE + 1. See elf/dl-debug-symbols.S. */
|
||||
struct r_debug_extended _r_debug_array[DL_NNS - 1];
|
||||
|
||||
+/* If not null, pointer to the _r_debug in the main executable. */
|
||||
+static struct r_debug *_r_debug_main;
|
||||
+
|
||||
+void
|
||||
+_dl_debug_post_relocate (struct link_map *main_map)
|
||||
+{
|
||||
+ /* Perform a full symbol search in all objects, to maintain
|
||||
+ compatibility if interposed _r_debug definitions. The lookup
|
||||
+ cannot fail because there is a definition in ld.so, and this
|
||||
+ function is only called if the ld.so search scope is not empty. */
|
||||
+ const ElfW(Sym) *sym = NULL;
|
||||
+ lookup_t result =_dl_lookup_symbol_x ("_r_debug", main_map, &sym,
|
||||
+ main_map->l_scope, NULL, 0, 0, NULL);
|
||||
+ if (sym->st_size >= sizeof (struct r_debug))
|
||||
+ {
|
||||
+ struct r_debug *main_r_debug = DL_SYMBOL_ADDRESS (result, sym);
|
||||
+ if (main_r_debug != &_r_debug_extended.base)
|
||||
+ {
|
||||
+ /* The extended version of the struct is not available in
|
||||
+ the main executable because a copy relocation has been
|
||||
+ used. r_map etc. have already been copied as part of the
|
||||
+ copy relocation processing. */
|
||||
+ main_r_debug->r_version = 1;
|
||||
+
|
||||
+ /* Record that dual updates of the initial link map are
|
||||
+ required. */
|
||||
+ _r_debug_main = main_r_debug;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/* Return the r_debug object for the namespace NS. */
|
||||
static inline struct r_debug_extended *
|
||||
get_rdebug (Lmid_t ns)
|
||||
@@ -71,6 +103,11 @@ void
|
||||
_dl_debug_change_state (struct r_debug *r, int state)
|
||||
{
|
||||
atomic_store_release (&r->r_state, state);
|
||||
+#ifdef SHARED
|
||||
+ if (r == &_r_debug_extended.base && _r_debug_main != NULL)
|
||||
+ /* Update the copy-relocation of _r_debug. */
|
||||
+ atomic_store_release (&_r_debug_main->r_state, state);
|
||||
+#endif
|
||||
_dl_debug_state ();
|
||||
}
|
||||
|
||||
@@ -103,7 +140,9 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
|
||||
if (ns - 1 == LM_ID_BASE)
|
||||
{
|
||||
atomic_store_release (&_r_debug_extended.r_next, r);
|
||||
- /* Now there are multiple namespaces. */
|
||||
+ /* Now there are multiple namespaces. Note that this
|
||||
+ deliberately does not update the copy in the main
|
||||
+ executable (if it exists). */
|
||||
atomic_store_release (&_r_debug_extended.base.r_version, 2);
|
||||
}
|
||||
else
|
||||
@@ -116,8 +155,15 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
|
||||
}
|
||||
|
||||
if (r->base.r_map == NULL)
|
||||
- atomic_store_release (&r->base.r_map,
|
||||
- (void *) GL(dl_ns)[ns]._ns_loaded);
|
||||
+ {
|
||||
+ struct link_map_public *l = (void *) GL(dl_ns)[ns]._ns_loaded;
|
||||
+ atomic_store_release (&r->base.r_map, l);
|
||||
+#ifdef SHARED
|
||||
+ if (ns == LM_ID_BASE && _r_debug_main != NULL)
|
||||
+ /* Update the copy-relocation of _r_debug. */
|
||||
+ atomic_store_release (&_r_debug_main->r_map, l);
|
||||
+#endif
|
||||
+ }
|
||||
|
||||
return &r->base;
|
||||
}
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index 0fe9986e4c7ed830..dac827e249b2fe14 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -2395,6 +2395,9 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
/* Likewise for the locking implementation. */
|
||||
__rtld_mutex_init ();
|
||||
|
||||
+ /* Update copy-relocated _r_debug if necessary. */
|
||||
+ _dl_debug_post_relocate (main_map);
|
||||
+
|
||||
/* Mark all the objects so we know they have been already relocated. */
|
||||
for (struct link_map *l = main_map; l != NULL; l = l->l_next)
|
||||
{
|
||||
@@ -2505,6 +2508,9 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
|
||||
__rtld_mutex_init ();
|
||||
__rtld_malloc_init_real (main_map);
|
||||
+
|
||||
+ /* Update copy-relocated _r_debug if necessary. */
|
||||
+ _dl_debug_post_relocate (main_map);
|
||||
}
|
||||
|
||||
/* All ld.so initialization is complete. Apply RELRO. */
|
||||
diff --git a/elf/tst-dlmopen4-nonpic.c b/elf/tst-dlmopen4-nonpic.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..ad4e40995337f4f9
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dlmopen4-nonpic.c
|
||||
@@ -0,0 +1,2 @@
|
||||
+#define BUILD_FOR_NONPIC
|
||||
+#include "tst-dlmopen4.c"
|
||||
diff --git a/elf/tst-dlmopen4-pic.c b/elf/tst-dlmopen4-pic.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..919fa85c2579fb5d
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dlmopen4-pic.c
|
||||
@@ -0,0 +1,2 @@
|
||||
+#define BUILD_FOR_PIC
|
||||
+#include "tst-dlmopen4.c"
|
||||
diff --git a/elf/tst-dlmopen4.c b/elf/tst-dlmopen4.c
|
||||
index 3fe150e50bc259f0..633addf41978cee8 100644
|
||||
--- a/elf/tst-dlmopen4.c
|
||||
+++ b/elf/tst-dlmopen4.c
|
||||
@@ -53,6 +53,15 @@ do_test (void)
|
||||
TEST_COMPARE (debug->base.r_version, 1);
|
||||
TEST_VERIFY_EXIT (debug->r_next == NULL);
|
||||
|
||||
+#ifdef BUILD_FOR_PIC
|
||||
+ /* In a PIC build, using _r_debug directly should give us the same
|
||||
+ object. */
|
||||
+ TEST_VERIFY (&_r_debug == &debug->base);
|
||||
+#endif
|
||||
+#ifdef BUILD_FOR_NONPIC
|
||||
+ TEST_COMPARE (_r_debug.r_version, 1);
|
||||
+#endif
|
||||
+
|
||||
void *h = xdlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so",
|
||||
RTLD_LAZY);
|
||||
|
||||
@@ -64,6 +73,19 @@ do_test (void)
|
||||
const char *name = basename (debug->r_next->base.r_map->l_name);
|
||||
TEST_COMPARE_STRING (name, "tst-dlmopen1mod.so");
|
||||
|
||||
+#ifdef BUILD_FOR_NONPIC
|
||||
+ /* If a copy relocation is used, it must be at version 1. */
|
||||
+ if (&_r_debug != &debug->base)
|
||||
+ {
|
||||
+ TEST_COMPARE (_r_debug.r_version, 1);
|
||||
+ TEST_COMPARE ((uintptr_t) _r_debug.r_map,
|
||||
+ (uintptr_t) debug->base.r_map);
|
||||
+ TEST_COMPARE (_r_debug.r_brk, debug->base.r_brk);
|
||||
+ TEST_COMPARE (_r_debug.r_state, debug->base.r_state);
|
||||
+ TEST_COMPARE (_r_debug.r_ldbase, debug->base.r_ldbase);
|
||||
+ }
|
||||
+#endif
|
||||
+
|
||||
xdlclose (h);
|
||||
|
||||
return 0;
|
||||
diff --git a/include/link.h b/include/link.h
|
||||
index 0cf130ddb8af2e89..bafac6c9628b183c 100644
|
||||
--- a/include/link.h
|
||||
+++ b/include/link.h
|
||||
@@ -366,6 +366,8 @@ struct auditstate
|
||||
dynamic linker. */
|
||||
extern struct r_debug_extended _r_debug_extended attribute_hidden;
|
||||
|
||||
+rtld_hidden_proto (_r_debug)
|
||||
+
|
||||
#if __ELF_NATIVE_CLASS == 32
|
||||
# define symbind symbind32
|
||||
# define LA_SYMBIND "la_symbind32"
|
||||
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
||||
index 371c32dd79c3ea2b..484893c2928db8e7 100644
|
||||
--- a/sysdeps/generic/ldsodefs.h
|
||||
+++ b/sysdeps/generic/ldsodefs.h
|
||||
@@ -1140,6 +1140,10 @@ rtld_hidden_proto (_dl_debug_state)
|
||||
extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
|
||||
attribute_hidden;
|
||||
|
||||
+/* This is called after relocation processing to handle a potential
|
||||
+ copy relocation for _r_debug. */
|
||||
+void _dl_debug_post_relocate (struct link_map *main_map) attribute_hidden;
|
||||
+
|
||||
/* Update the `r_map' member and return the address of `struct r_debug'
|
||||
of the namespace NS. */
|
||||
extern struct r_debug *_dl_debug_update (Lmid_t ns) attribute_hidden;
|
||||
106
SOURCES/glibc-RHEL-104852-1.patch
Normal file
106
SOURCES/glibc-RHEL-104852-1.patch
Normal file
@ -0,0 +1,106 @@
|
||||
commit 2cac9559e06044ba520e785c151fbbd25011865f
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Aug 1 10:20:23 2025 +0200
|
||||
|
||||
elf: Extract rtld_setup_phdr function from dl_main
|
||||
|
||||
Remove historic binutils reference from comment and update
|
||||
how this data is used by applications.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
Conflicts:
|
||||
elf/rtld.
|
||||
(missing __ehdr_start cleanup downstream)
|
||||
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index 667880e18ae816d8..a9073d4e14b07410 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -1306,6 +1306,45 @@ rtld_setup_main_map (struct link_map *main_map)
|
||||
return has_interp;
|
||||
}
|
||||
|
||||
+/* Set up the program header information for the dynamic linker
|
||||
+ itself. It can be accessed via _r_debug and dl_iterate_phdr
|
||||
+ callbacks. */
|
||||
+static void
|
||||
+rtld_setup_phdr (void)
|
||||
+{
|
||||
+ const ElfW(Ehdr) *rtld_ehdr;
|
||||
+
|
||||
+ /* Starting from binutils-2.23, the linker will define the magic symbol
|
||||
+ __ehdr_start to point to our own ELF header if it is visible in a
|
||||
+ segment that also includes the phdrs. If that's not available, we use
|
||||
+ the old method that assumes the beginning of the file is part of the
|
||||
+ lowest-addressed PT_LOAD segment. */
|
||||
+#ifdef HAVE_EHDR_START
|
||||
+ extern const ElfW(Ehdr) __ehdr_start __attribute__ ((visibility ("hidden")));
|
||||
+ rtld_ehdr = &__ehdr_start;
|
||||
+#else
|
||||
+ rtld_ehdr = (void *) GL(dl_rtld_map).l_map_start;
|
||||
+#endif
|
||||
+ assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr);
|
||||
+ assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr)));
|
||||
+
|
||||
+ const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff;
|
||||
+
|
||||
+ GL(dl_rtld_map).l_phdr = rtld_phdr;
|
||||
+ GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum;
|
||||
+
|
||||
+
|
||||
+ /* PT_GNU_RELRO is usually the last phdr. */
|
||||
+ size_t cnt = rtld_ehdr->e_phnum;
|
||||
+ while (cnt-- > 0)
|
||||
+ if (rtld_phdr[cnt].p_type == PT_GNU_RELRO)
|
||||
+ {
|
||||
+ GL(dl_rtld_map).l_relro_addr = rtld_phdr[cnt].p_vaddr;
|
||||
+ GL(dl_rtld_map).l_relro_size = rtld_phdr[cnt].p_memsz;
|
||||
+ break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/* 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
|
||||
@@ -1790,39 +1829,7 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
if (GLRO(dl_use_load_bias) == (ElfW(Addr)) -2)
|
||||
GLRO(dl_use_load_bias) = main_map->l_addr == 0 ? -1 : 0;
|
||||
|
||||
- /* Set up the program header information for the dynamic linker
|
||||
- itself. It is needed in the dl_iterate_phdr callbacks. */
|
||||
- const ElfW(Ehdr) *rtld_ehdr;
|
||||
-
|
||||
- /* Starting from binutils-2.23, the linker will define the magic symbol
|
||||
- __ehdr_start to point to our own ELF header if it is visible in a
|
||||
- segment that also includes the phdrs. If that's not available, we use
|
||||
- the old method that assumes the beginning of the file is part of the
|
||||
- lowest-addressed PT_LOAD segment. */
|
||||
-#ifdef HAVE_EHDR_START
|
||||
- extern const ElfW(Ehdr) __ehdr_start __attribute__ ((visibility ("hidden")));
|
||||
- rtld_ehdr = &__ehdr_start;
|
||||
-#else
|
||||
- rtld_ehdr = (void *) GL(dl_rtld_map).l_map_start;
|
||||
-#endif
|
||||
- assert (rtld_ehdr->e_ehsize == sizeof *rtld_ehdr);
|
||||
- assert (rtld_ehdr->e_phentsize == sizeof (ElfW(Phdr)));
|
||||
-
|
||||
- const ElfW(Phdr) *rtld_phdr = (const void *) rtld_ehdr + rtld_ehdr->e_phoff;
|
||||
-
|
||||
- GL(dl_rtld_map).l_phdr = rtld_phdr;
|
||||
- GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum;
|
||||
-
|
||||
-
|
||||
- /* PT_GNU_RELRO is usually the last phdr. */
|
||||
- size_t cnt = rtld_ehdr->e_phnum;
|
||||
- while (cnt-- > 0)
|
||||
- if (rtld_phdr[cnt].p_type == PT_GNU_RELRO)
|
||||
- {
|
||||
- GL(dl_rtld_map).l_relro_addr = rtld_phdr[cnt].p_vaddr;
|
||||
- GL(dl_rtld_map).l_relro_size = rtld_phdr[cnt].p_memsz;
|
||||
- break;
|
||||
- }
|
||||
+ rtld_setup_phdr ();
|
||||
|
||||
/* Add the dynamic linker to the TLS list if it also uses TLS. */
|
||||
if (GL(dl_rtld_map).l_tls_blocksize != 0)
|
||||
457
SOURCES/glibc-RHEL-104852-2.patch
Normal file
457
SOURCES/glibc-RHEL-104852-2.patch
Normal file
@ -0,0 +1,457 @@
|
||||
commit 20681be149b9eb1b6c1f4246bf4bd801221c86cd
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Aug 1 10:20:23 2025 +0200
|
||||
|
||||
elf: Handle ld.so with LOAD segment gaps in _dl_find_object (bug 31943)
|
||||
|
||||
Detect if ld.so not contiguous and handle that case in _dl_find_object.
|
||||
Set l_find_object_processed even for initially loaded link maps,
|
||||
otherwise dlopen of an initially loaded object adds it to
|
||||
_dlfo_loaded_mappings (where maps are expected to be contiguous),
|
||||
in addition to _dlfo_nodelete_mappings.
|
||||
|
||||
Test elf/tst-link-map-contiguous-ldso iterates over the loader
|
||||
image, reading every word to make sure memory is actually mapped.
|
||||
It only does that if the l_contiguous flag is set for the link map.
|
||||
Otherwise, it finds gaps with mmap and checks that _dl_find_object
|
||||
does not return the ld.so mapping for them.
|
||||
|
||||
The test elf/tst-link-map-contiguous-main does the same thing for
|
||||
the libc.so shared object. This only works if the kernel loaded
|
||||
the main program because the glibc dynamic loader may fill
|
||||
the gaps with PROT_NONE mappings in some cases, making it contiguous,
|
||||
but accesses to individual words may still fault.
|
||||
|
||||
Test elf/tst-link-map-contiguous-libc is again slightly different
|
||||
because the dynamic loader always fills the gaps with PROT_NONE
|
||||
mappings, so a different form of probing has to be used.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
Conflicts:
|
||||
elf/dl-find_object.h
|
||||
(missing SFrame support downstream. Update
|
||||
l_find_object_processed early due to the possible
|
||||
return from the for loop).
|
||||
elf/dl-find_object.c
|
||||
(missing is_rtld_link_map downstream)
|
||||
elf/rtld.c
|
||||
(missing GL(dl_rtld_map) refactoring downstream)
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index b37636c0f865f4e6..190ee83120c498a3 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -519,6 +519,8 @@ tests-internal += \
|
||||
tst-dl-hwcaps_split \
|
||||
tst-dlmopen2 \
|
||||
tst-hash-collision3 \
|
||||
+ tst-link-map-contiguous-ldso \
|
||||
+ tst-link-map-contiguous-libc \
|
||||
tst-ptrguard1 \
|
||||
tst-stackguard1 \
|
||||
tst-tls-surplus \
|
||||
@@ -531,6 +533,10 @@ tests-internal += \
|
||||
tst-dl_find_object tst-dl_find_object-threads \
|
||||
# tests-internal
|
||||
|
||||
+ifeq ($(build-hardcoded-path-in-tests),yes)
|
||||
+tests-internal += tst-link-map-contiguous-main
|
||||
+endif
|
||||
+
|
||||
tests-container += \
|
||||
tst-dlopen-self-container \
|
||||
tst-dlopen-tlsmodid-container \
|
||||
diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c
|
||||
index 62a8a61f6b6032cf..128329aa20ef9ed6 100644
|
||||
--- a/elf/dl-find_object.c
|
||||
+++ b/elf/dl-find_object.c
|
||||
@@ -468,6 +468,37 @@ rtld_hidden_def (__dl_find_object_internal)
|
||||
strong_alias (__dl_find_object_internal, _dl_find_object)
|
||||
#endif
|
||||
|
||||
+/* Subroutine of _dlfo_process_initial to split out noncontigous link
|
||||
+ maps. NODELETE is the number of used _dlfo_nodelete_mappings
|
||||
+ elements. It is incremented as needed, and the new NODELETE value
|
||||
+ is returned. */
|
||||
+static size_t
|
||||
+_dlfo_process_initial_noncontiguous_map (struct link_map *map,
|
||||
+ size_t nodelete)
|
||||
+{
|
||||
+ struct dl_find_object_internal dlfo;
|
||||
+ _dl_find_object_from_map (map, &dlfo);
|
||||
+
|
||||
+ /* PT_LOAD segments for a non-contiguous link map are added to the
|
||||
+ non-closeable mappings. */
|
||||
+ const ElfW(Phdr) *ph = map->l_phdr;
|
||||
+ const ElfW(Phdr) *ph_end = map->l_phdr + map->l_phnum;
|
||||
+ for (; ph < ph_end; ++ph)
|
||||
+ if (ph->p_type == PT_LOAD)
|
||||
+ {
|
||||
+ if (_dlfo_nodelete_mappings != NULL)
|
||||
+ {
|
||||
+ /* Second pass only. */
|
||||
+ _dlfo_nodelete_mappings[nodelete] = dlfo;
|
||||
+ ElfW(Addr) start = ph->p_vaddr + map->l_addr;
|
||||
+ _dlfo_nodelete_mappings[nodelete].map_start = start;
|
||||
+ _dlfo_nodelete_mappings[nodelete].map_end = start + ph->p_memsz;
|
||||
+ }
|
||||
+ ++nodelete;
|
||||
+ }
|
||||
+ return nodelete;
|
||||
+}
|
||||
+
|
||||
/* _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
|
||||
@@ -479,29 +510,8 @@ _dlfo_process_initial (void)
|
||||
|
||||
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;
|
||||
- }
|
||||
- }
|
||||
+ /* Contiguous case already handled in _dl_find_object_init. */
|
||||
+ nodelete = _dlfo_process_initial_noncontiguous_map (main_map, nodelete);
|
||||
|
||||
size_t loaded = 0;
|
||||
for (Lmid_t ns = 0; ns < GL(dl_nns); ++ns)
|
||||
@@ -513,11 +523,22 @@ _dlfo_process_initial (void)
|
||||
/* 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;
|
||||
+ /* The kernel may have loaded ld.so with gaps. */
|
||||
+ if (!l->l_contiguous
|
||||
+#ifdef SHARED
|
||||
+ && l == &GL(dl_rtld_map)
|
||||
+#endif
|
||||
+ )
|
||||
+ nodelete
|
||||
+ = _dlfo_process_initial_noncontiguous_map (l, nodelete);
|
||||
+ else
|
||||
+ {
|
||||
+ 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)
|
||||
{
|
||||
@@ -767,7 +788,6 @@ _dl_find_object_update_1 (struct link_map **loaded, size_t count)
|
||||
/* 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;
|
||||
}
|
||||
|
||||
diff --git a/elf/dl-find_object.h b/elf/dl-find_object.h
|
||||
index 11569efc9b7daf9c..fae25747edc6dca0 100644
|
||||
--- a/elf/dl-find_object.h
|
||||
+++ b/elf/dl-find_object.h
|
||||
@@ -87,7 +87,7 @@ _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 using relaxed MO stores. */
|
||||
+ *RESULT using relaxed MO stores. Set L->l_find_object_processed. */
|
||||
static void __attribute__ ((unused))
|
||||
_dl_find_object_from_map (struct link_map *l,
|
||||
struct dl_find_object_internal *result)
|
||||
@@ -100,6 +100,8 @@ _dl_find_object_from_map (struct link_map *l,
|
||||
atomic_store_relaxed (&result->eh_dbase, (void *) l->l_info[DT_PLTGOT]);
|
||||
#endif
|
||||
|
||||
+ l->l_find_object_processed = 1;
|
||||
+
|
||||
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)
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index a9073d4e14b07410..425003e6c8e452ab 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -1308,7 +1308,7 @@ rtld_setup_main_map (struct link_map *main_map)
|
||||
|
||||
/* Set up the program header information for the dynamic linker
|
||||
itself. It can be accessed via _r_debug and dl_iterate_phdr
|
||||
- callbacks. */
|
||||
+ callbacks, and it is used by _dl_find_object. */
|
||||
static void
|
||||
rtld_setup_phdr (void)
|
||||
{
|
||||
@@ -1334,6 +1334,29 @@ rtld_setup_phdr (void)
|
||||
GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum;
|
||||
|
||||
|
||||
+ GL(dl_rtld_map).l_contiguous = 1;
|
||||
+ /* The linker may not have produced a contiguous object. The kernel
|
||||
+ will load the object with actual gaps (unlike the glibc loader
|
||||
+ for shared objects, which always produces a contiguous mapping).
|
||||
+ See similar logic in rtld_setup_main_map above. */
|
||||
+ {
|
||||
+ ElfW(Addr) expected_load_address = 0;
|
||||
+ for (const ElfW(Phdr) *ph = rtld_phdr; ph < &rtld_phdr[rtld_ehdr->e_phnum];
|
||||
+ ++ph)
|
||||
+ if (ph->p_type == PT_LOAD)
|
||||
+ {
|
||||
+ ElfW(Addr) mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1);
|
||||
+ if (GL(dl_rtld_map).l_contiguous && expected_load_address != 0
|
||||
+ && expected_load_address != mapstart)
|
||||
+ GL(dl_rtld_map).l_contiguous = 0;
|
||||
+ ElfW(Addr) allocend = ph->p_vaddr + ph->p_memsz;
|
||||
+ /* The next expected address is the page following this load
|
||||
+ segment. */
|
||||
+ expected_load_address = ((allocend + GLRO(dl_pagesize) - 1)
|
||||
+ & ~(GLRO(dl_pagesize) - 1));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/* PT_GNU_RELRO is usually the last phdr. */
|
||||
size_t cnt = rtld_ehdr->e_phnum;
|
||||
while (cnt-- > 0)
|
||||
diff --git a/elf/tst-link-map-contiguous-ldso.c b/elf/tst-link-map-contiguous-ldso.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..04de808bb234fe38
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-link-map-contiguous-ldso.c
|
||||
@@ -0,0 +1,98 @@
|
||||
+/* Check that _dl_find_object behavior matches up with gaps.
|
||||
+ Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <dlfcn.h>
|
||||
+#include <gnu/lib-names.h>
|
||||
+#include <link.h>
|
||||
+#include <stdbool.h>
|
||||
+#include <stdio.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+#include <support/xunistd.h>
|
||||
+#include <sys/mman.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ struct link_map *l = xdlopen (LD_SO, RTLD_NOW);
|
||||
+ if (!l->l_contiguous)
|
||||
+ {
|
||||
+ puts ("info: ld.so link map is not contiguous");
|
||||
+
|
||||
+ /* Try to find holes by probing with mmap. */
|
||||
+ int pagesize = getpagesize ();
|
||||
+ bool gap_found = false;
|
||||
+ ElfW(Addr) addr = l->l_map_start;
|
||||
+ TEST_COMPARE (addr % pagesize, 0);
|
||||
+ while (addr < l->l_map_end)
|
||||
+ {
|
||||
+ void *expected = (void *) addr;
|
||||
+ void *ptr = xmmap (expected, 1, PROT_READ | PROT_WRITE,
|
||||
+ MAP_PRIVATE | MAP_ANONYMOUS, -1);
|
||||
+ struct dl_find_object dlfo;
|
||||
+ int dlfo_ret = _dl_find_object (expected, &dlfo);
|
||||
+ if (ptr == expected)
|
||||
+ {
|
||||
+ if (dlfo_ret < 0)
|
||||
+ {
|
||||
+ TEST_COMPARE (dlfo_ret, -1);
|
||||
+ printf ("info: hole without mapping data found at %p\n", ptr);
|
||||
+ }
|
||||
+ else
|
||||
+ FAIL ("object \"%s\" found in gap at %p",
|
||||
+ dlfo.dlfo_link_map->l_name, ptr);
|
||||
+ gap_found = true;
|
||||
+ }
|
||||
+ else if (dlfo_ret == 0)
|
||||
+ {
|
||||
+ if ((void *) dlfo.dlfo_link_map != (void *) l)
|
||||
+ {
|
||||
+ printf ("info: object \"%s\" found at %p\n",
|
||||
+ dlfo.dlfo_link_map->l_name, ptr);
|
||||
+ gap_found = true;
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ TEST_COMPARE (dlfo_ret, -1);
|
||||
+ xmunmap (ptr, 1);
|
||||
+ addr += pagesize;
|
||||
+ }
|
||||
+ if (!gap_found)
|
||||
+ FAIL ("no ld.so gap found");
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ puts ("info: ld.so link map is contiguous");
|
||||
+
|
||||
+ /* Assert that ld.so is truly contiguous in memory. */
|
||||
+ volatile long int *p = (volatile long int *) l->l_map_start;
|
||||
+ volatile long int *end = (volatile long int *) l->l_map_end;
|
||||
+ while (p < end)
|
||||
+ {
|
||||
+ *p;
|
||||
+ ++p;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ xdlclose (l);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
diff --git a/elf/tst-link-map-contiguous-libc.c b/elf/tst-link-map-contiguous-libc.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..eb5728c765ac3cfb
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-link-map-contiguous-libc.c
|
||||
@@ -0,0 +1,57 @@
|
||||
+/* Check that the entire libc.so program image is readable if contiguous.
|
||||
+ Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <gnu/lib-names.h>
|
||||
+#include <link.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+#include <support/xunistd.h>
|
||||
+#include <sys/mman.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ struct link_map *l = xdlopen (LIBC_SO, RTLD_NOW);
|
||||
+
|
||||
+ /* The dynamic loader fills holes with PROT_NONE mappings. */
|
||||
+ if (!l->l_contiguous)
|
||||
+ FAIL_EXIT1 ("libc.so link map is not contiguous");
|
||||
+
|
||||
+ /* Direct probing does not work because not everything is readable
|
||||
+ due to PROT_NONE mappings. */
|
||||
+ int pagesize = getpagesize ();
|
||||
+ ElfW(Addr) addr = l->l_map_start;
|
||||
+ TEST_COMPARE (addr % pagesize, 0);
|
||||
+ while (addr < l->l_map_end)
|
||||
+ {
|
||||
+ void *expected = (void *) addr;
|
||||
+ void *ptr = xmmap (expected, 1, PROT_READ | PROT_WRITE,
|
||||
+ MAP_PRIVATE | MAP_ANONYMOUS, -1);
|
||||
+ if (ptr == expected)
|
||||
+ FAIL ("hole in libc.so memory image after %lu bytes",
|
||||
+ (unsigned long int) (addr - l->l_map_start));
|
||||
+ xmunmap (ptr, 1);
|
||||
+ addr += pagesize;
|
||||
+ }
|
||||
+
|
||||
+ xdlclose (l);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+#include <support/test-driver.c>
|
||||
diff --git a/elf/tst-link-map-contiguous-main.c b/elf/tst-link-map-contiguous-main.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..2d1a054f0fbb0855
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-link-map-contiguous-main.c
|
||||
@@ -0,0 +1,45 @@
|
||||
+/* Check that the entire main program image is readable if contiguous.
|
||||
+ Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <link.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ struct link_map *l = xdlopen ("", RTLD_NOW);
|
||||
+ if (!l->l_contiguous)
|
||||
+ FAIL_UNSUPPORTED ("main link map is not contiguous");
|
||||
+
|
||||
+ /* This check only works if the kernel loaded the main program. The
|
||||
+ dynamic loader replaces gaps with PROT_NONE mappings, resulting
|
||||
+ in faults. */
|
||||
+ volatile long int *p = (volatile long int *) l->l_map_start;
|
||||
+ volatile long int *end = (volatile long int *) l->l_map_end;
|
||||
+ while (p < end)
|
||||
+ {
|
||||
+ *p;
|
||||
+ ++p;
|
||||
+ }
|
||||
+
|
||||
+ xdlclose (l);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+#include <support/test-driver.c>
|
||||
@ -12,21 +12,22 @@ Date: Mon Jul 21 21:43:49 2025 +0200
|
||||
Reviewed-by: Andreas K. Huettel <dilfridge@gentoo.org>
|
||||
|
||||
Conflicts:
|
||||
posix/Makefile
|
||||
(tests list not reformatted downstream)
|
||||
posix/Makefile (New test added)
|
||||
|
||||
diff --git a/posix/Makefile b/posix/Makefile
|
||||
index 4c32a088a73723c7..ef7a9ca31d9ee136 100644
|
||||
index 7b70b4a736bc1215..562e8cb85fdb6f43 100644
|
||||
--- a/posix/Makefile
|
||||
+++ b/posix/Makefile
|
||||
@@ -111,6 +111,7 @@ tests := test-errno tstgetopt testfnm runtests runptests \
|
||||
@@ -112,7 +112,8 @@ tests := test-errno tstgetopt testfnm runtests runptests \
|
||||
tst-sched_getaffinity \
|
||||
tst-cpuset-dynamic \
|
||||
tst-cpuset-static \
|
||||
+ tst-regcomp-bracket-free \
|
||||
|
||||
- tst-spawn6
|
||||
+ tst-spawn6 \
|
||||
+ tst-regcomp-bracket-free
|
||||
|
||||
# Test for the glob symbol version that was replaced in glibc 2.27.
|
||||
ifeq ($(have-GLIBC_2.26)$(build-shared),yesyes)
|
||||
diff --git a/posix/regcomp.c b/posix/regcomp.c
|
||||
index 887e5b50684e22f5..005e6459bbe8bd55 100644
|
||||
--- a/posix/regcomp.c
|
||||
33
SOURCES/glibc-RHEL-105957.patch
Normal file
33
SOURCES/glibc-RHEL-105957.patch
Normal file
@ -0,0 +1,33 @@
|
||||
Downstream patch only.
|
||||
|
||||
Revert changes made to elf/dl-readonly-area.c compared to
|
||||
ed6a68bac7cd056abda9008019c71b167f0362dc since `_dl_find_object` has
|
||||
been backported.
|
||||
|
||||
Note: `_dl_find_object` isn't directly made available internally
|
||||
downstream, we can use `__dl_find_object_internal` instead.
|
||||
|
||||
diff --git a/elf/dl-readonly-area.c b/elf/dl-readonly-area.c
|
||||
index 570b99b11527db13..3b39eed06a379ce3 100644
|
||||
--- a/elf/dl-readonly-area.c
|
||||
+++ b/elf/dl-readonly-area.c
|
||||
@@ -40,16 +40,11 @@ check_relro (const struct link_map *l, uintptr_t start, uintptr_t end)
|
||||
enum dl_readonly_area_error_type
|
||||
_dl_readonly_area (const void *ptr, size_t size)
|
||||
{
|
||||
- /* Protect against concurrent loads and unloads. */
|
||||
- __rtld_lock_lock_recursive (GL(dl_load_lock));
|
||||
-
|
||||
- const struct link_map *l = _dl_find_dso_for_object ((ElfW(Addr)) ptr);
|
||||
-
|
||||
- __rtld_lock_unlock_recursive (GL(dl_load_lock));
|
||||
-
|
||||
- if (l == NULL)
|
||||
+ struct dl_find_object dlfo;
|
||||
+ if (__dl_find_object_internal ((void *)ptr, &dlfo) != 0)
|
||||
return dl_readonly_area_not_found;
|
||||
|
||||
+ const struct link_map *l = dlfo.dlfo_link_map;
|
||||
uintptr_t ptr_start = (uintptr_t) ptr;
|
||||
uintptr_t ptr_end = ptr_start + size;
|
||||
|
||||
80
SOURCES/glibc-RHEL-105965.patch
Normal file
80
SOURCES/glibc-RHEL-105965.patch
Normal file
@ -0,0 +1,80 @@
|
||||
commit 620f0730f311635cd0e175a3ae4d0fc700c76366
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Mon Jul 28 14:16:52 2025 +0200
|
||||
|
||||
elf: Compile _dl_debug_state separately (bug 33224)
|
||||
|
||||
This ensures that the compiler will not inline it, so that
|
||||
debuggers which do not use the Systemtap probes can reliably
|
||||
set a breakpoint on it.
|
||||
|
||||
Reviewed-by: Andreas K. Huettel <dilfridge@gentoo.org>
|
||||
Tested-by: Andreas K. Huettel <dilfridge@gentoo.org>
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index b181150b36773d24..b37636c0f865f4e6 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -56,6 +56,7 @@ dl-routines = \
|
||||
dl-close \
|
||||
dl-debug \
|
||||
dl-debug-symbols \
|
||||
+ dl-debug_state \
|
||||
dl-deps \
|
||||
dl-exception \
|
||||
dl-execstack \
|
||||
diff --git a/elf/dl-debug.c b/elf/dl-debug.c
|
||||
index 4388a04cdf828898..8c1472a84ebfefe7 100644
|
||||
--- a/elf/dl-debug.c
|
||||
+++ b/elf/dl-debug.c
|
||||
@@ -167,14 +167,3 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
|
||||
|
||||
return &r->base;
|
||||
}
|
||||
-
|
||||
-
|
||||
-/* This function exists solely to have a breakpoint set on it by the
|
||||
- debugger. The debugger is supposed to find this function's address by
|
||||
- examining the r_brk member of struct r_debug, but GDB 4.15 in fact looks
|
||||
- for this particular symbol name in the PT_INTERP file. */
|
||||
-void
|
||||
-_dl_debug_state (void)
|
||||
-{
|
||||
-}
|
||||
-rtld_hidden_def (_dl_debug_state)
|
||||
diff --git a/elf/dl-debug_state.c b/elf/dl-debug_state.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..40c134a49e2455f3
|
||||
--- /dev/null
|
||||
+++ b/elf/dl-debug_state.c
|
||||
@@ -0,0 +1,30 @@
|
||||
+/* Debugger hook called after dynamic linker updates.
|
||||
+ Copyright (C) 1996-2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <ldsodefs.h>
|
||||
+
|
||||
+/* This function exists solely to have a breakpoint set on it by the
|
||||
+ debugger. The debugger is supposed to find this function's address by
|
||||
+ examining the r_brk member of struct r_debug, but GDB 4.15 in fact looks
|
||||
+ for this particular symbol name in the PT_INTERP file. Therefore,
|
||||
+ this function must not be inlined. */
|
||||
+void
|
||||
+_dl_debug_state (void)
|
||||
+{
|
||||
+}
|
||||
+rtld_hidden_def (_dl_debug_state)
|
||||
39
SOURCES/glibc-RHEL-107518.patch
Normal file
39
SOURCES/glibc-RHEL-107518.patch
Normal file
@ -0,0 +1,39 @@
|
||||
commit e5363e6f460c2d58809bf10fc96d70fd1ef8b5b2
|
||||
Author: Jens Remus <jremus@linux.ibm.com>
|
||||
Date: Fri Jul 25 15:40:03 2025 +0200
|
||||
|
||||
Use TLS initial-exec model for __libc_tsd_CTYPE_* thread variables [BZ #33234]
|
||||
|
||||
Commit 10a66a8e421b ("Remove <libc-tsd.h>") removed the TLS initial-exec
|
||||
(IE) model attribute from the __libc_tsd_CTYPE_* thread variable declarations
|
||||
and definitions. Commit a894f04d8776 ("Optimize __libc_tsd_* thread
|
||||
variable access") restored it on declarations.
|
||||
|
||||
Restore the TLS initial-exec model attribute on __libc_tsd_CTYPE_* thread
|
||||
variable definitions.
|
||||
|
||||
This resolves test tst-locale1 failure on s390 32-bit, when using a
|
||||
GNU linker without the fix from GNU binutils commit aefebe82dc89
|
||||
("IBM zSystems: Fix offset relative to static TLS").
|
||||
|
||||
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
diff --git a/ctype/ctype-info.c b/ctype/ctype-info.c
|
||||
index e0752b4a1af6df15..315cedcfaa357ead 100644
|
||||
--- a/ctype/ctype-info.c
|
||||
+++ b/ctype/ctype-info.c
|
||||
@@ -24,11 +24,11 @@
|
||||
__ctype_init before user code runs, but this does not happen for
|
||||
threads in secondary namespaces. With the initializers, secondary
|
||||
namespaces at least get locale data from the C locale. */
|
||||
-__thread const uint16_t * __libc_tsd_CTYPE_B
|
||||
+__thread const uint16_t * __libc_tsd_CTYPE_B attribute_tls_model_ie
|
||||
= (const uint16_t *) _nl_C_LC_CTYPE_class + 128;
|
||||
-__thread const int32_t * __libc_tsd_CTYPE_TOLOWER
|
||||
+__thread const int32_t * __libc_tsd_CTYPE_TOLOWER attribute_tls_model_ie
|
||||
= (const int32_t *) _nl_C_LC_CTYPE_tolower + 128;
|
||||
-__thread const int32_t * __libc_tsd_CTYPE_TOUPPER
|
||||
+__thread const int32_t * __libc_tsd_CTYPE_TOUPPER attribute_tls_model_ie
|
||||
= (const int32_t *) _nl_C_LC_CTYPE_toupper + 128;
|
||||
|
||||
|
||||
35
SOURCES/glibc-RHEL-107564.patch
Normal file
35
SOURCES/glibc-RHEL-107564.patch
Normal file
@ -0,0 +1,35 @@
|
||||
Downstream-only patch correcting the GLIBC_PRIVATE ABI preservation
|
||||
change in glibc-RHEL-93320-19.patch.
|
||||
|
||||
This patch adds a symbol alias that supplies the expect external
|
||||
name _dl_find_object within libc.a, where the forwarder in
|
||||
elf/libc-dl_find_object.c no longer exists after the changes
|
||||
in glibc-RHEL-93320-19.patch. It also brings back the
|
||||
tst-dl_find_object-static static linking test.
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index ba11f3a8b81e7218..7b75afda32bcd579 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -281,6 +281,7 @@ tests-static-normal := \
|
||||
# tests-static-normal
|
||||
|
||||
tests-static-internal := \
|
||||
+ tst-dl_find_object-static \
|
||||
tst-ptrguard1-static \
|
||||
tst-stackguard1-static \
|
||||
tst-tls1-static \
|
||||
diff --git a/elf/dl-find_object.c b/elf/dl-find_object.c
|
||||
index 99797580066cdce8..62a8a61f6b6032cf 100644
|
||||
--- a/elf/dl-find_object.c
|
||||
+++ b/elf/dl-find_object.c
|
||||
@@ -464,6 +464,9 @@ __dl_find_object_internal (void *pc1, struct dl_find_object *result)
|
||||
} /* Transaction retry loop. */
|
||||
}
|
||||
rtld_hidden_def (__dl_find_object_internal)
|
||||
+#ifndef SHARED
|
||||
+strong_alias (__dl_find_object_internal, _dl_find_object)
|
||||
+#endif
|
||||
|
||||
/* _dlfo_process_initial is called twice. First to compute the array
|
||||
sizes from the initial loaded mappings. Second to fill in the
|
||||
18
SOURCES/glibc-RHEL-108220.patch
Normal file
18
SOURCES/glibc-RHEL-108220.patch
Normal file
@ -0,0 +1,18 @@
|
||||
Downstream-only patch to remove duplicate Makefile target entry for
|
||||
tst-dlmopen1mod.
|
||||
|
||||
Conflict resolution in glibc-RHEL-93320-4.patch led to the duplicate
|
||||
tst-dlmopen1mod entry. Therefore remove it.
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index 190ee83120c498a3..e8587b10c1a8bedd 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -811,7 +811,6 @@ modules-names = \
|
||||
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 \
|
||||
36
SOURCES/glibc-RHEL-108221.patch
Normal file
36
SOURCES/glibc-RHEL-108221.patch
Normal file
@ -0,0 +1,36 @@
|
||||
In glibc-RHEL-93320-9.patch, the elf/tst-dl_find_object test was
|
||||
added. It was later disabled in glibc-RHEL-93320-19.patch,
|
||||
as noted in the patch description. This was missed when the
|
||||
patch was re-added in glibc-RHEL-107564.patch. The test remains
|
||||
valuable because we do not test _dl_find_object in libc.a elsewhere
|
||||
in the glibc build, so this patch disables just the failing subtest,
|
||||
and puts an explanation directly into the test.
|
||||
|
||||
diff --git a/elf/tst-dl_find_object.c b/elf/tst-dl_find_object.c
|
||||
index d8c217545d116453..6bfda7bd23a34c0d 100644
|
||||
--- a/elf/tst-dl_find_object.c
|
||||
+++ b/elf/tst-dl_find_object.c
|
||||
@@ -231,6 +231,7 @@ do_test (void)
|
||||
check (map_start, &expected, __LINE__);
|
||||
check (map_end, &expected, __LINE__);
|
||||
|
||||
+#ifndef FOR_STATIC
|
||||
/* Check that _dl_find_object works from a shared object (mostly for
|
||||
static dlopen). */
|
||||
__typeof (_dl_find_object) *find_object
|
||||
@@ -238,6 +239,15 @@ do_test (void)
|
||||
struct dl_find_object actual;
|
||||
TEST_COMPARE (find_object (&main_program_data, &actual), 0);
|
||||
check (&main_program_data, &actual, __LINE__); /* Reversed check. */
|
||||
+#else
|
||||
+ /* Downstream, _dl_find_object does not work after static dlopen
|
||||
+ because the ld.so copy loaded as part of static dlopen is not
|
||||
+ initialized. Upstream, we redirect _dl_find_object to the
|
||||
+ statically version from the main program by patching a function
|
||||
+ pointer in _rtld_global_ro. Downstream, we have not changed the
|
||||
+ layout of _rtld_global_ro, so this patching is missing. */
|
||||
+ printf ("info: skipping dlopen-based test for static build\n");
|
||||
+#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -13,10 +13,10 @@ Date: Fri Sep 12 21:33:34 2025 +0200
|
||||
Reviewed-by: Collin Funk <collin.funk1@gmail.com>
|
||||
|
||||
diff --git a/nss/getXXbyYY_r.c b/nss/getXXbyYY_r.c
|
||||
index c344528f72a2dcab..30f913876eb81594 100644
|
||||
index eae6c3480e..2b0735fb6a 100644
|
||||
--- a/nss/getXXbyYY_r.c
|
||||
+++ b/nss/getXXbyYY_r.c
|
||||
@@ -158,19 +158,15 @@ __merge_einval (LOOKUP_TYPE *a,
|
||||
@@ -157,19 +157,15 @@ __merge_einval (LOOKUP_TYPE *a,
|
||||
|
||||
#define CHECK_MERGE(err, status) \
|
||||
({ \
|
||||
226
SOURCES/glibc-RHEL-24168-1.patch
Normal file
226
SOURCES/glibc-RHEL-24168-1.patch
Normal file
@ -0,0 +1,226 @@
|
||||
Downstream-only patch to add arc4random to support/ for use in qsort
|
||||
testing.
|
||||
|
||||
The arc4random implementation is up-to-date with upstream commit
|
||||
2642002380aafb71a1d3b569b6d7ebeab3284816, with minor changes to keep
|
||||
everything self-contained within support infrastructure. Unlike the
|
||||
upstream version, this implementation is a cancellation point.
|
||||
|
||||
diff --git a/support/Makefile b/support/Makefile
|
||||
index d6d03c2ed3af3e6d..bffcb06d7185d674 100644
|
||||
--- a/support/Makefile
|
||||
+++ b/support/Makefile
|
||||
@@ -41,6 +41,8 @@ libsupport-routines = \
|
||||
resolv_response_context_free \
|
||||
resolv_test \
|
||||
set_fortify_handler \
|
||||
+ support-arc4random \
|
||||
+ support-arc4random_uniform \
|
||||
support-open-dev-null-range \
|
||||
support_become_root \
|
||||
support_can_chroot \
|
||||
diff --git a/support/support-arc4random.c b/support/support-arc4random.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..c4462b098c68cef5
|
||||
--- /dev/null
|
||||
+++ b/support/support-arc4random.c
|
||||
@@ -0,0 +1,99 @@
|
||||
+/* Pseudo Random Number Generator
|
||||
+ Copyright (C) 2022-2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <errno.h>
|
||||
+#include <not-cancel.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <sys/mman.h>
|
||||
+#include <sys/param.h>
|
||||
+#include <sys/random.h>
|
||||
+#include <poll.h>
|
||||
+
|
||||
+static void
|
||||
+arc4random_getrandom_failure (void)
|
||||
+{
|
||||
+ __libc_fatal ("Fatal glibc error: cannot get entropy for arc4random\n");
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+arc4random_buf (void *p, size_t n)
|
||||
+{
|
||||
+ static int seen_initialized;
|
||||
+ ssize_t l;
|
||||
+ int fd;
|
||||
+
|
||||
+ if (n == 0)
|
||||
+ return;
|
||||
+
|
||||
+ for (;;)
|
||||
+ {
|
||||
+ l = TEMP_FAILURE_RETRY (getrandom (p, n, 0));
|
||||
+ if (l > 0)
|
||||
+ {
|
||||
+ if ((size_t) l == n)
|
||||
+ return; /* Done reading, success. */
|
||||
+ p = (uint8_t *) p + l;
|
||||
+ n -= l;
|
||||
+ continue; /* Interrupted by a signal; keep going. */
|
||||
+ }
|
||||
+ else if (l < 0 && errno == ENOSYS)
|
||||
+ break; /* No syscall, so fallback to /dev/urandom. */
|
||||
+ arc4random_getrandom_failure ();
|
||||
+ }
|
||||
+
|
||||
+ if (atomic_load_relaxed (&seen_initialized) == 0)
|
||||
+ {
|
||||
+ /* Poll /dev/random as an approximation of RNG initialization. */
|
||||
+ struct pollfd pfd = { .events = POLLIN };
|
||||
+ pfd.fd = TEMP_FAILURE_RETRY (
|
||||
+ __open64_nocancel ("/dev/random", O_RDONLY | O_CLOEXEC | O_NOCTTY));
|
||||
+ if (pfd.fd < 0)
|
||||
+ arc4random_getrandom_failure ();
|
||||
+ if (TEMP_FAILURE_RETRY (poll (&pfd, 1, -1)) < 0)
|
||||
+ arc4random_getrandom_failure ();
|
||||
+ if (__close_nocancel (pfd.fd) < 0)
|
||||
+ arc4random_getrandom_failure ();
|
||||
+ atomic_store_relaxed (&seen_initialized, 1);
|
||||
+ }
|
||||
+
|
||||
+ fd = TEMP_FAILURE_RETRY (
|
||||
+ __open64_nocancel ("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY));
|
||||
+ if (fd < 0)
|
||||
+ arc4random_getrandom_failure ();
|
||||
+ for (;;)
|
||||
+ {
|
||||
+ l = TEMP_FAILURE_RETRY (__read_nocancel (fd, p, n));
|
||||
+ if (l <= 0)
|
||||
+ arc4random_getrandom_failure ();
|
||||
+ if ((size_t) l == n)
|
||||
+ break; /* Done reading, success. */
|
||||
+ p = (uint8_t *) p + l;
|
||||
+ n -= l;
|
||||
+ }
|
||||
+ if (__close_nocancel (fd) < 0)
|
||||
+ arc4random_getrandom_failure ();
|
||||
+}
|
||||
+
|
||||
+uint32_t
|
||||
+arc4random (void)
|
||||
+{
|
||||
+ uint32_t r;
|
||||
+ arc4random_buf (&r, sizeof (r));
|
||||
+ return r;
|
||||
+}
|
||||
diff --git a/support/support-arc4random_uniform.c b/support/support-arc4random_uniform.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..20108e7409cca81b
|
||||
--- /dev/null
|
||||
+++ b/support/support-arc4random_uniform.c
|
||||
@@ -0,0 +1,70 @@
|
||||
+/* Random pseudo generator number which returns a single 32 bit value
|
||||
+ uniformly distributed but with an upper_bound.
|
||||
+ Copyright (C) 2022-2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdlib.h>
|
||||
+#include <sys/param.h>
|
||||
+#include <support/support.h>
|
||||
+
|
||||
+/* Return a uniformly distributed random number less than N. The algorithm
|
||||
+ calculates a mask being the lowest power of two bounding the upper bound
|
||||
+ N, successively queries new random values, and rejects values outside of
|
||||
+ the request range.
|
||||
+
|
||||
+ For reject values, it also tries if the remaining entropy could fit on
|
||||
+ the asked range after range adjustment.
|
||||
+
|
||||
+ The algorithm avoids modulo and divide operations, which might be costly
|
||||
+ depending on the architecture. */
|
||||
+uint32_t
|
||||
+arc4random_uniform (uint32_t n)
|
||||
+{
|
||||
+ if (n <= 1)
|
||||
+ /* There is no valid return value for a zero limit, and 0 is the
|
||||
+ only possible result for limit 1. */
|
||||
+ return 0;
|
||||
+
|
||||
+ /* Powers of two are easy. */
|
||||
+ if (powerof2 (n))
|
||||
+ return arc4random () & (n - 1);
|
||||
+
|
||||
+ /* mask is the smallest power of 2 minus 1 number larger than n. */
|
||||
+ int z = __builtin_clz (n);
|
||||
+ uint32_t mask = ~UINT32_C(0) >> z;
|
||||
+ int bits = CHAR_BIT * sizeof (uint32_t) - z;
|
||||
+
|
||||
+ while (1)
|
||||
+ {
|
||||
+ uint32_t value = arc4random ();
|
||||
+
|
||||
+ /* Return if the lower power of 2 minus 1 satisfy the condition. */
|
||||
+ uint32_t r = value & mask;
|
||||
+ if (r < n)
|
||||
+ return r;
|
||||
+
|
||||
+ /* Otherwise check if remaining bits of entropy provides fits in the
|
||||
+ bound. */
|
||||
+ for (int bits_left = z; bits_left >= bits; bits_left -= bits)
|
||||
+ {
|
||||
+ value >>= bits;
|
||||
+ r = value & mask;
|
||||
+ if (r < n)
|
||||
+ return r;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/support/support.h b/support/support.h
|
||||
index b69f588e2edce6be..ed7862daf9e4120a 100644
|
||||
--- a/support/support.h
|
||||
+++ b/support/support.h
|
||||
@@ -220,6 +220,19 @@ void support_stack_free (struct support_stack *stack);
|
||||
The returned value is the lowest file descriptor number. */
|
||||
int support_open_dev_null_range (int num, int flags, mode_t mode);
|
||||
|
||||
+/* Return a random integer between zero and 2**32-1 (inclusive). */
|
||||
+extern uint32_t arc4random (void)
|
||||
+ __THROW __wur;
|
||||
+
|
||||
+/* Fill the buffer with random data. */
|
||||
+extern void arc4random_buf (void *__buf, size_t __size)
|
||||
+ __THROW __nonnull ((1));
|
||||
+
|
||||
+/* Return a random number between zero (inclusive) and the specified
|
||||
+ limit (exclusive). */
|
||||
+extern uint32_t arc4random_uniform (__uint32_t __upper_bound)
|
||||
+ __THROW __wur;
|
||||
+
|
||||
__END_DECLS
|
||||
|
||||
#endif /* SUPPORT_H */
|
||||
43
SOURCES/glibc-RHEL-24168-10.patch
Normal file
43
SOURCES/glibc-RHEL-24168-10.patch
Normal file
@ -0,0 +1,43 @@
|
||||
commit f8cfb6836e8d91bb789b2e7fd65338d6f5bd459c
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Wed Nov 8 15:18:02 2023 +0100
|
||||
|
||||
stdlib: Avoid element self-comparisons in qsort
|
||||
|
||||
This improves compatibility with applications which assume that qsort
|
||||
does not invoke the comparison function with equal pointer arguments.
|
||||
|
||||
The newly introduced branches should be predictable, as leading to a
|
||||
call to the comparison function. If the prediction fails, we avoid
|
||||
calling the function.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index cb1619aa0ae7de72..2ee39e2c492f792e 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -137,7 +137,7 @@ siftdown (void *base, size_t size, size_t k, size_t n,
|
||||
if (j < n && cmp (base + (j * size), base + ((j + 1) * size), arg) < 0)
|
||||
j++;
|
||||
|
||||
- if (cmp (base + (k * size), base + (j * size), arg) >= 0)
|
||||
+ if (j == k || cmp (base + (k * size), base + (j * size), arg) >= 0)
|
||||
break;
|
||||
|
||||
do_swap (base + (size * j), base + (k * size), size, swap_type);
|
||||
@@ -333,10 +333,12 @@ __qsort_r (void *const pbase, size_t total_elems, size_t size,
|
||||
that this algorithm runs much faster than others. */
|
||||
do
|
||||
{
|
||||
- while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
|
||||
+ while (left_ptr != mid
|
||||
+ && (*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
|
||||
left_ptr += size;
|
||||
|
||||
- while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
|
||||
+ while (right_ptr != mid
|
||||
+ && (*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
|
||||
right_ptr -= size;
|
||||
|
||||
if (left_ptr < right_ptr)
|
||||
25
SOURCES/glibc-RHEL-24168-11.patch
Normal file
25
SOURCES/glibc-RHEL-24168-11.patch
Normal file
@ -0,0 +1,25 @@
|
||||
commit e4d8117b82065dc72e8df80097360e7c05a349b9
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Tue Nov 21 16:45:35 2023 +0100
|
||||
|
||||
stdlib: Avoid another self-comparison in qsort
|
||||
|
||||
In the insertion phase, we could run off the start of the array if the
|
||||
comparison function never runs zero. In that case, it never finds the
|
||||
initial element that terminates the iteration.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index 2ee39e2c492f792e..0d5f8b92e8072965 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -218,7 +218,7 @@ insertion_sort_qsort_partitions (void *const pbase, size_t total_elems,
|
||||
while ((run_ptr += size) <= end_ptr)
|
||||
{
|
||||
tmp_ptr = run_ptr - size;
|
||||
- while (cmp (run_ptr, tmp_ptr, arg) < 0)
|
||||
+ while (run_ptr != tmp_ptr && cmp (run_ptr, tmp_ptr, arg) < 0)
|
||||
tmp_ptr -= size;
|
||||
|
||||
tmp_ptr += size;
|
||||
273
SOURCES/glibc-RHEL-24168-12.patch
Normal file
273
SOURCES/glibc-RHEL-24168-12.patch
Normal file
@ -0,0 +1,273 @@
|
||||
commit 55364e1f7dfab372f0710513c4d1c967c4965f71
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Tue Nov 21 16:45:35 2023 +0100
|
||||
|
||||
stdlib: Handle various corner cases in the fallback heapsort for qsort
|
||||
|
||||
The previous implementation did not consistently apply the rule that
|
||||
the child nodes of node K are at 2 * K + 1 and 2 * K + 2, or
|
||||
that the parent node is at (K - 1) / 2.
|
||||
|
||||
Add an internal test that targets the heapsort implementation
|
||||
directly.
|
||||
|
||||
Reported-by: Stepan Golosunov <stepan@golosunov.pp.ru>
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/stdlib/Makefile b/stdlib/Makefile
|
||||
index 4039e5395eeea2b0..ee005ce8caa48abe 100644
|
||||
--- a/stdlib/Makefile
|
||||
+++ b/stdlib/Makefile
|
||||
@@ -254,6 +254,7 @@ tests := \
|
||||
# tests
|
||||
|
||||
tests-internal := \
|
||||
+ tst-qsort4 \
|
||||
tst-strtod1i \
|
||||
tst-strtod3 \
|
||||
tst-strtod4 \
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index 0d5f8b92e8072965..b207c12d2f0a38cc 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -126,29 +126,44 @@ pop (stack_node *top, char **lo, char **hi, size_t *depth)
|
||||
return top;
|
||||
}
|
||||
|
||||
-/* NB: N is inclusive bound for BASE. */
|
||||
+/* Establish the heap condition at index K, that is, the key at K will
|
||||
+ not be less than either of its children, at 2 * K + 1 and 2 * K + 2
|
||||
+ (if they exist). N is the last valid index. */
|
||||
static inline void
|
||||
siftdown (void *base, size_t size, size_t k, size_t n,
|
||||
enum swap_type_t swap_type, __compar_d_fn_t cmp, void *arg)
|
||||
{
|
||||
- while (k <= n / 2)
|
||||
+ /* There can only be a heap condition violation if there are
|
||||
+ children. */
|
||||
+ while (2 * k + 1 <= n)
|
||||
{
|
||||
- size_t j = 2 * k;
|
||||
+ /* Left child. */
|
||||
+ size_t j = 2 * k + 1;
|
||||
+ /* If the right child is larger, use it. */
|
||||
if (j < n && cmp (base + (j * size), base + ((j + 1) * size), arg) < 0)
|
||||
j++;
|
||||
|
||||
+ /* If k is already >= to its children, we are done. */
|
||||
if (j == k || cmp (base + (k * size), base + (j * size), arg) >= 0)
|
||||
break;
|
||||
|
||||
+ /* Heal the violation. */
|
||||
do_swap (base + (size * j), base + (k * size), size, swap_type);
|
||||
+
|
||||
+ /* Swapping with j may have introduced a violation at j. Fix
|
||||
+ it in the next loop iteration. */
|
||||
k = j;
|
||||
}
|
||||
}
|
||||
|
||||
+/* Establish the heap condition for the indices 0 to N (inclusive). */
|
||||
static inline void
|
||||
heapify (void *base, size_t size, size_t n, enum swap_type_t swap_type,
|
||||
__compar_d_fn_t cmp, void *arg)
|
||||
{
|
||||
+ /* If n is odd, k = n / 2 has a left child at n, so this is the
|
||||
+ largest index that can have a heap condition violation regarding
|
||||
+ its children. */
|
||||
size_t k = n / 2;
|
||||
while (1)
|
||||
{
|
||||
@@ -158,32 +173,38 @@ heapify (void *base, size_t size, size_t n, enum swap_type_t swap_type,
|
||||
}
|
||||
}
|
||||
|
||||
-/* A non-recursive heapsort, used on introsort implementation as a fallback
|
||||
- routine with worst-case performance of O(nlog n) and worst-case space
|
||||
- complexity of O(1). It sorts the array starting at BASE and ending at
|
||||
- END, with each element of SIZE bytes. The SWAP_TYPE is the callback
|
||||
- function used to swap elements, and CMP is the function used to compare
|
||||
- elements. */
|
||||
+/* A non-recursive heapsort, used on introsort implementation as a
|
||||
+ fallback routine with worst-case performance of O(nlog n) and
|
||||
+ worst-case space complexity of O(1). It sorts the array starting
|
||||
+ at BASE and ending at END (inclusive), with each element of SIZE
|
||||
+ bytes. The SWAP_TYPE is the callback function used to swap
|
||||
+ elements, and CMP is the function used to compare elements. */
|
||||
static void
|
||||
heapsort_r (void *base, void *end, size_t size, enum swap_type_t swap_type,
|
||||
__compar_d_fn_t cmp, void *arg)
|
||||
{
|
||||
- const size_t count = ((uintptr_t) end - (uintptr_t) base) / size;
|
||||
-
|
||||
- if (count < 2)
|
||||
+ size_t n = ((uintptr_t) end - (uintptr_t) base) / size;
|
||||
+ if (n <= 1)
|
||||
+ /* Handled by insertion sort. */
|
||||
return;
|
||||
|
||||
- size_t n = count - 1;
|
||||
-
|
||||
/* Build the binary heap, largest value at the base[0]. */
|
||||
heapify (base, size, n, swap_type, cmp, arg);
|
||||
|
||||
- /* On each iteration base[0:n] is the binary heap, while base[n:count]
|
||||
- is sorted. */
|
||||
- while (n > 0)
|
||||
+ while (true)
|
||||
{
|
||||
+ /* Indices 0 .. n contain the binary heap. Extract the largest
|
||||
+ element put it into the final position in the array. */
|
||||
do_swap (base, base + (n * size), size, swap_type);
|
||||
+
|
||||
+ /* The heap is now one element shorter. */
|
||||
n--;
|
||||
+ if (n == 0)
|
||||
+ break;
|
||||
+
|
||||
+ /* By swapping in elements 0 and the previous value of n (now at
|
||||
+ n + 1), we likely introduced a heap condition violation. Fix
|
||||
+ it for the reduced heap. */
|
||||
siftdown (base, size, 0, n, swap_type, cmp, arg);
|
||||
}
|
||||
}
|
||||
diff --git a/stdlib/tst-qsort4.c b/stdlib/tst-qsort4.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..a7abaa1a37461666
|
||||
--- /dev/null
|
||||
+++ b/stdlib/tst-qsort4.c
|
||||
@@ -0,0 +1,134 @@
|
||||
+/* Test the heapsort implementation behind qsort.
|
||||
+ Copyright (C) 2023 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
|
||||
+ <http://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include "qsort.c"
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/support.h>
|
||||
+
|
||||
+static int
|
||||
+cmp (const void *a1, const void *b1, void *closure)
|
||||
+{
|
||||
+ const signed char *a = a1;
|
||||
+ const signed char *b = b1;
|
||||
+ return *a - *b;
|
||||
+}
|
||||
+
|
||||
+/* Wrapper around heapsort_r that set ups the required variables. */
|
||||
+static void
|
||||
+heapsort_wrapper (void *const pbase, size_t total_elems, size_t size,
|
||||
+ __compar_d_fn_t cmp, void *arg)
|
||||
+{
|
||||
+ char *base_ptr = (char *) pbase;
|
||||
+ char *lo = base_ptr;
|
||||
+ char *hi = &lo[size * (total_elems - 1)];
|
||||
+
|
||||
+ if (total_elems <= 1)
|
||||
+ /* Avoid lossage with unsigned arithmetic below. */
|
||||
+ return;
|
||||
+
|
||||
+ enum swap_type_t swap_type;
|
||||
+ if (is_aligned (pbase, size, 8))
|
||||
+ swap_type = SWAP_WORDS_64;
|
||||
+ else if (is_aligned (pbase, size, 4))
|
||||
+ swap_type = SWAP_WORDS_32;
|
||||
+ else
|
||||
+ swap_type = SWAP_BYTES;
|
||||
+ heapsort_r (lo, hi, size, swap_type, cmp, arg);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+check_one_sort (signed char *array, int length)
|
||||
+{
|
||||
+ signed char *copy = xmalloc (length);
|
||||
+ memcpy (copy, array, length);
|
||||
+ heapsort_wrapper (copy, length, 1, cmp, NULL);
|
||||
+
|
||||
+ /* Verify that the result is sorted. */
|
||||
+ for (int i = 1; i < length; ++i)
|
||||
+ if (copy[i] < copy[i - 1])
|
||||
+ {
|
||||
+ support_record_failure ();
|
||||
+ printf ("error: sorting failure for length %d at offset %d\n",
|
||||
+ length, i - 1);
|
||||
+ printf ("input:");
|
||||
+ for (int i = 0; i < length; ++i)
|
||||
+ printf (" %d", array[i]);
|
||||
+ printf ("\noutput:");
|
||||
+ for (int i = 0; i < length; ++i)
|
||||
+ printf (" %d", copy[i]);
|
||||
+ putchar ('\n');
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* Verify that no elements went away or were added. */
|
||||
+ {
|
||||
+ int expected_counts[256];
|
||||
+ for (int i = 0; i < length; ++i)
|
||||
+ ++expected_counts[array[i] & 0xff];
|
||||
+ int actual_counts[256];
|
||||
+ for (int i = 0; i < length; ++i)
|
||||
+ ++actual_counts[copy[i] & 0xff];
|
||||
+ for (int i = 0; i < 256; ++i)
|
||||
+ TEST_COMPARE (expected_counts[i], expected_counts[i]);
|
||||
+ }
|
||||
+
|
||||
+ free (copy);
|
||||
+}
|
||||
+
|
||||
+/* Enumerate all possible combinations of LENGTH elements. */
|
||||
+static void
|
||||
+check_combinations (int length, signed char *start, int offset)
|
||||
+{
|
||||
+ if (offset == length)
|
||||
+ check_one_sort (start, length);
|
||||
+ else
|
||||
+ for (int i = 0; i < length; ++i)
|
||||
+ {
|
||||
+ start[offset] = i;
|
||||
+ check_combinations(length, start, offset + 1);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ /* A random permutation of 20 values. */
|
||||
+ check_one_sort ((signed char[20]) {5, 12, 16, 10, 14, 11, 9, 13, 8, 15,
|
||||
+ 0, 17, 3, 7, 1, 18, 2, 19, 4, 6}, 20);
|
||||
+
|
||||
+
|
||||
+ /* A permutation that appeared during adversarial testing for the
|
||||
+ quicksort pass. */
|
||||
+ check_one_sort ((signed char[16]) {15, 3, 4, 2, 1, 0, 8, 7, 6, 5, 14,
|
||||
+ 13, 12, 11, 10, 9}, 16);
|
||||
+
|
||||
+ /* Array lengths 2 and less are not handled by heapsort_r and
|
||||
+ deferred to insertion sort. */
|
||||
+ for (int i = 3; i <= 8; ++i)
|
||||
+ {
|
||||
+ signed char *buf = xmalloc (i);
|
||||
+ check_combinations (i, buf, 0);
|
||||
+ free (buf);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
248
SOURCES/glibc-RHEL-24168-13.patch
Normal file
248
SOURCES/glibc-RHEL-24168-13.patch
Normal file
@ -0,0 +1,248 @@
|
||||
commit 64e4acf24da15c11cb83f933947df3b2e8a700cd
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Tue Nov 21 16:45:35 2023 +0100
|
||||
|
||||
stdlib: The qsort implementation needs to use heapsort in more cases
|
||||
|
||||
The existing logic avoided internal stack overflow. To avoid
|
||||
a denial-of-service condition with adversarial input, it is necessary
|
||||
to fall over to heapsort if tail-recursing deeply, too, which does
|
||||
not result in a deep stack of pending partitions.
|
||||
|
||||
The new test stdlib/tst-qsort5 is based on Douglas McIlroy's paper
|
||||
on this subject.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
Conflicts:
|
||||
stdlib/Makefile: Adjust for getenv tests in glibc-RHEL-67692-4.patch.
|
||||
|
||||
diff --git a/stdlib/Makefile b/stdlib/Makefile
|
||||
index ee005ce8caa48abe..a1a511da37f0c18e 100644
|
||||
--- a/stdlib/Makefile
|
||||
+++ b/stdlib/Makefile
|
||||
@@ -212,6 +212,7 @@ tests := \
|
||||
tst-qsort \
|
||||
tst-qsort2 \
|
||||
tst-qsort3 \
|
||||
+ tst-qsort5 \
|
||||
tst-quick_exit \
|
||||
tst-rand48 \
|
||||
tst-rand48-2 \
|
||||
@@ -483,6 +484,7 @@ $(objpfx)tst-setcontext3.out: tst-setcontext3.sh $(objpfx)tst-setcontext3
|
||||
$(common-objpfx)stdlib/; \
|
||||
$(evaluate-test)
|
||||
|
||||
+$(objpfx)tst-qsort5: $(libm)
|
||||
$(objpfx)tst-getenv-signal: $(shared-thread-library)
|
||||
$(objpfx)tst-getenv-thread: $(shared-thread-library)
|
||||
$(objpfx)tst-getenv-unsetenv: $(shared-thread-library)
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index b207c12d2f0a38cc..df8d0012c759e509 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -390,14 +390,23 @@ __qsort_r (void *const pbase, size_t total_elems, size_t size,
|
||||
{
|
||||
if ((size_t) (hi - left_ptr) <= max_thresh)
|
||||
/* Ignore both small partitions. */
|
||||
- top = pop (top, &lo, &hi, &depth);
|
||||
+ {
|
||||
+ top = pop (top, &lo, &hi, &depth);
|
||||
+ --depth;
|
||||
+ }
|
||||
else
|
||||
- /* Ignore small left partition. */
|
||||
- lo = left_ptr;
|
||||
+ {
|
||||
+ /* Ignore small left partition. */
|
||||
+ lo = left_ptr;
|
||||
+ --depth;
|
||||
+ }
|
||||
}
|
||||
else if ((size_t) (hi - left_ptr) <= max_thresh)
|
||||
/* Ignore small right partition. */
|
||||
- hi = right_ptr;
|
||||
+ {
|
||||
+ hi = right_ptr;
|
||||
+ --depth;
|
||||
+ }
|
||||
else if ((right_ptr - lo) > (hi - left_ptr))
|
||||
{
|
||||
/* Push larger left partition indices. */
|
||||
diff --git a/stdlib/tst-qsort5.c b/stdlib/tst-qsort5.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..d3a88c30f8ffb135
|
||||
--- /dev/null
|
||||
+++ b/stdlib/tst-qsort5.c
|
||||
@@ -0,0 +1,171 @@
|
||||
+/* Adversarial test for qsort_r.
|
||||
+ Copyright (C) 2023 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
|
||||
+ <http://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+/* The approach follows Douglas McIlroy, A Killer Adversary for
|
||||
+ Quicksort. Software—Practice and Experience 29 (1999) 341-344.
|
||||
+ Downloaded <http://www.cs.dartmouth.edu/~doug/mdmspe.pdf>
|
||||
+ (2023-11-17). */
|
||||
+
|
||||
+#include <math.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <stdio.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/support.h>
|
||||
+
|
||||
+struct context
|
||||
+{
|
||||
+ /* Called the gas value in the paper. This value is larger than all
|
||||
+ other values (length minus one will do), so comparison with any
|
||||
+ decided value has a known result. */
|
||||
+ int undecided_value;
|
||||
+
|
||||
+ /* If comparing undecided values, one of them as to be assigned a
|
||||
+ value to ensure consistency with future comparisons. This is the
|
||||
+ value that will be used. Starts out at zero. */
|
||||
+ int next_decided;
|
||||
+
|
||||
+ /* Used to trick pivot selection. Deciding the value for the last
|
||||
+ seen undcided value in a decided/undecided comparison happens
|
||||
+ to trick the many qsort implementations. */
|
||||
+ int last_undecided_index;
|
||||
+
|
||||
+ /* This array contains the actually asigned values. The call to
|
||||
+ qsort_r sorts a different array that contains indices into this
|
||||
+ array. */
|
||||
+ int *decided_values;
|
||||
+};
|
||||
+
|
||||
+static int
|
||||
+compare_opponent (const void *l1, const void *r1, void *ctx1)
|
||||
+{
|
||||
+ const int *l = l1;
|
||||
+ const int *r = r1;
|
||||
+ struct context *ctx = ctx1;
|
||||
+ int rvalue = ctx->decided_values[*r];
|
||||
+ int lvalue = ctx->decided_values[*l];
|
||||
+
|
||||
+ if (lvalue == ctx->undecided_value)
|
||||
+ {
|
||||
+ if (rvalue == ctx->undecided_value)
|
||||
+ {
|
||||
+ /* Both values are undecided. In this case, make a decision
|
||||
+ for the last-used undecided value. This is tweak is very
|
||||
+ specific to quicksort. */
|
||||
+ if (*l == ctx->last_undecided_index)
|
||||
+ {
|
||||
+ ctx->decided_values[*l] = ctx->next_decided;
|
||||
+ ++ctx->next_decided;
|
||||
+ /* The undecided value or *r is greater. */
|
||||
+ return -1;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ ctx->decided_values[*r] = ctx->next_decided;
|
||||
+ ++ctx->next_decided;
|
||||
+ /* The undecided value for *l is greater. */
|
||||
+ return 1;
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ ctx->last_undecided_index = *l;
|
||||
+ return 1;
|
||||
+ }
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ /* *l is a decided value. */
|
||||
+ if (rvalue == ctx->undecided_value)
|
||||
+ {
|
||||
+ ctx->last_undecided_index = *r;
|
||||
+ /* The undecided value for *r is greater. */
|
||||
+ return -1;
|
||||
+ }
|
||||
+ else
|
||||
+ return lvalue - rvalue;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/* Return a pointer to the adversarial permutation of length N. */
|
||||
+static int *
|
||||
+create_permutation (size_t n)
|
||||
+{
|
||||
+ struct context ctx =
|
||||
+ {
|
||||
+ .undecided_value = n - 1, /* Larger than all other values. */
|
||||
+ .decided_values = xcalloc (n, sizeof (int)),
|
||||
+ };
|
||||
+ for (size_t i = 0; i < n; ++i)
|
||||
+ ctx.decided_values[i] = ctx.undecided_value;
|
||||
+ int *scratch = xcalloc (n, sizeof (int));
|
||||
+ for (size_t i = 0; i < n; ++i)
|
||||
+ scratch[i] = i;
|
||||
+ qsort_r (scratch, n, sizeof (*scratch), compare_opponent, &ctx);
|
||||
+ free (scratch);
|
||||
+ return ctx.decided_values;
|
||||
+}
|
||||
+
|
||||
+/* Callback function for qsort which counts the number of invocations
|
||||
+ in *CLOSURE. */
|
||||
+static int
|
||||
+compare_counter (const void *l1, const void *r1, void *closure)
|
||||
+{
|
||||
+ const int *l = l1;
|
||||
+ const int *r = r1;
|
||||
+ unsigned long long int *counter = closure;
|
||||
+ ++*counter;
|
||||
+ return *l - *r;
|
||||
+}
|
||||
+
|
||||
+/* Count the comparisons required for an adversarial permutation of
|
||||
+ length N. */
|
||||
+static unsigned long long int
|
||||
+count_comparisons (size_t n)
|
||||
+{
|
||||
+ int *array = create_permutation (n);
|
||||
+ unsigned long long int counter = 0;
|
||||
+ qsort_r (array, n, sizeof (*array), compare_counter, &counter);
|
||||
+ free (array);
|
||||
+ return counter;
|
||||
+}
|
||||
+
|
||||
+/* Check the scaling factor for one adversarial permutation of length
|
||||
+ N, and report some statistics. */
|
||||
+static void
|
||||
+check_one_n (size_t n)
|
||||
+{
|
||||
+ unsigned long long int count = count_comparisons (n);
|
||||
+ double factor = count / (n * log (count));
|
||||
+ printf ("info: length %zu: %llu comparisons ~ %f * n * log (n)\n",
|
||||
+ n, count, factor);
|
||||
+ /* This is an arbitrary factor which is true for the current
|
||||
+ implementation across a wide range of sizes. */
|
||||
+ TEST_VERIFY (factor <= 4.5);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ check_one_n (100);
|
||||
+ check_one_n (1000);
|
||||
+ for (int i = 1; i <= 15; ++i)
|
||||
+ check_one_n (i * 10 * 1000);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
105
SOURCES/glibc-RHEL-24168-14.patch
Normal file
105
SOURCES/glibc-RHEL-24168-14.patch
Normal file
@ -0,0 +1,105 @@
|
||||
commit b9390ba93676c4b1e87e218af5e7e4bb596312ac
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Mon Dec 4 06:35:56 2023 +0100
|
||||
|
||||
stdlib: Fix array bounds protection in insertion sort phase of qsort
|
||||
|
||||
The previous check did not do anything because tmp_ptr already
|
||||
points before run_ptr due to the way it is initialized.
|
||||
|
||||
Fixes commit e4d8117b82065dc72e8df80097360e7c05a349b9
|
||||
("stdlib: Avoid another self-comparison in qsort").
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/stdlib/Makefile b/stdlib/Makefile
|
||||
index a1a511da37f0c18e..82d9d909890853b7 100644
|
||||
--- a/stdlib/Makefile
|
||||
+++ b/stdlib/Makefile
|
||||
@@ -213,6 +213,7 @@ tests := \
|
||||
tst-qsort2 \
|
||||
tst-qsort3 \
|
||||
tst-qsort5 \
|
||||
+ tst-qsort6 \
|
||||
tst-quick_exit \
|
||||
tst-rand48 \
|
||||
tst-rand48-2 \
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index df8d0012c759e509..3d5405705862ddf0 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -239,7 +239,7 @@ insertion_sort_qsort_partitions (void *const pbase, size_t total_elems,
|
||||
while ((run_ptr += size) <= end_ptr)
|
||||
{
|
||||
tmp_ptr = run_ptr - size;
|
||||
- while (run_ptr != tmp_ptr && cmp (run_ptr, tmp_ptr, arg) < 0)
|
||||
+ while (tmp_ptr != base_ptr && cmp (run_ptr, tmp_ptr, arg) < 0)
|
||||
tmp_ptr -= size;
|
||||
|
||||
tmp_ptr += size;
|
||||
diff --git a/stdlib/tst-qsort6.c b/stdlib/tst-qsort6.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..8ec0a6b633bc8398
|
||||
--- /dev/null
|
||||
+++ b/stdlib/tst-qsort6.c
|
||||
@@ -0,0 +1,60 @@
|
||||
+/* Test qsort with invalid comparison functions.
|
||||
+ Copyright (C) 2023 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
|
||||
+ <http://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <array_length.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <support/check.h>
|
||||
+
|
||||
+/* Invalid comparison function that always returns -1. */
|
||||
+static int
|
||||
+invalid_compare_1 (const void *a1, const void *b1)
|
||||
+{
|
||||
+ const int *a = a1;
|
||||
+ const int *b = b1;
|
||||
+ /* Check that the marker value matches, which means that we are
|
||||
+ likely within the array. */
|
||||
+ TEST_COMPARE (*a, 842523635);
|
||||
+ TEST_COMPARE (*b, 842523635);
|
||||
+ TEST_VERIFY_EXIT (*a == 842523635);
|
||||
+ TEST_VERIFY_EXIT (*b == 842523635);
|
||||
+ return -1;
|
||||
+}
|
||||
+
|
||||
+/* Invalid comparison function that always returns 1. */
|
||||
+static int
|
||||
+invalid_compare_2 (const void *a1, const void *b1)
|
||||
+{
|
||||
+ const int *a = a1;
|
||||
+ const int *b = b1;
|
||||
+ TEST_COMPARE (*a, 842523635);
|
||||
+ TEST_COMPARE (*b, 842523635);
|
||||
+ TEST_VERIFY_EXIT (*a == 842523635);
|
||||
+ TEST_VERIFY_EXIT (*b == 842523635);
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ int array[] = {842523635, 842523635, 842523635, 842523635, 842523635};
|
||||
+ qsort (array, array_length (array), sizeof (array[0]), invalid_compare_1);
|
||||
+ qsort (array, array_length (array), sizeof (array[0]), invalid_compare_2);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
876
SOURCES/glibc-RHEL-24168-15.patch
Normal file
876
SOURCES/glibc-RHEL-24168-15.patch
Normal file
@ -0,0 +1,876 @@
|
||||
commit 709fbd3ec3595f2d1076b4fec09a739327459288
|
||||
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Date: Mon Jan 15 11:07:21 2024 -0300
|
||||
|
||||
stdlib: Reinstate stable mergesort implementation on qsort
|
||||
|
||||
The mergesort removal from qsort implementation (commit 03bf8357e8)
|
||||
had the side-effect of making sorting nonstable. Although neither
|
||||
POSIX nor C standard specify that qsort should be stable, it seems
|
||||
that it has become an instance of Hyrum's law where multiple programs
|
||||
expect it.
|
||||
|
||||
Also, the resulting introsort implementation is not faster than
|
||||
the previous mergesort (which makes the change even less appealing).
|
||||
|
||||
This patch restores the previous mergesort implementation, with the
|
||||
exception of machinery that checks the resulting allocation against
|
||||
the _SC_PHYS_PAGES (it only adds complexity and the heuristic not
|
||||
always make sense depending on the system configuration and load).
|
||||
The alloca usage was replaced with a fixed-size buffer.
|
||||
|
||||
For the fallback mechanism, the implementation uses heapsort. It is
|
||||
simpler than quicksort, and it does not suffer from adversarial
|
||||
inputs. With memory overcommit, it should be rarely triggered.
|
||||
|
||||
The drawback is mergesort requires O(n) extra space, and since it is
|
||||
allocated with malloc the function is AS-signal-unsafe. It should be
|
||||
feasible to change it to use mmap, although I am not sure how urgent
|
||||
it is. The heapsort is also nonstable, so programs that require a
|
||||
stable sort would still be subject to this latent issue.
|
||||
|
||||
The tst-qsort5 is removed since it will not create quicksort adversarial
|
||||
inputs with the current qsort_r implementation.
|
||||
|
||||
Checked on x86_64-linux-gnu and aarch64-linux-gnu.
|
||||
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
Conflicts:
|
||||
stdlib/tst-qsort5.c: Deletion had conflicts due to copyright update.
|
||||
|
||||
diff --git a/manual/argp.texi b/manual/argp.texi
|
||||
index b77ad68285ecb732..0023441812d4e584 100644
|
||||
--- a/manual/argp.texi
|
||||
+++ b/manual/argp.texi
|
||||
@@ -735,7 +735,7 @@ for options, bad phase of the moon, etc.
|
||||
@c hol_set_group ok
|
||||
@c hol_find_entry ok
|
||||
@c hol_sort @mtslocale @acucorrupt
|
||||
-@c qsort dup
|
||||
+@c qsort dup @acucorrupt
|
||||
@c hol_entry_qcmp @mtslocale
|
||||
@c hol_entry_cmp @mtslocale
|
||||
@c group_cmp ok
|
||||
diff --git a/manual/locale.texi b/manual/locale.texi
|
||||
index f6afa5dc44a2a016..1b3f97839bb5d068 100644
|
||||
--- a/manual/locale.texi
|
||||
+++ b/manual/locale.texi
|
||||
@@ -253,7 +253,7 @@ The symbols in this section are defined in the header file @file{locale.h}.
|
||||
@c calculate_head_size ok
|
||||
@c __munmap ok
|
||||
@c compute_hashval ok
|
||||
-@c qsort dup
|
||||
+@c qsort dup @acucorrupt
|
||||
@c rangecmp ok
|
||||
@c malloc @ascuheap @acsmem
|
||||
@c strdup @ascuheap @acsmem
|
||||
diff --git a/manual/search.texi b/manual/search.texi
|
||||
index a550858478f7fc83..ffaadc46f51b18f9 100644
|
||||
--- a/manual/search.texi
|
||||
+++ b/manual/search.texi
|
||||
@@ -159,7 +159,7 @@ To sort an array using an arbitrary comparison function, use the
|
||||
|
||||
@deftypefun void qsort (void *@var{array}, size_t @var{count}, size_t @var{size}, comparison_fn_t @var{compare})
|
||||
@standards{ISO, stdlib.h}
|
||||
-@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
|
||||
+@safety{@prelim{}@mtsafe{}@assafe{}@acunsafe{@acucorrupt{}}}
|
||||
The @code{qsort} function sorts the array @var{array}. The array
|
||||
contains @var{count} elements, each of which is of size @var{size}.
|
||||
|
||||
@@ -199,8 +199,9 @@ Functions}):
|
||||
The @code{qsort} function derives its name from the fact that it was
|
||||
originally implemented using the ``quick sort'' algorithm.
|
||||
|
||||
-The implementation of @code{qsort} in this library is an in-place sort
|
||||
-and uses a constant extra space (allocated on the stack).
|
||||
+The implementation of @code{qsort} attempts to allocate auxiliary storage
|
||||
+and use the merge sort algorithm, without violating C standard requirement
|
||||
+that arguments passed to the comparison function point within the array.
|
||||
@end deftypefun
|
||||
|
||||
@node Search/Sort Example
|
||||
diff --git a/stdlib/Makefile b/stdlib/Makefile
|
||||
index 82d9d909890853b7..a9d91a57c08ac506 100644
|
||||
--- a/stdlib/Makefile
|
||||
+++ b/stdlib/Makefile
|
||||
@@ -212,7 +212,6 @@ tests := \
|
||||
tst-qsort \
|
||||
tst-qsort2 \
|
||||
tst-qsort3 \
|
||||
- tst-qsort5 \
|
||||
tst-qsort6 \
|
||||
tst-quick_exit \
|
||||
tst-rand48 \
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index 3d5405705862ddf0..b95889047ba31193 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -20,6 +20,7 @@
|
||||
Engineering a sort function; Jon Bentley and M. Douglas McIlroy;
|
||||
Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */
|
||||
|
||||
+#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <memswap.h>
|
||||
#include <stdlib.h>
|
||||
@@ -33,9 +34,13 @@ enum swap_type_t
|
||||
{
|
||||
SWAP_WORDS_64,
|
||||
SWAP_WORDS_32,
|
||||
+ SWAP_VOID_ARG,
|
||||
SWAP_BYTES
|
||||
};
|
||||
|
||||
+typedef uint32_t __attribute__ ((__may_alias__)) u32_alias_t;
|
||||
+typedef uint64_t __attribute__ ((__may_alias__)) u64_alias_t;
|
||||
+
|
||||
/* If this function returns true, elements can be safely copied using word
|
||||
loads and stores. Otherwise, it might not be safe. BASE (as an integer)
|
||||
must be a multiple of the word alignment. SIZE must be a multiple of
|
||||
@@ -52,7 +57,6 @@ is_aligned (const void *base, size_t size, size_t wordsize)
|
||||
static inline void
|
||||
swap_words_64 (void * restrict a, void * restrict b, size_t n)
|
||||
{
|
||||
- typedef uint64_t __attribute__ ((__may_alias__)) u64_alias_t;
|
||||
do
|
||||
{
|
||||
n -= 8;
|
||||
@@ -65,7 +69,6 @@ swap_words_64 (void * restrict a, void * restrict b, size_t n)
|
||||
static inline void
|
||||
swap_words_32 (void * restrict a, void * restrict b, size_t n)
|
||||
{
|
||||
- typedef uint32_t __attribute__ ((__may_alias__)) u32_alias_t;
|
||||
do
|
||||
{
|
||||
n -= 4;
|
||||
@@ -89,43 +92,6 @@ do_swap (void * restrict a, void * restrict b, size_t size,
|
||||
__memswap (a, b, size);
|
||||
}
|
||||
|
||||
-/* Discontinue quicksort algorithm when partition gets below this size.
|
||||
- This particular magic number was chosen to work best on a Sun 4/260. */
|
||||
-#define MAX_THRESH 4
|
||||
-
|
||||
-/* Stack node declarations used to store unfulfilled partition obligations. */
|
||||
-typedef struct
|
||||
- {
|
||||
- char *lo;
|
||||
- char *hi;
|
||||
- size_t depth;
|
||||
- } stack_node;
|
||||
-
|
||||
-/* The stack needs log (total_elements) entries (we could even subtract
|
||||
- log(MAX_THRESH)). Since total_elements has type size_t, we get as
|
||||
- upper bound for log (total_elements):
|
||||
- bits per byte (CHAR_BIT) * sizeof(size_t). */
|
||||
-enum { STACK_SIZE = CHAR_BIT * sizeof (size_t) };
|
||||
-
|
||||
-static inline stack_node *
|
||||
-push (stack_node *top, char *lo, char *hi, size_t depth)
|
||||
-{
|
||||
- top->lo = lo;
|
||||
- top->hi = hi;
|
||||
- top->depth = depth;
|
||||
- return ++top;
|
||||
-}
|
||||
-
|
||||
-static inline stack_node *
|
||||
-pop (stack_node *top, char **lo, char **hi, size_t *depth)
|
||||
-{
|
||||
- --top;
|
||||
- *lo = top->lo;
|
||||
- *hi = top->hi;
|
||||
- *depth = top->depth;
|
||||
- return top;
|
||||
-}
|
||||
-
|
||||
/* Establish the heap condition at index K, that is, the key at K will
|
||||
not be less than either of its children, at 2 * K + 1 and 2 * K + 2
|
||||
(if they exist). N is the last valid index. */
|
||||
@@ -173,21 +139,35 @@ heapify (void *base, size_t size, size_t n, enum swap_type_t swap_type,
|
||||
}
|
||||
}
|
||||
|
||||
-/* A non-recursive heapsort, used on introsort implementation as a
|
||||
- fallback routine with worst-case performance of O(nlog n) and
|
||||
- worst-case space complexity of O(1). It sorts the array starting
|
||||
- at BASE and ending at END (inclusive), with each element of SIZE
|
||||
- bytes. The SWAP_TYPE is the callback function used to swap
|
||||
- elements, and CMP is the function used to compare elements. */
|
||||
+static enum swap_type_t
|
||||
+get_swap_type (void *const pbase, size_t size)
|
||||
+{
|
||||
+ if ((size & (sizeof (uint32_t) - 1)) == 0
|
||||
+ && ((uintptr_t) pbase) % __alignof__ (uint32_t) == 0)
|
||||
+ {
|
||||
+ if (size == sizeof (uint32_t))
|
||||
+ return SWAP_WORDS_32;
|
||||
+ else if (size == sizeof (uint64_t)
|
||||
+ && ((uintptr_t) pbase) % __alignof__ (uint64_t) == 0)
|
||||
+ return SWAP_WORDS_64;
|
||||
+ }
|
||||
+ return SWAP_BYTES;
|
||||
+}
|
||||
+
|
||||
+
|
||||
+/* A non-recursive heapsort with worst-case performance of O(nlog n) and
|
||||
+ worst-case space complexity of O(1). It sorts the array starting at
|
||||
+ BASE with n + 1 elements of SIZE bytes. The SWAP_TYPE is the callback
|
||||
+ function used to swap elements, and CMP is the function used to compare
|
||||
+ elements. */
|
||||
static void
|
||||
-heapsort_r (void *base, void *end, size_t size, enum swap_type_t swap_type,
|
||||
- __compar_d_fn_t cmp, void *arg)
|
||||
+heapsort_r (void *base, size_t n, size_t size, __compar_d_fn_t cmp, void *arg)
|
||||
{
|
||||
- size_t n = ((uintptr_t) end - (uintptr_t) base) / size;
|
||||
if (n <= 1)
|
||||
- /* Handled by insertion sort. */
|
||||
return;
|
||||
|
||||
+ enum swap_type_t swap_type = get_swap_type (base, size);
|
||||
+
|
||||
/* Build the binary heap, largest value at the base[0]. */
|
||||
heapify (base, size, n, swap_type, cmp, arg);
|
||||
|
||||
@@ -209,226 +189,226 @@ heapsort_r (void *base, void *end, size_t size, enum swap_type_t swap_type,
|
||||
}
|
||||
}
|
||||
|
||||
-static inline void
|
||||
-insertion_sort_qsort_partitions (void *const pbase, size_t total_elems,
|
||||
- size_t size, enum swap_type_t swap_type,
|
||||
- __compar_d_fn_t cmp, void *arg)
|
||||
+/* The maximum size in bytes required by mergesort that will be provided
|
||||
+ through a buffer allocated in the stack. */
|
||||
+#define QSORT_STACK_SIZE 1024
|
||||
+
|
||||
+/* Elements larger than this value will be sorted through indirect sorting
|
||||
+ to minimize the need to memory swap calls. */
|
||||
+#define INDIRECT_SORT_SIZE_THRES 32
|
||||
+
|
||||
+struct msort_param
|
||||
{
|
||||
- char *base_ptr = (char *) pbase;
|
||||
- char *const end_ptr = &base_ptr[size * (total_elems - 1)];
|
||||
- char *tmp_ptr = base_ptr;
|
||||
-#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
- const size_t max_thresh = MAX_THRESH * size;
|
||||
- char *thresh = min(end_ptr, base_ptr + max_thresh);
|
||||
- char *run_ptr;
|
||||
+ size_t s;
|
||||
+ enum swap_type_t var;
|
||||
+ __compar_d_fn_t cmp;
|
||||
+ void *arg;
|
||||
+ char *t;
|
||||
+};
|
||||
|
||||
- /* Find smallest element in first threshold and place it at the
|
||||
- array's beginning. This is the smallest array element,
|
||||
- and the operation speeds up insertion sort's inner loop. */
|
||||
+static void
|
||||
+msort_with_tmp (const struct msort_param *p, void *b, size_t n)
|
||||
+{
|
||||
+ char *b1, *b2;
|
||||
+ size_t n1, n2;
|
||||
|
||||
- for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
|
||||
- if (cmp (run_ptr, tmp_ptr, arg) < 0)
|
||||
- tmp_ptr = run_ptr;
|
||||
+ if (n <= 1)
|
||||
+ return;
|
||||
|
||||
- if (tmp_ptr != base_ptr)
|
||||
- do_swap (tmp_ptr, base_ptr, size, swap_type);
|
||||
+ n1 = n / 2;
|
||||
+ n2 = n - n1;
|
||||
+ b1 = b;
|
||||
+ b2 = (char *) b + (n1 * p->s);
|
||||
|
||||
- /* Insertion sort, running from left-hand-side up to right-hand-side. */
|
||||
+ msort_with_tmp (p, b1, n1);
|
||||
+ msort_with_tmp (p, b2, n2);
|
||||
|
||||
- run_ptr = base_ptr + size;
|
||||
- while ((run_ptr += size) <= end_ptr)
|
||||
+ char *tmp = p->t;
|
||||
+ const size_t s = p->s;
|
||||
+ __compar_d_fn_t cmp = p->cmp;
|
||||
+ void *arg = p->arg;
|
||||
+ switch (p->var)
|
||||
{
|
||||
- tmp_ptr = run_ptr - size;
|
||||
- while (tmp_ptr != base_ptr && cmp (run_ptr, tmp_ptr, arg) < 0)
|
||||
- tmp_ptr -= size;
|
||||
-
|
||||
- tmp_ptr += size;
|
||||
- if (tmp_ptr != run_ptr)
|
||||
- {
|
||||
- char *trav;
|
||||
-
|
||||
- trav = run_ptr + size;
|
||||
- while (--trav >= run_ptr)
|
||||
- {
|
||||
- char c = *trav;
|
||||
- char *hi, *lo;
|
||||
-
|
||||
- for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
|
||||
- *hi = *lo;
|
||||
- *hi = c;
|
||||
- }
|
||||
- }
|
||||
+ case SWAP_WORDS_32:
|
||||
+ while (n1 > 0 && n2 > 0)
|
||||
+ {
|
||||
+ if (cmp (b1, b2, arg) <= 0)
|
||||
+ {
|
||||
+ *(u32_alias_t *) tmp = *(u32_alias_t *) b1;
|
||||
+ b1 += sizeof (u32_alias_t);
|
||||
+ --n1;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ *(u32_alias_t *) tmp = *(u32_alias_t *) b2;
|
||||
+ b2 += sizeof (u32_alias_t);
|
||||
+ --n2;
|
||||
+ }
|
||||
+ tmp += sizeof (u32_alias_t);
|
||||
+ }
|
||||
+ break;
|
||||
+ case SWAP_WORDS_64:
|
||||
+ while (n1 > 0 && n2 > 0)
|
||||
+ {
|
||||
+ if (cmp (b1, b2, arg) <= 0)
|
||||
+ {
|
||||
+ *(u64_alias_t *) tmp = *(u64_alias_t *) b1;
|
||||
+ b1 += sizeof (u64_alias_t);
|
||||
+ --n1;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ *(u64_alias_t *) tmp = *(u64_alias_t *) b2;
|
||||
+ b2 += sizeof (u64_alias_t);
|
||||
+ --n2;
|
||||
+ }
|
||||
+ tmp += sizeof (u64_alias_t);
|
||||
+ }
|
||||
+ break;
|
||||
+ case SWAP_VOID_ARG:
|
||||
+ while (n1 > 0 && n2 > 0)
|
||||
+ {
|
||||
+ if ((*cmp) (*(const void **) b1, *(const void **) b2, arg) <= 0)
|
||||
+ {
|
||||
+ *(void **) tmp = *(void **) b1;
|
||||
+ b1 += sizeof (void *);
|
||||
+ --n1;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ *(void **) tmp = *(void **) b2;
|
||||
+ b2 += sizeof (void *);
|
||||
+ --n2;
|
||||
+ }
|
||||
+ tmp += sizeof (void *);
|
||||
+ }
|
||||
+ break;
|
||||
+ default:
|
||||
+ while (n1 > 0 && n2 > 0)
|
||||
+ {
|
||||
+ if (cmp (b1, b2, arg) <= 0)
|
||||
+ {
|
||||
+ tmp = (char *) __mempcpy (tmp, b1, s);
|
||||
+ b1 += s;
|
||||
+ --n1;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ tmp = (char *) __mempcpy (tmp, b2, s);
|
||||
+ b2 += s;
|
||||
+ --n2;
|
||||
+ }
|
||||
+ }
|
||||
+ break;
|
||||
}
|
||||
-}
|
||||
-
|
||||
-/* Order size using quicksort. This implementation incorporates
|
||||
- four optimizations discussed in Sedgewick:
|
||||
|
||||
- 1. Non-recursive, using an explicit stack of pointer that store the
|
||||
- next array partition to sort. To save time, this maximum amount
|
||||
- of space required to store an array of SIZE_MAX is allocated on the
|
||||
- stack. Assuming a 32-bit (64 bit) integer for size_t, this needs
|
||||
- only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes).
|
||||
- Pretty cheap, actually.
|
||||
-
|
||||
- 2. Chose the pivot element using a median-of-three decision tree.
|
||||
- This reduces the probability of selecting a bad pivot value and
|
||||
- eliminates certain extraneous comparisons.
|
||||
+ if (n1 > 0)
|
||||
+ memcpy (tmp, b1, n1 * s);
|
||||
+ memcpy (b, p->t, (n - n2) * s);
|
||||
+}
|
||||
|
||||
- 3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
|
||||
- insertion sort to order the MAX_THRESH items within each partition.
|
||||
- This is a big win, since insertion sort is faster for small, mostly
|
||||
- sorted array segments.
|
||||
+static void
|
||||
+__attribute_used__
|
||||
+indirect_msort_with_tmp (const struct msort_param *p, void *b, size_t n,
|
||||
+ size_t s)
|
||||
+{
|
||||
+ /* Indirect sorting. */
|
||||
+ char *ip = (char *) b;
|
||||
+ void **tp = (void **) (p->t + n * sizeof (void *));
|
||||
+ void **t = tp;
|
||||
+ void *tmp_storage = (void *) (tp + n);
|
||||
|
||||
- 4. The larger of the two sub-partitions is always pushed onto the
|
||||
- stack first, with the algorithm then concentrating on the
|
||||
- smaller partition. This *guarantees* no more than log (total_elems)
|
||||
- stack size is needed (actually O(1) in this case)! */
|
||||
+ while ((void *) t < tmp_storage)
|
||||
+ {
|
||||
+ *t++ = ip;
|
||||
+ ip += s;
|
||||
+ }
|
||||
+ msort_with_tmp (p, p->t + n * sizeof (void *), n);
|
||||
+
|
||||
+ /* tp[0] .. tp[n - 1] is now sorted, copy around entries of
|
||||
+ the original array. Knuth vol. 3 (2nd ed.) exercise 5.2-10. */
|
||||
+ char *kp;
|
||||
+ size_t i;
|
||||
+ for (i = 0, ip = (char *) b; i < n; i++, ip += s)
|
||||
+ if ((kp = tp[i]) != ip)
|
||||
+ {
|
||||
+ size_t j = i;
|
||||
+ char *jp = ip;
|
||||
+ memcpy (tmp_storage, ip, s);
|
||||
+
|
||||
+ do
|
||||
+ {
|
||||
+ size_t k = (kp - (char *) b) / s;
|
||||
+ tp[j] = jp;
|
||||
+ memcpy (jp, kp, s);
|
||||
+ j = k;
|
||||
+ jp = kp;
|
||||
+ kp = tp[k];
|
||||
+ }
|
||||
+ while (kp != ip);
|
||||
+
|
||||
+ tp[j] = jp;
|
||||
+ memcpy (jp, tmp_storage, s);
|
||||
+ }
|
||||
+}
|
||||
|
||||
void
|
||||
__qsort_r (void *const pbase, size_t total_elems, size_t size,
|
||||
__compar_d_fn_t cmp, void *arg)
|
||||
{
|
||||
- char *base_ptr = (char *) pbase;
|
||||
-
|
||||
- const size_t max_thresh = MAX_THRESH * size;
|
||||
-
|
||||
if (total_elems <= 1)
|
||||
- /* Avoid lossage with unsigned arithmetic below. */
|
||||
return;
|
||||
|
||||
- enum swap_type_t swap_type;
|
||||
- if (is_aligned (pbase, size, 8))
|
||||
- swap_type = SWAP_WORDS_64;
|
||||
- else if (is_aligned (pbase, size, 4))
|
||||
- swap_type = SWAP_WORDS_32;
|
||||
- else
|
||||
- swap_type = SWAP_BYTES;
|
||||
+ /* Align to the maximum size used by the swap optimization. */
|
||||
+ _Alignas (uint64_t) char tmp[QSORT_STACK_SIZE];
|
||||
+ size_t total_size = total_elems * size;
|
||||
+ char *buf;
|
||||
|
||||
- /* Maximum depth before quicksort switches to heapsort. */
|
||||
- size_t depth = 2 * (sizeof (size_t) * CHAR_BIT - 1
|
||||
- - __builtin_clzl (total_elems));
|
||||
+ if (size > INDIRECT_SORT_SIZE_THRES)
|
||||
+ total_size = 2 * total_elems * sizeof (void *) + size;
|
||||
|
||||
- if (total_elems > MAX_THRESH)
|
||||
+ if (total_size < sizeof buf)
|
||||
+ buf = tmp;
|
||||
+ else
|
||||
{
|
||||
- char *lo = base_ptr;
|
||||
- char *hi = &lo[size * (total_elems - 1)];
|
||||
- stack_node stack[STACK_SIZE];
|
||||
- stack_node *top = push (stack, NULL, NULL, depth);
|
||||
-
|
||||
- while (stack < top)
|
||||
- {
|
||||
- if (depth == 0)
|
||||
- {
|
||||
- heapsort_r (lo, hi, size, swap_type, cmp, arg);
|
||||
- top = pop (top, &lo, &hi, &depth);
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- char *left_ptr;
|
||||
- char *right_ptr;
|
||||
-
|
||||
- /* Select median value from among LO, MID, and HI. Rearrange
|
||||
- LO and HI so the three values are sorted. This lowers the
|
||||
- probability of picking a pathological pivot value and
|
||||
- skips a comparison for both the LEFT_PTR and RIGHT_PTR in
|
||||
- the while loops. */
|
||||
-
|
||||
- char *mid = lo + size * ((hi - lo) / size >> 1);
|
||||
-
|
||||
- if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
|
||||
- do_swap (mid, lo, size, swap_type);
|
||||
- if ((*cmp) ((void *) hi, (void *) mid, arg) < 0)
|
||||
- do_swap (mid, hi, size, swap_type);
|
||||
- else
|
||||
- goto jump_over;
|
||||
- if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
|
||||
- do_swap (mid, lo, size, swap_type);
|
||||
- jump_over:;
|
||||
-
|
||||
- left_ptr = lo + size;
|
||||
- right_ptr = hi - size;
|
||||
-
|
||||
- /* Here's the famous ``collapse the walls'' section of quicksort.
|
||||
- Gotta like those tight inner loops! They are the main reason
|
||||
- that this algorithm runs much faster than others. */
|
||||
- do
|
||||
- {
|
||||
- while (left_ptr != mid
|
||||
- && (*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
|
||||
- left_ptr += size;
|
||||
-
|
||||
- while (right_ptr != mid
|
||||
- && (*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
|
||||
- right_ptr -= size;
|
||||
-
|
||||
- if (left_ptr < right_ptr)
|
||||
- {
|
||||
- do_swap (left_ptr, right_ptr, size, swap_type);
|
||||
- if (mid == left_ptr)
|
||||
- mid = right_ptr;
|
||||
- else if (mid == right_ptr)
|
||||
- mid = left_ptr;
|
||||
- left_ptr += size;
|
||||
- right_ptr -= size;
|
||||
- }
|
||||
- else if (left_ptr == right_ptr)
|
||||
- {
|
||||
- left_ptr += size;
|
||||
- right_ptr -= size;
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
- while (left_ptr <= right_ptr);
|
||||
-
|
||||
- /* Set up pointers for next iteration. First determine whether
|
||||
- left and right partitions are below the threshold size. If so,
|
||||
- ignore one or both. Otherwise, push the larger partition's
|
||||
- bounds on the stack and continue sorting the smaller one. */
|
||||
-
|
||||
- if ((size_t) (right_ptr - lo) <= max_thresh)
|
||||
- {
|
||||
- if ((size_t) (hi - left_ptr) <= max_thresh)
|
||||
- /* Ignore both small partitions. */
|
||||
- {
|
||||
- top = pop (top, &lo, &hi, &depth);
|
||||
- --depth;
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- /* Ignore small left partition. */
|
||||
- lo = left_ptr;
|
||||
- --depth;
|
||||
- }
|
||||
- }
|
||||
- else if ((size_t) (hi - left_ptr) <= max_thresh)
|
||||
- /* Ignore small right partition. */
|
||||
- {
|
||||
- hi = right_ptr;
|
||||
- --depth;
|
||||
- }
|
||||
- else if ((right_ptr - lo) > (hi - left_ptr))
|
||||
- {
|
||||
- /* Push larger left partition indices. */
|
||||
- top = push (top, lo, right_ptr, depth - 1);
|
||||
- lo = left_ptr;
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- /* Push larger right partition indices. */
|
||||
- top = push (top, left_ptr, hi, depth - 1);
|
||||
- hi = right_ptr;
|
||||
- }
|
||||
- }
|
||||
+ int save = errno;
|
||||
+ buf = malloc (total_size);
|
||||
+ __set_errno (save);
|
||||
+ if (buf == NULL)
|
||||
+ {
|
||||
+ /* Fallback to heapsort in case of memory failure. */
|
||||
+ heapsort_r (pbase, total_elems - 1, size, cmp, arg);
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (size > INDIRECT_SORT_SIZE_THRES)
|
||||
+ {
|
||||
+ const struct msort_param msort_param =
|
||||
+ {
|
||||
+ .s = sizeof (void *),
|
||||
+ .cmp = cmp,
|
||||
+ .arg = arg,
|
||||
+ .var = SWAP_VOID_ARG,
|
||||
+ .t = buf,
|
||||
+ };
|
||||
+ indirect_msort_with_tmp (&msort_param, pbase, total_elems, size);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ const struct msort_param msort_param =
|
||||
+ {
|
||||
+ .s = size,
|
||||
+ .cmp = cmp,
|
||||
+ .arg = arg,
|
||||
+ .var = get_swap_type (pbase, size),
|
||||
+ .t = buf,
|
||||
+ };
|
||||
+ msort_with_tmp (&msort_param, pbase, total_elems);
|
||||
}
|
||||
|
||||
- /* Once the BASE_PTR array is partially sorted by quicksort the rest
|
||||
- is completely sorted using insertion sort, since this is efficient
|
||||
- for partitions below MAX_THRESH size. BASE_PTR points to the beginning
|
||||
- of the array to sort, and END_PTR points at the very last element in
|
||||
- the array (*not* one beyond it!). */
|
||||
- insertion_sort_qsort_partitions (pbase, total_elems, size, swap_type, cmp,
|
||||
- arg);
|
||||
+ if (buf != tmp)
|
||||
+ free (buf);
|
||||
}
|
||||
libc_hidden_def (__qsort_r)
|
||||
weak_alias (__qsort_r, qsort_r)
|
||||
diff --git a/stdlib/tst-qsort4.c b/stdlib/tst-qsort4.c
|
||||
index a7abaa1a37461666..4cf373f22e28fade 100644
|
||||
--- a/stdlib/tst-qsort4.c
|
||||
+++ b/stdlib/tst-qsort4.c
|
||||
@@ -30,35 +30,12 @@ cmp (const void *a1, const void *b1, void *closure)
|
||||
return *a - *b;
|
||||
}
|
||||
|
||||
-/* Wrapper around heapsort_r that set ups the required variables. */
|
||||
-static void
|
||||
-heapsort_wrapper (void *const pbase, size_t total_elems, size_t size,
|
||||
- __compar_d_fn_t cmp, void *arg)
|
||||
-{
|
||||
- char *base_ptr = (char *) pbase;
|
||||
- char *lo = base_ptr;
|
||||
- char *hi = &lo[size * (total_elems - 1)];
|
||||
-
|
||||
- if (total_elems <= 1)
|
||||
- /* Avoid lossage with unsigned arithmetic below. */
|
||||
- return;
|
||||
-
|
||||
- enum swap_type_t swap_type;
|
||||
- if (is_aligned (pbase, size, 8))
|
||||
- swap_type = SWAP_WORDS_64;
|
||||
- else if (is_aligned (pbase, size, 4))
|
||||
- swap_type = SWAP_WORDS_32;
|
||||
- else
|
||||
- swap_type = SWAP_BYTES;
|
||||
- heapsort_r (lo, hi, size, swap_type, cmp, arg);
|
||||
-}
|
||||
-
|
||||
static void
|
||||
check_one_sort (signed char *array, int length)
|
||||
{
|
||||
signed char *copy = xmalloc (length);
|
||||
memcpy (copy, array, length);
|
||||
- heapsort_wrapper (copy, length, 1, cmp, NULL);
|
||||
+ heapsort_r (copy, length - 1, 1, cmp, NULL);
|
||||
|
||||
/* Verify that the result is sorted. */
|
||||
for (int i = 1; i < length; ++i)
|
||||
diff --git a/stdlib/tst-qsort5.c b/stdlib/tst-qsort5.c
|
||||
deleted file mode 100644
|
||||
index d3a88c30f8ffb135..0000000000000000
|
||||
--- a/stdlib/tst-qsort5.c
|
||||
+++ /dev/null
|
||||
@@ -1,171 +0,0 @@
|
||||
-/* Adversarial test for qsort_r.
|
||||
- Copyright (C) 2023 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
|
||||
- <http://www.gnu.org/licenses/>. */
|
||||
-
|
||||
-/* The approach follows Douglas McIlroy, A Killer Adversary for
|
||||
- Quicksort. Software—Practice and Experience 29 (1999) 341-344.
|
||||
- Downloaded <http://www.cs.dartmouth.edu/~doug/mdmspe.pdf>
|
||||
- (2023-11-17). */
|
||||
-
|
||||
-#include <math.h>
|
||||
-#include <stdlib.h>
|
||||
-#include <stdio.h>
|
||||
-#include <support/check.h>
|
||||
-#include <support/support.h>
|
||||
-
|
||||
-struct context
|
||||
-{
|
||||
- /* Called the gas value in the paper. This value is larger than all
|
||||
- other values (length minus one will do), so comparison with any
|
||||
- decided value has a known result. */
|
||||
- int undecided_value;
|
||||
-
|
||||
- /* If comparing undecided values, one of them as to be assigned a
|
||||
- value to ensure consistency with future comparisons. This is the
|
||||
- value that will be used. Starts out at zero. */
|
||||
- int next_decided;
|
||||
-
|
||||
- /* Used to trick pivot selection. Deciding the value for the last
|
||||
- seen undcided value in a decided/undecided comparison happens
|
||||
- to trick the many qsort implementations. */
|
||||
- int last_undecided_index;
|
||||
-
|
||||
- /* This array contains the actually asigned values. The call to
|
||||
- qsort_r sorts a different array that contains indices into this
|
||||
- array. */
|
||||
- int *decided_values;
|
||||
-};
|
||||
-
|
||||
-static int
|
||||
-compare_opponent (const void *l1, const void *r1, void *ctx1)
|
||||
-{
|
||||
- const int *l = l1;
|
||||
- const int *r = r1;
|
||||
- struct context *ctx = ctx1;
|
||||
- int rvalue = ctx->decided_values[*r];
|
||||
- int lvalue = ctx->decided_values[*l];
|
||||
-
|
||||
- if (lvalue == ctx->undecided_value)
|
||||
- {
|
||||
- if (rvalue == ctx->undecided_value)
|
||||
- {
|
||||
- /* Both values are undecided. In this case, make a decision
|
||||
- for the last-used undecided value. This is tweak is very
|
||||
- specific to quicksort. */
|
||||
- if (*l == ctx->last_undecided_index)
|
||||
- {
|
||||
- ctx->decided_values[*l] = ctx->next_decided;
|
||||
- ++ctx->next_decided;
|
||||
- /* The undecided value or *r is greater. */
|
||||
- return -1;
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- ctx->decided_values[*r] = ctx->next_decided;
|
||||
- ++ctx->next_decided;
|
||||
- /* The undecided value for *l is greater. */
|
||||
- return 1;
|
||||
- }
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- ctx->last_undecided_index = *l;
|
||||
- return 1;
|
||||
- }
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- /* *l is a decided value. */
|
||||
- if (rvalue == ctx->undecided_value)
|
||||
- {
|
||||
- ctx->last_undecided_index = *r;
|
||||
- /* The undecided value for *r is greater. */
|
||||
- return -1;
|
||||
- }
|
||||
- else
|
||||
- return lvalue - rvalue;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-/* Return a pointer to the adversarial permutation of length N. */
|
||||
-static int *
|
||||
-create_permutation (size_t n)
|
||||
-{
|
||||
- struct context ctx =
|
||||
- {
|
||||
- .undecided_value = n - 1, /* Larger than all other values. */
|
||||
- .decided_values = xcalloc (n, sizeof (int)),
|
||||
- };
|
||||
- for (size_t i = 0; i < n; ++i)
|
||||
- ctx.decided_values[i] = ctx.undecided_value;
|
||||
- int *scratch = xcalloc (n, sizeof (int));
|
||||
- for (size_t i = 0; i < n; ++i)
|
||||
- scratch[i] = i;
|
||||
- qsort_r (scratch, n, sizeof (*scratch), compare_opponent, &ctx);
|
||||
- free (scratch);
|
||||
- return ctx.decided_values;
|
||||
-}
|
||||
-
|
||||
-/* Callback function for qsort which counts the number of invocations
|
||||
- in *CLOSURE. */
|
||||
-static int
|
||||
-compare_counter (const void *l1, const void *r1, void *closure)
|
||||
-{
|
||||
- const int *l = l1;
|
||||
- const int *r = r1;
|
||||
- unsigned long long int *counter = closure;
|
||||
- ++*counter;
|
||||
- return *l - *r;
|
||||
-}
|
||||
-
|
||||
-/* Count the comparisons required for an adversarial permutation of
|
||||
- length N. */
|
||||
-static unsigned long long int
|
||||
-count_comparisons (size_t n)
|
||||
-{
|
||||
- int *array = create_permutation (n);
|
||||
- unsigned long long int counter = 0;
|
||||
- qsort_r (array, n, sizeof (*array), compare_counter, &counter);
|
||||
- free (array);
|
||||
- return counter;
|
||||
-}
|
||||
-
|
||||
-/* Check the scaling factor for one adversarial permutation of length
|
||||
- N, and report some statistics. */
|
||||
-static void
|
||||
-check_one_n (size_t n)
|
||||
-{
|
||||
- unsigned long long int count = count_comparisons (n);
|
||||
- double factor = count / (n * log (count));
|
||||
- printf ("info: length %zu: %llu comparisons ~ %f * n * log (n)\n",
|
||||
- n, count, factor);
|
||||
- /* This is an arbitrary factor which is true for the current
|
||||
- implementation across a wide range of sizes. */
|
||||
- TEST_VERIFY (factor <= 4.5);
|
||||
-}
|
||||
-
|
||||
-static int
|
||||
-do_test (void)
|
||||
-{
|
||||
- check_one_n (100);
|
||||
- check_one_n (1000);
|
||||
- for (int i = 1; i <= 15; ++i)
|
||||
- check_one_n (i * 10 * 1000);
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-#include <support/test-driver.c>
|
||||
27
SOURCES/glibc-RHEL-24168-16.patch
Normal file
27
SOURCES/glibc-RHEL-24168-16.patch
Normal file
@ -0,0 +1,27 @@
|
||||
commit 74d2731a5fb2676b64092bc25e7f193db1b17b2b
|
||||
Author: Kuan-Wei Chiu <visitorckw@gmail.com>
|
||||
Date: Tue Jan 16 10:16:56 2024 +0800
|
||||
|
||||
stdlib: Fix heapsort for cases with exactly two elements
|
||||
|
||||
When malloc fails to allocate a buffer and falls back to heapsort, the
|
||||
current heapsort implementation does not perform sorting when there are
|
||||
exactly two elements. Heapsort is now skipped only when there is
|
||||
exactly one element.
|
||||
|
||||
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index b95889047ba31193..7b6c7e1f79974157 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -163,7 +163,7 @@ get_swap_type (void *const pbase, size_t size)
|
||||
static void
|
||||
heapsort_r (void *base, size_t n, size_t size, __compar_d_fn_t cmp, void *arg)
|
||||
{
|
||||
- if (n <= 1)
|
||||
+ if (n == 0)
|
||||
return;
|
||||
|
||||
enum swap_type_t swap_type = get_swap_type (base, size);
|
||||
27
SOURCES/glibc-RHEL-24168-17.patch
Normal file
27
SOURCES/glibc-RHEL-24168-17.patch
Normal file
@ -0,0 +1,27 @@
|
||||
commit 1bb28b7b4f01709b841c86850e1bb83b554feafe
|
||||
Author: Kuan-Wei Chiu <visitorckw@gmail.com>
|
||||
Date: Tue Jan 16 10:16:57 2024 +0800
|
||||
|
||||
stdlib: Verify heapsort for two-element cases
|
||||
|
||||
Adjust the testing approach to start from scenarios with only 2
|
||||
elements, as insertion sort no longer handles such cases.
|
||||
|
||||
Signed-off-by: Kuan-Wei Chiu <visitorckw@gmail.com>
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/stdlib/tst-qsort4.c b/stdlib/tst-qsort4.c
|
||||
index 4cf373f22e28fade..7909793d9eb3edc7 100644
|
||||
--- a/stdlib/tst-qsort4.c
|
||||
+++ b/stdlib/tst-qsort4.c
|
||||
@@ -96,9 +96,7 @@ do_test (void)
|
||||
check_one_sort ((signed char[16]) {15, 3, 4, 2, 1, 0, 8, 7, 6, 5, 14,
|
||||
13, 12, 11, 10, 9}, 16);
|
||||
|
||||
- /* Array lengths 2 and less are not handled by heapsort_r and
|
||||
- deferred to insertion sort. */
|
||||
- for (int i = 3; i <= 8; ++i)
|
||||
+ for (int i = 2; i <= 8; ++i)
|
||||
{
|
||||
signed char *buf = xmalloc (i);
|
||||
check_combinations (i, buf, 0);
|
||||
32
SOURCES/glibc-RHEL-24168-18.patch
Normal file
32
SOURCES/glibc-RHEL-24168-18.patch
Normal file
@ -0,0 +1,32 @@
|
||||
commit 31bd548650673e8b5ae1a31f1c596ff8305a5d4c
|
||||
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Date: Wed Jan 17 08:08:01 2024 -0300
|
||||
|
||||
stdlib: Remove unused is_aligned function from qsort.c
|
||||
|
||||
Checked on x86_64-linux-gnu.
|
||||
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index 7b6c7e1f79974157..8db8a81d182dd1fc 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -41,19 +41,6 @@ enum swap_type_t
|
||||
typedef uint32_t __attribute__ ((__may_alias__)) u32_alias_t;
|
||||
typedef uint64_t __attribute__ ((__may_alias__)) u64_alias_t;
|
||||
|
||||
-/* If this function returns true, elements can be safely copied using word
|
||||
- loads and stores. Otherwise, it might not be safe. BASE (as an integer)
|
||||
- must be a multiple of the word alignment. SIZE must be a multiple of
|
||||
- WORDSIZE. Since WORDSIZE must be a multiple of the word alignment, and
|
||||
- WORDSIZE is a power of two on all supported platforms, this function for
|
||||
- speed merely checks that BASE and SIZE are both multiples of the word
|
||||
- size. */
|
||||
-static inline bool
|
||||
-is_aligned (const void *base, size_t size, size_t wordsize)
|
||||
-{
|
||||
- return (((uintptr_t) base | size) & (wordsize - 1)) == 0;
|
||||
-}
|
||||
-
|
||||
static inline void
|
||||
swap_words_64 (void * restrict a, void * restrict b, size_t n)
|
||||
{
|
||||
51
SOURCES/glibc-RHEL-24168-19.patch
Normal file
51
SOURCES/glibc-RHEL-24168-19.patch
Normal file
@ -0,0 +1,51 @@
|
||||
commit dfa3394a605c8f6f25e4f827789bc89eca1d206c
|
||||
Author: Xi Ruoyao <xry111@xry111.site>
|
||||
Date: Tue Jan 23 04:29:18 2024 +0800
|
||||
|
||||
qsort: Fix a typo causing unnecessary malloc/free (BZ 31276)
|
||||
|
||||
In qsort_r we allocate a buffer sized QSORT_STACK_SIZE (1024) on stack
|
||||
and we intend to use it if all elements can fit into it. But there is a
|
||||
typo:
|
||||
|
||||
if (total_size < sizeof buf)
|
||||
buf = tmp;
|
||||
else
|
||||
/* allocate a buffer on heap and use it ... */
|
||||
|
||||
Here "buf" is a pointer, thus sizeof buf is just 4 or 8, instead of
|
||||
1024. There is also a minor issue that we should use "<=" instead of
|
||||
"<".
|
||||
|
||||
This bug is detected debugging some strange heap corruption running the
|
||||
Ruby-3.3.0 test suite (on an experimental Linux From Scratch build using
|
||||
Binutils-2.41.90 and Glibc trunk, and also Fedora Rawhide [1]). It
|
||||
seems Ruby is doing some wild "optimization" by jumping into somewhere
|
||||
in qsort_r instead of calling it normally, resulting in a double free of
|
||||
buf if we allocate it on heap. The issue can be reproduced
|
||||
deterministically with:
|
||||
|
||||
LD_PRELOAD=/usr/lib/libc_malloc_debug.so MALLOC_CHECK_=3 \
|
||||
LD_LIBRARY_PATH=. ./ruby test/runner.rb test/ruby/test_enum.rb
|
||||
|
||||
in Ruby-3.3.0 tree after building it. This change would hide the issue
|
||||
for Ruby, but Ruby is likely still buggy (if using this "optimization"
|
||||
sorting larger arrays).
|
||||
|
||||
[1]:https://kojipkgs.fedoraproject.org/work/tasks/9729/111889729/build.log
|
||||
|
||||
Signed-off-by: Xi Ruoyao <xry111@xry111.site>
|
||||
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index 8db8a81d182dd1fc..2cdd5c1fe790f55c 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -354,7 +354,7 @@ __qsort_r (void *const pbase, size_t total_elems, size_t size,
|
||||
if (size > INDIRECT_SORT_SIZE_THRES)
|
||||
total_size = 2 * total_elems * sizeof (void *) + size;
|
||||
|
||||
- if (total_size < sizeof buf)
|
||||
+ if (total_size <= sizeof tmp)
|
||||
buf = tmp;
|
||||
else
|
||||
{
|
||||
344
SOURCES/glibc-RHEL-24168-2.patch
Normal file
344
SOURCES/glibc-RHEL-24168-2.patch
Normal file
@ -0,0 +1,344 @@
|
||||
commit d275970ab56f8ba6a3ca598aba75db4daabe5924
|
||||
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Date: Fri Apr 8 09:57:57 2022 -0300
|
||||
|
||||
stdlib: Reflow and sort most variable assignments
|
||||
|
||||
diff --git a/stdlib/Makefile b/stdlib/Makefile
|
||||
index fe43bec0f9d581d5..03f8478c64408ed3 100644
|
||||
--- a/stdlib/Makefile
|
||||
+++ b/stdlib/Makefile
|
||||
@@ -22,49 +22,145 @@ subdir := stdlib
|
||||
|
||||
include ../Makeconfig
|
||||
|
||||
-headers := stdlib.h bits/stdlib.h bits/stdlib-ldbl.h bits/stdlib-float.h \
|
||||
- monetary.h bits/monetary-ldbl.h \
|
||||
- inttypes.h stdint.h bits/wordsize.h bits/timesize.h \
|
||||
- errno.h sys/errno.h bits/errno.h bits/types/error_t.h \
|
||||
- ucontext.h sys/ucontext.h bits/indirect-return.h \
|
||||
- alloca.h fmtmsg.h \
|
||||
- bits/stdlib-bsearch.h sys/random.h bits/stdint-intn.h \
|
||||
- bits/stdint-uintn.h bits/time64.h \
|
||||
-
|
||||
-routines := \
|
||||
- atof atoi atol atoll \
|
||||
- abort \
|
||||
- bsearch qsort msort \
|
||||
- getenv putenv setenv secure-getenv \
|
||||
- exit on_exit atexit cxa_atexit cxa_finalize old_atexit \
|
||||
- quick_exit at_quick_exit cxa_at_quick_exit cxa_thread_atexit_impl \
|
||||
- abs labs llabs \
|
||||
- div ldiv lldiv \
|
||||
- mblen mbstowcs mbtowc wcstombs wctomb \
|
||||
- random random_r rand rand_r \
|
||||
- drand48 erand48 lrand48 nrand48 mrand48 jrand48 \
|
||||
- srand48 seed48 lcong48 \
|
||||
- drand48_r erand48_r lrand48_r nrand48_r mrand48_r jrand48_r \
|
||||
- srand48_r seed48_r lcong48_r \
|
||||
- drand48-iter getrandom getentropy \
|
||||
- strfromf strfromd strfroml \
|
||||
- strtol strtoul strtoll strtoull \
|
||||
- strtol_l strtoul_l strtoll_l strtoull_l \
|
||||
- strtof strtod strtold \
|
||||
- strtof_l strtod_l strtold_l \
|
||||
- strtof_nan strtod_nan strtold_nan \
|
||||
- system canonicalize \
|
||||
- a64l l64a \
|
||||
- rpmatch strfmon strfmon_l getsubopt xpg_basename fmtmsg \
|
||||
- getcontext setcontext makecontext swapcontext
|
||||
-aux = grouping groupingwc tens_in_limb
|
||||
+headers := \
|
||||
+ alloca.h \
|
||||
+ bits/errno.h \
|
||||
+ bits/indirect-return.h \
|
||||
+ bits/monetary-ldbl.h \
|
||||
+ bits/stdint-intn.h \
|
||||
+ bits/stdint-uintn.h \
|
||||
+ bits/stdlib-bsearch.h \
|
||||
+ bits/stdlib-float.h \
|
||||
+ bits/stdlib.h \
|
||||
+ bits/stdlib-ldbl.h \
|
||||
+ bits/time64.h \
|
||||
+ bits/timesize.h \
|
||||
+ bits/types/error_t.h \
|
||||
+ bits/wordsize.h \
|
||||
+ errno.h \
|
||||
+ fmtmsg.h \
|
||||
+ inttypes.h \
|
||||
+ monetary.h \
|
||||
+ stdint.h \
|
||||
+ stdlib.h \
|
||||
+ sys/errno.h \
|
||||
+ sys/random.h \
|
||||
+ sys/ucontext.h \
|
||||
+ ucontext.h \
|
||||
+ # headers
|
||||
+
|
||||
+routines := \
|
||||
+ a64l \
|
||||
+ abort \
|
||||
+ abs \
|
||||
+ at_quick_exit \
|
||||
+ atof \
|
||||
+ atoi \
|
||||
+ atol\
|
||||
+ atoll \
|
||||
+ bsearch \
|
||||
+ canonicalize \
|
||||
+ cxa_at_quick_exit \
|
||||
+ cxa_atexit \
|
||||
+ cxa_finalize \
|
||||
+ cxa_thread_atexit_impl \
|
||||
+ div \
|
||||
+ drand48 \
|
||||
+ drand48-iter \
|
||||
+ drand48_r \
|
||||
+ erand48 \
|
||||
+ erand48_r \
|
||||
+ exit \
|
||||
+ fmtmsg \
|
||||
+ getcontext \
|
||||
+ getentropy \
|
||||
+ getenv \
|
||||
+ getrandom \
|
||||
+ getsubopt \
|
||||
+ jrand48 \
|
||||
+ jrand48_r \
|
||||
+ l64a \
|
||||
+ labs \
|
||||
+ lcong48 \
|
||||
+ lcong48_r \
|
||||
+ ldiv \
|
||||
+ llabs \
|
||||
+ lldiv \
|
||||
+ lrand48 \
|
||||
+ lrand48_r \
|
||||
+ makecontext \
|
||||
+ mblen \
|
||||
+ mbstowcs \
|
||||
+ mbtowc \
|
||||
+ mrand48 \
|
||||
+ mrand48_r \
|
||||
+ msort \
|
||||
+ nrand48 \
|
||||
+ nrand48_r \
|
||||
+ old_atexit \
|
||||
+ on_exit atexit \
|
||||
+ putenv \
|
||||
+ qsort \
|
||||
+ quick_exit \
|
||||
+ rand \
|
||||
+ rand_r \
|
||||
+ random \
|
||||
+ random_r \
|
||||
+ rpmatch \
|
||||
+ secure-getenv \
|
||||
+ seed48 \
|
||||
+ seed48_r \
|
||||
+ setcontext \
|
||||
+ setenv \
|
||||
+ srand48 \
|
||||
+ srand48_r \
|
||||
+ strfmon \
|
||||
+ strfmon_l \
|
||||
+ strfromd \
|
||||
+ strfromf \
|
||||
+ strfroml \
|
||||
+ strtod \
|
||||
+ strtod_l \
|
||||
+ strtod_nan \
|
||||
+ strtof \
|
||||
+ strtof_l \
|
||||
+ strtof_nan \
|
||||
+ strtol \
|
||||
+ strtol_l \
|
||||
+ strtold \
|
||||
+ strtold_l \
|
||||
+ strtold_nan \
|
||||
+ strtoll \
|
||||
+ strtoll_l \
|
||||
+ strtoul \
|
||||
+ strtoul_l \
|
||||
+ strtoull \
|
||||
+ strtoull_l \
|
||||
+ swapcontext \
|
||||
+ system \
|
||||
+ wcstombs \
|
||||
+ wctomb \
|
||||
+ xpg_basename \
|
||||
+ # routines
|
||||
+
|
||||
+aux = \
|
||||
+ grouping \
|
||||
+ groupingwc \
|
||||
+ tens_in_limb \
|
||||
+ # aux
|
||||
|
||||
# These routines will be omitted from the libc shared object.
|
||||
# Instead the static object files will be included in a special archive
|
||||
# linked against when the shared library will be used.
|
||||
-static-only-routines = atexit at_quick_exit
|
||||
+static-only-routines = \
|
||||
+ atexit \
|
||||
+ at_quick_exit \
|
||||
+ # static-only-routines
|
||||
+
|
||||
+test-srcs := \
|
||||
+ tst-fmtmsg \
|
||||
+ #test-srcs
|
||||
|
||||
-test-srcs := tst-fmtmsg
|
||||
tests := \
|
||||
bug-fmtmsg1 \
|
||||
bug-getcontext \
|
||||
@@ -155,15 +251,29 @@ tests := \
|
||||
tst-width \
|
||||
tst-width-stdint \
|
||||
tst-xpg-basename \
|
||||
-# tests
|
||||
+ # tests
|
||||
+
|
||||
+tests-internal := \
|
||||
+ tst-strtod1i \
|
||||
+ tst-strtod3 \
|
||||
+ tst-strtod4 \
|
||||
+ tst-strtod5i \
|
||||
+ tst-tls-atexit \
|
||||
+ tst-tls-atexit-nodelete \
|
||||
+ # tests-internal
|
||||
+
|
||||
+tests-static := \
|
||||
+ tst-secure-getenv \
|
||||
+ # tests-static
|
||||
|
||||
-tests-internal := tst-strtod1i tst-strtod3 tst-strtod4 tst-strtod5i \
|
||||
- tst-tls-atexit tst-tls-atexit-nodelete
|
||||
-tests-static := tst-secure-getenv
|
||||
-tests-container := tst-system
|
||||
+tests-container := \
|
||||
+ tst-system \
|
||||
+ #tests-container
|
||||
|
||||
ifeq ($(build-hardcoded-path-in-tests),yes)
|
||||
-tests += tst-empty-env
|
||||
+tests += \
|
||||
+ tst-empty-env \
|
||||
+ # tests
|
||||
endif
|
||||
|
||||
LDLIBS-test-atexit-race = $(shared-thread-library)
|
||||
@@ -188,30 +298,76 @@ CFLAGS-tst-thread-quick_exit.o = -std=c++11
|
||||
LDLIBS-tst-thread-quick_exit = -lstdc++
|
||||
$(objpfx)tst-thread-quick_exit: $(shared-thread-library)
|
||||
else
|
||||
-tests-unsupported += tst-quick_exit tst-thread-quick_exit
|
||||
+tests-unsupported += \
|
||||
+ tst-quick_exit \
|
||||
+ tst-thread-quick_exit \
|
||||
+ # tests-unsupported
|
||||
endif
|
||||
|
||||
-modules-names = tst-tls-atexit-lib test-dlclose-exit-race-helper
|
||||
+modules-names = \
|
||||
+ test-dlclose-exit-race-helper \
|
||||
+ tst-tls-atexit-lib \
|
||||
+ # modules-names
|
||||
extra-test-objs += $(addsuffix .os, $(modules-names))
|
||||
|
||||
ifeq ($(build-shared),yes)
|
||||
-tests += tst-putenv
|
||||
+tests += \
|
||||
+ tst-putenv \
|
||||
+ # tests
|
||||
endif
|
||||
|
||||
# Several mpn functions from GNU MP are used by the strtod function.
|
||||
-mpn-routines := inlines add_n addmul_1 cmp divmod_1 divrem udiv_qrnnd \
|
||||
- lshift rshift mod_1 mul mul_1 mul_n sub_n submul_1
|
||||
-mpn-headers = longlong.h gmp.h gmp-impl.h gmp-mparam.h asm-syntax.h
|
||||
-
|
||||
-routines := $(strip $(routines) $(mpn-routines)) \
|
||||
- dbl2mpn ldbl2mpn \
|
||||
- mpn2flt mpn2dbl mpn2ldbl
|
||||
-aux += fpioconst mp_clz_tab
|
||||
-
|
||||
-tests-extras += tst-putenvmod
|
||||
-extra-test-objs += tst-putenvmod.os
|
||||
-
|
||||
-generated += isomac isomac.out tst-putenvmod.so
|
||||
+mpn-routines := \
|
||||
+ add_n \
|
||||
+ addmul_1 \
|
||||
+ cmp \
|
||||
+ divmod_1 \
|
||||
+ divrem \
|
||||
+ inlines \
|
||||
+ lshift \
|
||||
+ mod_1 \
|
||||
+ mul \
|
||||
+ mul_1 \
|
||||
+ mul_n \
|
||||
+ rshift \
|
||||
+ sub_n \
|
||||
+ submul_1 \
|
||||
+ udiv_qrnnd \
|
||||
+ # mpn-routines
|
||||
+mpn-headers = \
|
||||
+ asm-syntax.h \
|
||||
+ gmp-impl.h \
|
||||
+ gmp-mparam.h \
|
||||
+ gmp.h \
|
||||
+ longlong.h \
|
||||
+ # mpn-headers
|
||||
+
|
||||
+routines := \
|
||||
+ $(strip $(routines) $(mpn-routines)) \
|
||||
+ dbl2mpn \
|
||||
+ ldbl2mpn \
|
||||
+ mpn2dbl \
|
||||
+ mpn2flt \
|
||||
+ mpn2ldbl \
|
||||
+ # routines
|
||||
+aux += \
|
||||
+ fpioconst \
|
||||
+ mp_clz_tab \
|
||||
+ # aux
|
||||
+
|
||||
+tests-extras += \
|
||||
+ tst-putenvmod \
|
||||
+ # tests-extras
|
||||
+
|
||||
+extra-test-objs += \
|
||||
+ tst-putenvmod.os \
|
||||
+ # extra-test-objs
|
||||
+
|
||||
+generated += \
|
||||
+ isomac \
|
||||
+ isomac.out \
|
||||
+ tst-putenvmod.so \
|
||||
+ # generated
|
||||
|
||||
CFLAGS-bsearch.c += $(uses-callbacks)
|
||||
CFLAGS-msort.c += $(uses-callbacks)
|
||||
@@ -247,9 +403,17 @@ endif
|
||||
include ../Rules
|
||||
|
||||
ifeq ($(run-built-tests),yes)
|
||||
-LOCALES := cs_CZ.UTF-8 de_DE.UTF-8 en_US.ISO-8859-1 tr_TR.UTF-8 \
|
||||
- tr_TR.ISO-8859-9 tg_TJ.UTF-8 hr_HR.UTF-8 hi_IN.UTF-8 \
|
||||
- el_GR.UTF-8
|
||||
+LOCALES := \
|
||||
+ cs_CZ.UTF-8 \
|
||||
+ de_DE.UTF-8 \
|
||||
+ el_GR.UTF-8 \
|
||||
+ en_US.ISO-8859-1 \
|
||||
+ hi_IN.UTF-8 \
|
||||
+ hr_HR.UTF-8 \
|
||||
+ tg_TJ.UTF-8 \
|
||||
+ tr_TR.ISO-8859-9 \
|
||||
+ tr_TR.UTF-8 \
|
||||
+ # LOCALES
|
||||
include ../gen-locales.mk
|
||||
|
||||
$(objpfx)bug-strtod2.out: $(gen-locales)
|
||||
65
SOURCES/glibc-RHEL-24168-20.patch
Normal file
65
SOURCES/glibc-RHEL-24168-20.patch
Normal file
@ -0,0 +1,65 @@
|
||||
commit e7b90e6e605cf236d4bd79e4930cd6a46f9932c7
|
||||
Author: Paul Eggert <eggert@cs.ucla.edu>
|
||||
Date: Thu Feb 1 11:52:46 2024 -0800
|
||||
|
||||
stdlib: fix qsort example in manual
|
||||
|
||||
* manual/search.texi (Comparison Functions, Array Sort Function):
|
||||
Sort an array of long ints, not doubles, to avoid hassles
|
||||
with NaNs.
|
||||
|
||||
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
|
||||
|
||||
diff --git a/manual/search.texi b/manual/search.texi
|
||||
index ffaadc46f51b18f9..db577a5332651c36 100644
|
||||
--- a/manual/search.texi
|
||||
+++ b/manual/search.texi
|
||||
@@ -35,19 +35,22 @@ second, zero if they are ``equal'', and positive if the first argument
|
||||
is ``greater''.
|
||||
|
||||
Here is an example of a comparison function which works with an array of
|
||||
-numbers of type @code{double}:
|
||||
+numbers of type @code{long int}:
|
||||
|
||||
@smallexample
|
||||
int
|
||||
-compare_doubles (const void *a, const void *b)
|
||||
+compare_long_ints (const void *a, const void *b)
|
||||
@{
|
||||
- const double *da = (const double *) a;
|
||||
- const double *db = (const double *) b;
|
||||
+ const long int *la = a;
|
||||
+ const long int *lb = b;
|
||||
|
||||
- return (*da > *db) - (*da < *db);
|
||||
+ return (*la > *lb) - (*la < *lb);
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
+(The code would have to be more complicated for an array of @code{double},
|
||||
+to handle NaNs correctly.)
|
||||
+
|
||||
The header file @file{stdlib.h} defines a name for the data type of
|
||||
comparison functions. This type is a GNU extension.
|
||||
|
||||
@@ -183,16 +186,16 @@ in the array before making some comparisons. The only way to perform
|
||||
a stable sort with @code{qsort} is to first augment the objects with a
|
||||
monotonic counter of some kind.
|
||||
|
||||
-Here is a simple example of sorting an array of doubles in numerical
|
||||
+Here is a simple example of sorting an array of @code{long int} in numerical
|
||||
order, using the comparison function defined above (@pxref{Comparison
|
||||
Functions}):
|
||||
|
||||
@smallexample
|
||||
@{
|
||||
- double *array;
|
||||
- int size;
|
||||
+ long int *array;
|
||||
+ size_t nmemb;
|
||||
@dots{}
|
||||
- qsort (array, size, sizeof (double), compare_doubles);
|
||||
+ qsort (array, nmemb, sizeof *array, compare_long_ints);
|
||||
@}
|
||||
@end smallexample
|
||||
|
||||
142
SOURCES/glibc-RHEL-24168-21.patch
Normal file
142
SOURCES/glibc-RHEL-24168-21.patch
Normal file
@ -0,0 +1,142 @@
|
||||
commit 57581acd9559217e859fdac693145ce6399f4d70
|
||||
Author: Paul Eggert <eggert@cs.ucla.edu>
|
||||
Date: Sat Apr 6 08:44:01 2024 -0700
|
||||
|
||||
Fix bsearch, qsort doc to match POSIX better
|
||||
|
||||
* manual/search.texi (Array Search Function):
|
||||
Correct the statement about lfind’s mean runtime:
|
||||
it is proportional to a number (not that number),
|
||||
and this is true only if random elements are searched for.
|
||||
Relax the constraint on bsearch’s array argument:
|
||||
POSIX says it need not be sorted, only partially sorted.
|
||||
Say that the first arg passed to bsearch’s comparison function
|
||||
is the key, and the second arg is an array element, as
|
||||
POSIX requires. For bsearch and qsort, say that the
|
||||
comparison function should not alter the array, as POSIX
|
||||
requires. For qsort, say that the comparison function
|
||||
must define a total order, as POSIX requires, that
|
||||
it should not depend on element addresses, that
|
||||
the original array index can be used for stable sorts,
|
||||
and that if qsort still works if memory allocation fails.
|
||||
Be more consistent in calling the array elements
|
||||
“elements” rather than “objects”.
|
||||
|
||||
Co-authored-by: Zack Weinberg <zack@owlfolio.org>
|
||||
|
||||
diff --git a/manual/search.texi b/manual/search.texi
|
||||
index db577a5332651c36..cb08c494092ef77f 100644
|
||||
--- a/manual/search.texi
|
||||
+++ b/manual/search.texi
|
||||
@@ -84,8 +84,9 @@ The return value is a pointer to the matching element in the array
|
||||
starting at @var{base} if it is found. If no matching element is
|
||||
available @code{NULL} is returned.
|
||||
|
||||
-The mean runtime of this function is @code{*@var{nmemb}}/2. This
|
||||
-function should only be used if elements often get added to or deleted from
|
||||
+The mean runtime of this function is proportional to @code{*@var{nmemb}/2},
|
||||
+assuming random elements of the array are searched for. This
|
||||
+function should be used only if elements often get added to or deleted from
|
||||
the array in which case it might not be useful to sort the array before
|
||||
searching.
|
||||
@end deftypefun
|
||||
@@ -122,26 +123,34 @@ bytes. If one is sure the element is in the array it is better to use
|
||||
calling @code{lsearch}.
|
||||
@end deftypefun
|
||||
|
||||
-To search a sorted array for an element matching the key, use the
|
||||
-@code{bsearch} function. The prototype for this function is in
|
||||
+To search a sorted or partially sorted array for an element matching the key,
|
||||
+use the @code{bsearch} function. The prototype for this function is in
|
||||
the header file @file{stdlib.h}.
|
||||
@pindex stdlib.h
|
||||
|
||||
@deftypefun {void *} bsearch (const void *@var{key}, const void *@var{array}, size_t @var{count}, size_t @var{size}, comparison_fn_t @var{compare})
|
||||
@standards{ISO, stdlib.h}
|
||||
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
|
||||
-The @code{bsearch} function searches the sorted array @var{array} for an object
|
||||
+The @code{bsearch} function searches @var{array} for an element
|
||||
that is equivalent to @var{key}. The array contains @var{count} elements,
|
||||
each of which is of size @var{size} bytes.
|
||||
|
||||
The @var{compare} function is used to perform the comparison. This
|
||||
-function is called with two pointer arguments and should return an
|
||||
+function is called with arguments that point to the key and to an
|
||||
+array element, in that order, and should return an
|
||||
integer less than, equal to, or greater than zero corresponding to
|
||||
-whether its first argument is considered less than, equal to, or greater
|
||||
-than its second argument. The elements of the @var{array} must already
|
||||
-be sorted in ascending order according to this comparison function.
|
||||
-
|
||||
-The return value is a pointer to the matching array element, or a null
|
||||
+whether the key is considered less than, equal to, or greater than
|
||||
+the array element. The function should not alter the array's contents,
|
||||
+and the same array element should always compare the same way with the key.
|
||||
+
|
||||
+Although the array need not be completely sorted, it should be
|
||||
+partially sorted with respect to @var{key}. That is, the array should
|
||||
+begin with elements that compare less than @var{key}, followed by
|
||||
+elements that compare equal to @var{key}, and ending with elements
|
||||
+that compare greater than @var{key}. Any or all of these element
|
||||
+sequences can be empty.
|
||||
+
|
||||
+The return value is a pointer to a matching array element, or a null
|
||||
pointer if no match is found. If the array contains more than one element
|
||||
that matches, the one that is returned is unspecified.
|
||||
|
||||
@@ -171,20 +180,22 @@ array elements. This function is called with two pointer arguments and
|
||||
should return an integer less than, equal to, or greater than zero
|
||||
corresponding to whether its first argument is considered less than,
|
||||
equal to, or greater than its second argument.
|
||||
+The function must not alter the array's contents, and must define a
|
||||
+total ordering on the array elements, including any unusual values
|
||||
+such as floating-point NaN (@pxref{Infinity and NaN}).
|
||||
+Because the sorting process can move elements,
|
||||
+the function's return value must not depend on the element addresses
|
||||
+or the relative positions of elements within the array,
|
||||
+as these are meaningless while @code{qsort} is running.
|
||||
|
||||
@cindex stable sorting
|
||||
-@strong{Warning:} If two objects compare as equal, their order after
|
||||
+@strong{Warning:} If two elements compare equal, their order after
|
||||
sorting is unpredictable. That is to say, the sorting is not stable.
|
||||
This can make a difference when the comparison considers only part of
|
||||
-the elements. Two elements with the same sort key may differ in other
|
||||
-respects.
|
||||
-
|
||||
-Although the object addresses passed to the comparison function lie
|
||||
-within the array, they need not correspond with the original locations
|
||||
-of those objects because the sorting algorithm may swap around objects
|
||||
-in the array before making some comparisons. The only way to perform
|
||||
-a stable sort with @code{qsort} is to first augment the objects with a
|
||||
-monotonic counter of some kind.
|
||||
+the elements and two elements that compare equal may differ in other
|
||||
+respects. To ensure a stable sort in this situation, you can augment
|
||||
+each element with an appropriate tie-breaking value, such as its
|
||||
+original array index.
|
||||
|
||||
Here is a simple example of sorting an array of @code{long int} in numerical
|
||||
order, using the comparison function defined above (@pxref{Comparison
|
||||
@@ -202,18 +213,19 @@ Functions}):
|
||||
The @code{qsort} function derives its name from the fact that it was
|
||||
originally implemented using the ``quick sort'' algorithm.
|
||||
|
||||
-The implementation of @code{qsort} attempts to allocate auxiliary storage
|
||||
+The implementation of @code{qsort} attempts to allocate auxiliary memory
|
||||
and use the merge sort algorithm, without violating C standard requirement
|
||||
that arguments passed to the comparison function point within the array.
|
||||
+If the memory allocation fails, @code{qsort} resorts to a slower algorithm.
|
||||
@end deftypefun
|
||||
|
||||
@node Search/Sort Example
|
||||
@section Searching and Sorting Example
|
||||
|
||||
Here is an example showing the use of @code{qsort} and @code{bsearch}
|
||||
-with an array of structures. The objects in the array are sorted
|
||||
+with an array of structures. The elements of the array are sorted
|
||||
by comparing their @code{name} fields with the @code{strcmp} function.
|
||||
-Then, we can look up individual objects based on their names.
|
||||
+Then, we can look up individual elements based on their names.
|
||||
|
||||
@comment This example is dedicated to the memory of Jim Henson. RIP.
|
||||
@smallexample
|
||||
26
SOURCES/glibc-RHEL-24168-22.patch
Normal file
26
SOURCES/glibc-RHEL-24168-22.patch
Normal file
@ -0,0 +1,26 @@
|
||||
commit 7eed691cc2b6c5dbb6066ee1251606a744c7f05c
|
||||
Author: Arjun Shankar <arjun@redhat.com>
|
||||
Date: Wed Jul 2 17:11:32 2025 +0200
|
||||
|
||||
stdlib/Makefile: Remove deleted test's libm dependency
|
||||
|
||||
tst-qsort5 was deleted in 709fbd3ec3595f2d1076b4fec09a739327459288.
|
||||
Therefore remove its redundant libm dependency.
|
||||
|
||||
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
Conflicts:
|
||||
stdlib/Makefile: Context line mismatch due to missing tests.
|
||||
|
||||
diff --git a/stdlib/Makefile b/stdlib/Makefile
|
||||
index a9d91a57c08ac506..e517e306b868c432 100644
|
||||
--- a/stdlib/Makefile
|
||||
+++ b/stdlib/Makefile
|
||||
@@ -484,7 +484,6 @@ $(objpfx)tst-setcontext3.out: tst-setcontext3.sh $(objpfx)tst-setcontext3
|
||||
$(common-objpfx)stdlib/; \
|
||||
$(evaluate-test)
|
||||
|
||||
-$(objpfx)tst-qsort5: $(libm)
|
||||
$(objpfx)tst-getenv-signal: $(shared-thread-library)
|
||||
$(objpfx)tst-getenv-thread: $(shared-thread-library)
|
||||
$(objpfx)tst-getenv-unsetenv: $(shared-thread-library)
|
||||
286
SOURCES/glibc-RHEL-24168-3.patch
Normal file
286
SOURCES/glibc-RHEL-24168-3.patch
Normal file
@ -0,0 +1,286 @@
|
||||
commit fccf38c51746e0817c2409bb361398f9465e0760
|
||||
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Date: Tue Oct 3 09:22:45 2023 -0300
|
||||
|
||||
string: Add internal memswap implementation
|
||||
|
||||
The prototype is:
|
||||
|
||||
void __memswap (void *restrict p1, void *restrict p2, size_t n)
|
||||
|
||||
The function swaps the content of two memory blocks P1 and P2 of
|
||||
len N. Memory overlap is NOT handled.
|
||||
|
||||
It will be used on qsort optimization.
|
||||
|
||||
Checked on x86_64-linux-gnu and aarch64-linux-gnu.
|
||||
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
|
||||
|
||||
diff --git a/string/Makefile b/string/Makefile
|
||||
index 3e4331113f08424c..3ebf7597aad75bfe 100644
|
||||
--- a/string/Makefile
|
||||
+++ b/string/Makefile
|
||||
@@ -66,6 +66,18 @@ tests := tester inl-tester noinl-tester testcopy test-ffs \
|
||||
test-sig_np tst-strerror-fail \
|
||||
test-strdup test-strndup
|
||||
|
||||
+tests-static-internal := \
|
||||
+ test-memswap \
|
||||
+# tests-static-internal
|
||||
+
|
||||
+tests-internal := \
|
||||
+ $(tests-static-internal) \
|
||||
+ # tests-internal
|
||||
+
|
||||
+tests-static := \
|
||||
+ $(tests-static-internal) \
|
||||
+ # tests-static
|
||||
+
|
||||
# Both tests require the .mo translation files generated by msgfmt.
|
||||
tests-translation := tst-strsignal \
|
||||
tst-strerror
|
||||
diff --git a/string/test-memswap.c b/string/test-memswap.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..162beb91e3e96c23
|
||||
--- /dev/null
|
||||
+++ b/string/test-memswap.c
|
||||
@@ -0,0 +1,192 @@
|
||||
+/* Test and measure memcpy functions.
|
||||
+ Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <string.h>
|
||||
+#include <support/check.h>
|
||||
+#include <memswap.h>
|
||||
+
|
||||
+#define TEST_MAIN
|
||||
+#define BUF1PAGES 3
|
||||
+#include "test-string.h"
|
||||
+
|
||||
+static unsigned char *ref1;
|
||||
+static unsigned char *ref2;
|
||||
+
|
||||
+static void
|
||||
+do_one_test (unsigned char *p1, unsigned char *ref1, unsigned char *p2,
|
||||
+ unsigned char *ref2, size_t len)
|
||||
+{
|
||||
+ __memswap (p1, p2, len);
|
||||
+
|
||||
+ TEST_COMPARE_BLOB (p1, len, ref2, len);
|
||||
+ TEST_COMPARE_BLOB (p2, len, ref1, len);
|
||||
+}
|
||||
+
|
||||
+static inline void
|
||||
+do_test (size_t align1, size_t align2, size_t len)
|
||||
+{
|
||||
+ align1 &= page_size;
|
||||
+ if (align1 + len >= page_size)
|
||||
+ return;
|
||||
+
|
||||
+ align2 &= page_size;
|
||||
+ if (align2 + len >= page_size)
|
||||
+ return;
|
||||
+
|
||||
+ unsigned char *p1 = buf1 + align1;
|
||||
+ unsigned char *p2 = buf2 + align2;
|
||||
+ for (size_t repeats = 0; repeats < 2; ++repeats)
|
||||
+ {
|
||||
+ size_t i, j;
|
||||
+ for (i = 0, j = 1; i < len; i++, j += 23)
|
||||
+ {
|
||||
+ ref1[i] = p1[i] = j;
|
||||
+ ref2[i] = p2[i] = UCHAR_MAX - j;
|
||||
+ }
|
||||
+
|
||||
+ do_one_test (p1, ref1, p2, ref2, len);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+do_random_tests (void)
|
||||
+{
|
||||
+ for (size_t n = 0; n < ITERATIONS; n++)
|
||||
+ {
|
||||
+ size_t len, size, size1, size2, align1, align2;
|
||||
+
|
||||
+ if (n == 0)
|
||||
+ {
|
||||
+ len = getpagesize ();
|
||||
+ size = len + 512;
|
||||
+ size1 = size;
|
||||
+ size2 = size;
|
||||
+ align1 = 512;
|
||||
+ align2 = 512;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ if ((random () & 255) == 0)
|
||||
+ size = 65536;
|
||||
+ else
|
||||
+ size = 768;
|
||||
+ if (size > page_size)
|
||||
+ size = page_size;
|
||||
+ size1 = size;
|
||||
+ size2 = size;
|
||||
+ size_t i = random ();
|
||||
+ if (i & 3)
|
||||
+ size -= 256;
|
||||
+ if (i & 1)
|
||||
+ size1 -= 256;
|
||||
+ if (i & 2)
|
||||
+ size2 -= 256;
|
||||
+ if (i & 4)
|
||||
+ {
|
||||
+ len = random () % size;
|
||||
+ align1 = size1 - len - (random () & 31);
|
||||
+ align2 = size2 - len - (random () & 31);
|
||||
+ if (align1 > size1)
|
||||
+ align1 = 0;
|
||||
+ if (align2 > size2)
|
||||
+ align2 = 0;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ align1 = random () & 63;
|
||||
+ align2 = random () & 63;
|
||||
+ len = random () % size;
|
||||
+ if (align1 + len > size1)
|
||||
+ align1 = size1 - len;
|
||||
+ if (align2 + len > size2)
|
||||
+ align2 = size2 - len;
|
||||
+ }
|
||||
+ }
|
||||
+ unsigned char *p1 = buf1 + page_size - size1;
|
||||
+ unsigned char *p2 = buf2 + page_size - size2;
|
||||
+ size_t j = align1 + len + 256;
|
||||
+ if (j > size1)
|
||||
+ j = size1;
|
||||
+ for (size_t i = 0; i < j; ++i)
|
||||
+ ref1[i] = p1[i] = random () & 255;
|
||||
+
|
||||
+ j = align2 + len + 256;
|
||||
+ if (j > size2)
|
||||
+ j = size2;
|
||||
+
|
||||
+ for (size_t i = 0; i < j; ++i)
|
||||
+ ref2[i] = p2[i] = random () & 255;
|
||||
+
|
||||
+ do_one_test (p1 + align1, ref1 + align1, p2 + align2, ref2 + align2, len);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+test_main (void)
|
||||
+{
|
||||
+ test_init ();
|
||||
+ /* Use the start of buf1 for reference buffers. */
|
||||
+ ref1 = buf1;
|
||||
+ ref2 = buf1 + page_size;
|
||||
+ buf1 = ref2 + page_size;
|
||||
+
|
||||
+ printf ("%23s", "");
|
||||
+ printf ("\t__memswap\n");
|
||||
+
|
||||
+ for (size_t i = 0; i < 18; ++i)
|
||||
+ {
|
||||
+ do_test (0, 0, 1 << i);
|
||||
+ do_test (i, 0, 1 << i);
|
||||
+ do_test (0, i, 1 << i);
|
||||
+ do_test (i, i, 1 << i);
|
||||
+ }
|
||||
+
|
||||
+ for (size_t i = 0; i < 32; ++i)
|
||||
+ {
|
||||
+ do_test (0, 0, i);
|
||||
+ do_test (i, 0, i);
|
||||
+ do_test (0, i, i);
|
||||
+ do_test (i, i, i);
|
||||
+ }
|
||||
+
|
||||
+ for (size_t i = 3; i < 32; ++i)
|
||||
+ {
|
||||
+ if ((i & (i - 1)) == 0)
|
||||
+ continue;
|
||||
+ do_test (0, 0, 16 * i);
|
||||
+ do_test (i, 0, 16 * i);
|
||||
+ do_test (0, i, 16 * i);
|
||||
+ do_test (i, i, 16 * i);
|
||||
+ }
|
||||
+
|
||||
+ for (size_t i = 19; i <= 25; ++i)
|
||||
+ {
|
||||
+ do_test (255, 0, 1 << i);
|
||||
+ do_test (0, 4000, 1 << i);
|
||||
+ do_test (0, 255, i);
|
||||
+ do_test (0, 4000, i);
|
||||
+ }
|
||||
+
|
||||
+ do_test (0, 0, getpagesize ());
|
||||
+
|
||||
+ do_random_tests ();
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
diff --git a/sysdeps/generic/memswap.h b/sysdeps/generic/memswap.h
|
||||
new file mode 100644
|
||||
index 0000000000000000..f09dae1ebbc2ec0f
|
||||
--- /dev/null
|
||||
+++ b/sysdeps/generic/memswap.h
|
||||
@@ -0,0 +1,41 @@
|
||||
+/* Swap the content of two memory blocks, overlap is NOT handled.
|
||||
+ Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <string.h>
|
||||
+
|
||||
+static inline void
|
||||
+__memswap (void *__restrict p1, void *__restrict p2, size_t n)
|
||||
+{
|
||||
+ /* Use multiple small memcpys with constant size to enable inlining on most
|
||||
+ targets. */
|
||||
+ enum { SWAP_GENERIC_SIZE = 32 };
|
||||
+ unsigned char tmp[SWAP_GENERIC_SIZE];
|
||||
+ while (n > SWAP_GENERIC_SIZE)
|
||||
+ {
|
||||
+ memcpy (tmp, p1, SWAP_GENERIC_SIZE);
|
||||
+ p1 = __mempcpy (p1, p2, SWAP_GENERIC_SIZE);
|
||||
+ p2 = __mempcpy (p2, tmp, SWAP_GENERIC_SIZE);
|
||||
+ n -= SWAP_GENERIC_SIZE;
|
||||
+ }
|
||||
+ while (n > 0)
|
||||
+ {
|
||||
+ unsigned char t = ((unsigned char *)p1)[--n];
|
||||
+ ((unsigned char *)p1)[n] = ((unsigned char *)p2)[n];
|
||||
+ ((unsigned char *)p2)[n] = t;
|
||||
+ }
|
||||
+}
|
||||
157
SOURCES/glibc-RHEL-24168-4.patch
Normal file
157
SOURCES/glibc-RHEL-24168-4.patch
Normal file
@ -0,0 +1,157 @@
|
||||
commit 21d30c774c7f9f5878f0bf9438736c702b0a58a3
|
||||
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Date: Tue Oct 3 09:22:46 2023 -0300
|
||||
|
||||
stdlib: Optimization qsort{_r} swap implementation
|
||||
|
||||
The optimization takes in consideration both the most common elements
|
||||
are either 32 or 64 bit in size and inputs are aligned to the word
|
||||
boundary. This is similar to what msort does.
|
||||
|
||||
For large buffer the swap operation uses memcpy/mempcpy with a
|
||||
small fixed size buffer (so compiler might inline the operations).
|
||||
|
||||
Checked on x86_64-linux-gnu.
|
||||
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
|
||||
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index 23f2d283147073ac..59b220ba1c375ca3 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -22,22 +22,73 @@
|
||||
|
||||
#include <alloca.h>
|
||||
#include <limits.h>
|
||||
+#include <memswap.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
+#include <stdbool.h>
|
||||
|
||||
-/* Byte-wise swap two items of size SIZE. */
|
||||
-#define SWAP(a, b, size) \
|
||||
- do \
|
||||
- { \
|
||||
- size_t __size = (size); \
|
||||
- char *__a = (a), *__b = (b); \
|
||||
- do \
|
||||
- { \
|
||||
- char __tmp = *__a; \
|
||||
- *__a++ = *__b; \
|
||||
- *__b++ = __tmp; \
|
||||
- } while (--__size > 0); \
|
||||
- } while (0)
|
||||
+/* Swap SIZE bytes between addresses A and B. These helpers are provided
|
||||
+ along the generic one as an optimization. */
|
||||
+
|
||||
+enum swap_type_t
|
||||
+ {
|
||||
+ SWAP_WORDS_64,
|
||||
+ SWAP_WORDS_32,
|
||||
+ SWAP_BYTES
|
||||
+ };
|
||||
+
|
||||
+/* If this function returns true, elements can be safely copied using word
|
||||
+ loads and stores. Otherwise, it might not be safe. BASE (as an integer)
|
||||
+ must be a multiple of the word alignment. SIZE must be a multiple of
|
||||
+ WORDSIZE. Since WORDSIZE must be a multiple of the word alignment, and
|
||||
+ WORDSIZE is a power of two on all supported platforms, this function for
|
||||
+ speed merely checks that BASE and SIZE are both multiples of the word
|
||||
+ size. */
|
||||
+static inline bool
|
||||
+is_aligned (const void *base, size_t size, size_t wordsize)
|
||||
+{
|
||||
+ return (((uintptr_t) base | size) & (wordsize - 1)) == 0;
|
||||
+}
|
||||
+
|
||||
+static inline void
|
||||
+swap_words_64 (void * restrict a, void * restrict b, size_t n)
|
||||
+{
|
||||
+ typedef uint64_t __attribute__ ((__may_alias__)) u64_alias_t;
|
||||
+ do
|
||||
+ {
|
||||
+ n -= 8;
|
||||
+ u64_alias_t t = *(u64_alias_t *)(a + n);
|
||||
+ *(u64_alias_t *)(a + n) = *(u64_alias_t *)(b + n);
|
||||
+ *(u64_alias_t *)(b + n) = t;
|
||||
+ } while (n);
|
||||
+}
|
||||
+
|
||||
+static inline void
|
||||
+swap_words_32 (void * restrict a, void * restrict b, size_t n)
|
||||
+{
|
||||
+ typedef uint32_t __attribute__ ((__may_alias__)) u32_alias_t;
|
||||
+ do
|
||||
+ {
|
||||
+ n -= 4;
|
||||
+ u32_alias_t t = *(u32_alias_t *)(a + n);
|
||||
+ *(u32_alias_t *)(a + n) = *(u32_alias_t *)(b + n);
|
||||
+ *(u32_alias_t *)(b + n) = t;
|
||||
+ } while (n);
|
||||
+}
|
||||
+
|
||||
+/* Replace the indirect call with a serie of if statements. It should help
|
||||
+ the branch predictor. */
|
||||
+static void
|
||||
+do_swap (void * restrict a, void * restrict b, size_t size,
|
||||
+ enum swap_type_t swap_type)
|
||||
+{
|
||||
+ if (swap_type == SWAP_WORDS_64)
|
||||
+ swap_words_64 (a, b, size);
|
||||
+ else if (swap_type == SWAP_WORDS_32)
|
||||
+ swap_words_32 (a, b, size);
|
||||
+ else
|
||||
+ __memswap (a, b, size);
|
||||
+}
|
||||
|
||||
/* Discontinue quicksort algorithm when partition gets below this size.
|
||||
This particular magic number was chosen to work best on a Sun 4/260. */
|
||||
@@ -97,6 +148,14 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
/* Avoid lossage with unsigned arithmetic below. */
|
||||
return;
|
||||
|
||||
+ enum swap_type_t swap_type;
|
||||
+ if (is_aligned (pbase, size, 8))
|
||||
+ swap_type = SWAP_WORDS_64;
|
||||
+ else if (is_aligned (pbase, size, 4))
|
||||
+ swap_type = SWAP_WORDS_32;
|
||||
+ else
|
||||
+ swap_type = SWAP_BYTES;
|
||||
+
|
||||
if (total_elems > MAX_THRESH)
|
||||
{
|
||||
char *lo = base_ptr;
|
||||
@@ -120,13 +179,13 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
char *mid = lo + size * ((hi - lo) / size >> 1);
|
||||
|
||||
if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
|
||||
- SWAP (mid, lo, size);
|
||||
+ do_swap (mid, lo, size, swap_type);
|
||||
if ((*cmp) ((void *) hi, (void *) mid, arg) < 0)
|
||||
- SWAP (mid, hi, size);
|
||||
+ do_swap (mid, hi, size, swap_type);
|
||||
else
|
||||
goto jump_over;
|
||||
if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
|
||||
- SWAP (mid, lo, size);
|
||||
+ do_swap (mid, lo, size, swap_type);
|
||||
jump_over:;
|
||||
|
||||
left_ptr = lo + size;
|
||||
@@ -145,7 +204,7 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
|
||||
if (left_ptr < right_ptr)
|
||||
{
|
||||
- SWAP (left_ptr, right_ptr, size);
|
||||
+ do_swap (left_ptr, right_ptr, size, swap_type);
|
||||
if (mid == left_ptr)
|
||||
mid = right_ptr;
|
||||
else if (mid == right_ptr)
|
||||
@@ -217,7 +276,7 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
tmp_ptr = run_ptr;
|
||||
|
||||
if (tmp_ptr != base_ptr)
|
||||
- SWAP (tmp_ptr, base_ptr, size);
|
||||
+ do_swap (tmp_ptr, base_ptr, size, swap_type);
|
||||
|
||||
/* Insertion sort, running from left-hand-side up to right-hand-side. */
|
||||
|
||||
125
SOURCES/glibc-RHEL-24168-5.patch
Normal file
125
SOURCES/glibc-RHEL-24168-5.patch
Normal file
@ -0,0 +1,125 @@
|
||||
commit a035a9857e11faf16ed021b5e80faf215262afd1
|
||||
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Date: Tue Oct 3 09:22:47 2023 -0300
|
||||
|
||||
stdlib: Move insertion sort out qsort
|
||||
|
||||
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
|
||||
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index 59b220ba1c375ca3..35020e4c00e5fce3 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -112,6 +112,58 @@ typedef struct
|
||||
#define STACK_NOT_EMPTY (stack < top)
|
||||
|
||||
|
||||
+static inline void
|
||||
+insertion_sort_qsort_partitions (void *const pbase, size_t total_elems,
|
||||
+ size_t size, enum swap_type_t swap_type,
|
||||
+ __compar_d_fn_t cmp, void *arg)
|
||||
+{
|
||||
+ char *base_ptr = (char *) pbase;
|
||||
+ char *const end_ptr = &base_ptr[size * (total_elems - 1)];
|
||||
+ char *tmp_ptr = base_ptr;
|
||||
+#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
+ const size_t max_thresh = MAX_THRESH * size;
|
||||
+ char *thresh = min(end_ptr, base_ptr + max_thresh);
|
||||
+ char *run_ptr;
|
||||
+
|
||||
+ /* Find smallest element in first threshold and place it at the
|
||||
+ array's beginning. This is the smallest array element,
|
||||
+ and the operation speeds up insertion sort's inner loop. */
|
||||
+
|
||||
+ for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
|
||||
+ if (cmp (run_ptr, tmp_ptr, arg) < 0)
|
||||
+ tmp_ptr = run_ptr;
|
||||
+
|
||||
+ if (tmp_ptr != base_ptr)
|
||||
+ do_swap (tmp_ptr, base_ptr, size, swap_type);
|
||||
+
|
||||
+ /* Insertion sort, running from left-hand-side up to right-hand-side. */
|
||||
+
|
||||
+ run_ptr = base_ptr + size;
|
||||
+ while ((run_ptr += size) <= end_ptr)
|
||||
+ {
|
||||
+ tmp_ptr = run_ptr - size;
|
||||
+ while (cmp (run_ptr, tmp_ptr, arg) < 0)
|
||||
+ tmp_ptr -= size;
|
||||
+
|
||||
+ tmp_ptr += size;
|
||||
+ if (tmp_ptr != run_ptr)
|
||||
+ {
|
||||
+ char *trav;
|
||||
+
|
||||
+ trav = run_ptr + size;
|
||||
+ while (--trav >= run_ptr)
|
||||
+ {
|
||||
+ char c = *trav;
|
||||
+ char *hi, *lo;
|
||||
+
|
||||
+ for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
|
||||
+ *hi = *lo;
|
||||
+ *hi = c;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/* Order size using quicksort. This implementation incorporates
|
||||
four optimizations discussed in Sedgewick:
|
||||
|
||||
@@ -258,51 +310,6 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
for partitions below MAX_THRESH size. BASE_PTR points to the beginning
|
||||
of the array to sort, and END_PTR points at the very last element in
|
||||
the array (*not* one beyond it!). */
|
||||
-
|
||||
-#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
-
|
||||
- {
|
||||
- char *const end_ptr = &base_ptr[size * (total_elems - 1)];
|
||||
- char *tmp_ptr = base_ptr;
|
||||
- char *thresh = min(end_ptr, base_ptr + max_thresh);
|
||||
- char *run_ptr;
|
||||
-
|
||||
- /* Find smallest element in first threshold and place it at the
|
||||
- array's beginning. This is the smallest array element,
|
||||
- and the operation speeds up insertion sort's inner loop. */
|
||||
-
|
||||
- for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
|
||||
- if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0)
|
||||
- tmp_ptr = run_ptr;
|
||||
-
|
||||
- if (tmp_ptr != base_ptr)
|
||||
- do_swap (tmp_ptr, base_ptr, size, swap_type);
|
||||
-
|
||||
- /* Insertion sort, running from left-hand-side up to right-hand-side. */
|
||||
-
|
||||
- run_ptr = base_ptr + size;
|
||||
- while ((run_ptr += size) <= end_ptr)
|
||||
- {
|
||||
- tmp_ptr = run_ptr - size;
|
||||
- while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0)
|
||||
- tmp_ptr -= size;
|
||||
-
|
||||
- tmp_ptr += size;
|
||||
- if (tmp_ptr != run_ptr)
|
||||
- {
|
||||
- char *trav;
|
||||
-
|
||||
- trav = run_ptr + size;
|
||||
- while (--trav >= run_ptr)
|
||||
- {
|
||||
- char c = *trav;
|
||||
- char *hi, *lo;
|
||||
-
|
||||
- for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
|
||||
- *hi = *lo;
|
||||
- *hi = c;
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
+ insertion_sort_qsort_partitions (pbase, total_elems, size, swap_type, cmp,
|
||||
+ arg);
|
||||
}
|
||||
85
SOURCES/glibc-RHEL-24168-6.patch
Normal file
85
SOURCES/glibc-RHEL-24168-6.patch
Normal file
@ -0,0 +1,85 @@
|
||||
commit d097f3c79be55d646d86efb7ce876bf84d5ebe4e
|
||||
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Date: Tue Oct 3 09:22:48 2023 -0300
|
||||
|
||||
stdlib: qsort: Move some macros to inline function
|
||||
|
||||
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
|
||||
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index 35020e4c00e5fce3..821a87420638c5a5 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -101,15 +101,28 @@ typedef struct
|
||||
char *hi;
|
||||
} stack_node;
|
||||
|
||||
-/* The next 4 #defines implement a very fast in-line stack abstraction. */
|
||||
/* The stack needs log (total_elements) entries (we could even subtract
|
||||
log(MAX_THRESH)). Since total_elements has type size_t, we get as
|
||||
upper bound for log (total_elements):
|
||||
bits per byte (CHAR_BIT) * sizeof(size_t). */
|
||||
-#define STACK_SIZE (CHAR_BIT * sizeof (size_t))
|
||||
-#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
|
||||
-#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi)))
|
||||
-#define STACK_NOT_EMPTY (stack < top)
|
||||
+enum { STACK_SIZE = CHAR_BIT * sizeof (size_t) };
|
||||
+
|
||||
+static inline stack_node *
|
||||
+push (stack_node *top, char *lo, char *hi)
|
||||
+{
|
||||
+ top->lo = lo;
|
||||
+ top->hi = hi;
|
||||
+ return ++top;
|
||||
+}
|
||||
+
|
||||
+static inline stack_node *
|
||||
+pop (stack_node *top, char **lo, char **hi)
|
||||
+{
|
||||
+ --top;
|
||||
+ *lo = top->lo;
|
||||
+ *hi = top->hi;
|
||||
+ return top;
|
||||
+}
|
||||
|
||||
|
||||
static inline void
|
||||
@@ -213,11 +226,9 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
char *lo = base_ptr;
|
||||
char *hi = &lo[size * (total_elems - 1)];
|
||||
stack_node stack[STACK_SIZE];
|
||||
- stack_node *top = stack;
|
||||
-
|
||||
- PUSH (NULL, NULL);
|
||||
+ stack_node *top = stack + 1;
|
||||
|
||||
- while (STACK_NOT_EMPTY)
|
||||
+ while (stack < top)
|
||||
{
|
||||
char *left_ptr;
|
||||
char *right_ptr;
|
||||
@@ -282,7 +293,7 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
{
|
||||
if ((size_t) (hi - left_ptr) <= max_thresh)
|
||||
/* Ignore both small partitions. */
|
||||
- POP (lo, hi);
|
||||
+ top = pop (top, &lo, &hi);
|
||||
else
|
||||
/* Ignore small left partition. */
|
||||
lo = left_ptr;
|
||||
@@ -293,13 +304,13 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
else if ((right_ptr - lo) > (hi - left_ptr))
|
||||
{
|
||||
/* Push larger left partition indices. */
|
||||
- PUSH (lo, right_ptr);
|
||||
+ top = push (top, lo, right_ptr);
|
||||
lo = left_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Push larger right partition indices. */
|
||||
- PUSH (left_ptr, hi);
|
||||
+ top = push (top, left_ptr, hi);
|
||||
hi = right_ptr;
|
||||
}
|
||||
}
|
||||
176
SOURCES/glibc-RHEL-24168-7.patch
Normal file
176
SOURCES/glibc-RHEL-24168-7.patch
Normal file
@ -0,0 +1,176 @@
|
||||
commit 274a46c9b25ab733a1fb9fb1497f1beecae30193
|
||||
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Date: Tue Oct 3 09:22:49 2023 -0300
|
||||
|
||||
stdlib: Implement introsort for qsort (BZ 19305)
|
||||
|
||||
This patch makes the quicksort implementation to acts as introsort, to
|
||||
avoid worse-case performance (and thus making it O(nlog n)). It switch
|
||||
to heapsort when the depth level reaches 2*log2(total elements). The
|
||||
heapsort is a textbook implementation.
|
||||
|
||||
Checked on x86_64-linux-gnu and aarch64-linux-gnu.
|
||||
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
|
||||
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index 821a87420638c5a5..db299eb333cf0302 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -99,6 +99,7 @@ typedef struct
|
||||
{
|
||||
char *lo;
|
||||
char *hi;
|
||||
+ size_t depth;
|
||||
} stack_node;
|
||||
|
||||
/* The stack needs log (total_elements) entries (we could even subtract
|
||||
@@ -108,22 +109,85 @@ typedef struct
|
||||
enum { STACK_SIZE = CHAR_BIT * sizeof (size_t) };
|
||||
|
||||
static inline stack_node *
|
||||
-push (stack_node *top, char *lo, char *hi)
|
||||
+push (stack_node *top, char *lo, char *hi, size_t depth)
|
||||
{
|
||||
top->lo = lo;
|
||||
top->hi = hi;
|
||||
+ top->depth = depth;
|
||||
return ++top;
|
||||
}
|
||||
|
||||
static inline stack_node *
|
||||
-pop (stack_node *top, char **lo, char **hi)
|
||||
+pop (stack_node *top, char **lo, char **hi, size_t *depth)
|
||||
{
|
||||
--top;
|
||||
*lo = top->lo;
|
||||
*hi = top->hi;
|
||||
+ *depth = top->depth;
|
||||
return top;
|
||||
}
|
||||
|
||||
+/* NB: N is inclusive bound for BASE. */
|
||||
+static inline void
|
||||
+siftdown (void *base, size_t size, size_t k, size_t n,
|
||||
+ enum swap_type_t swap_type, __compar_d_fn_t cmp, void *arg)
|
||||
+{
|
||||
+ while (k <= n / 2)
|
||||
+ {
|
||||
+ size_t j = 2 * k;
|
||||
+ if (j < n && cmp (base + (j * size), base + ((j + 1) * size), arg) < 0)
|
||||
+ j++;
|
||||
+
|
||||
+ if (cmp (base + (k * size), base + (j * size), arg) >= 0)
|
||||
+ break;
|
||||
+
|
||||
+ do_swap (base + (size * j), base + (k * size), size, swap_type);
|
||||
+ k = j;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static inline void
|
||||
+heapify (void *base, size_t size, size_t n, enum swap_type_t swap_type,
|
||||
+ __compar_d_fn_t cmp, void *arg)
|
||||
+{
|
||||
+ size_t k = n / 2;
|
||||
+ while (1)
|
||||
+ {
|
||||
+ siftdown (base, size, k, n, swap_type, cmp, arg);
|
||||
+ if (k-- == 0)
|
||||
+ break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/* A non-recursive heapsort, used on introsort implementation as a fallback
|
||||
+ routine with worst-case performance of O(nlog n) and worst-case space
|
||||
+ complexity of O(1). It sorts the array starting at BASE and ending at
|
||||
+ END, with each element of SIZE bytes. The SWAP_TYPE is the callback
|
||||
+ function used to swap elements, and CMP is the function used to compare
|
||||
+ elements. */
|
||||
+static void
|
||||
+heapsort_r (void *base, void *end, size_t size, enum swap_type_t swap_type,
|
||||
+ __compar_d_fn_t cmp, void *arg)
|
||||
+{
|
||||
+ const size_t count = ((uintptr_t) end - (uintptr_t) base) / size;
|
||||
+
|
||||
+ if (count < 2)
|
||||
+ return;
|
||||
+
|
||||
+ size_t n = count - 1;
|
||||
+
|
||||
+ /* Build the binary heap, largest value at the base[0]. */
|
||||
+ heapify (base, size, n, swap_type, cmp, arg);
|
||||
+
|
||||
+ /* On each iteration base[0:n] is the binary heap, while base[n:count]
|
||||
+ is sorted. */
|
||||
+ while (n > 0)
|
||||
+ {
|
||||
+ do_swap (base, base + (n * size), size, swap_type);
|
||||
+ n--;
|
||||
+ siftdown (base, size, 0, n, swap_type, cmp, arg);
|
||||
+ }
|
||||
+}
|
||||
|
||||
static inline void
|
||||
insertion_sort_qsort_partitions (void *const pbase, size_t total_elems,
|
||||
@@ -209,7 +273,7 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
|
||||
const size_t max_thresh = MAX_THRESH * size;
|
||||
|
||||
- if (total_elems == 0)
|
||||
+ if (total_elems <= 1)
|
||||
/* Avoid lossage with unsigned arithmetic below. */
|
||||
return;
|
||||
|
||||
@@ -221,15 +285,26 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
else
|
||||
swap_type = SWAP_BYTES;
|
||||
|
||||
+ /* Maximum depth before quicksort switches to heapsort. */
|
||||
+ size_t depth = 2 * (sizeof (size_t) * CHAR_BIT - 1
|
||||
+ - __builtin_clzl (total_elems));
|
||||
+
|
||||
if (total_elems > MAX_THRESH)
|
||||
{
|
||||
char *lo = base_ptr;
|
||||
char *hi = &lo[size * (total_elems - 1)];
|
||||
stack_node stack[STACK_SIZE];
|
||||
- stack_node *top = stack + 1;
|
||||
+ stack_node *top = push (stack, NULL, NULL, depth);
|
||||
|
||||
while (stack < top)
|
||||
{
|
||||
+ if (depth == 0)
|
||||
+ {
|
||||
+ heapsort_r (lo, hi, size, swap_type, cmp, arg);
|
||||
+ top = pop (top, &lo, &hi, &depth);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
char *left_ptr;
|
||||
char *right_ptr;
|
||||
|
||||
@@ -293,7 +368,7 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
{
|
||||
if ((size_t) (hi - left_ptr) <= max_thresh)
|
||||
/* Ignore both small partitions. */
|
||||
- top = pop (top, &lo, &hi);
|
||||
+ top = pop (top, &lo, &hi, &depth);
|
||||
else
|
||||
/* Ignore small left partition. */
|
||||
lo = left_ptr;
|
||||
@@ -304,13 +379,13 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
else if ((right_ptr - lo) > (hi - left_ptr))
|
||||
{
|
||||
/* Push larger left partition indices. */
|
||||
- top = push (top, lo, right_ptr);
|
||||
+ top = push (top, lo, right_ptr, depth - 1);
|
||||
lo = left_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Push larger right partition indices. */
|
||||
- top = push (top, left_ptr, hi);
|
||||
+ top = push (top, left_ptr, hi, depth - 1);
|
||||
hi = right_ptr;
|
||||
}
|
||||
}
|
||||
491
SOURCES/glibc-RHEL-24168-8.patch
Normal file
491
SOURCES/glibc-RHEL-24168-8.patch
Normal file
@ -0,0 +1,491 @@
|
||||
commit 03bf8357e8291857a435afcc3048e0b697b6cc04
|
||||
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Date: Tue Oct 3 09:22:50 2023 -0300
|
||||
|
||||
stdlib: Remove use of mergesort on qsort (BZ 21719)
|
||||
|
||||
This patch removes the mergesort optimization on qsort implementation
|
||||
and uses the introsort instead. The mergesort implementation has some
|
||||
issues:
|
||||
|
||||
- It is as-safe only for certain types sizes (if total size is less
|
||||
than 1 KB with large element sizes also forcing memory allocation)
|
||||
which contradicts the function documentation. Although not required
|
||||
by the C standard, it is preferable and doable to have an O(1) space
|
||||
implementation.
|
||||
|
||||
- The malloc for certain element size and element number adds
|
||||
arbitrary latency (might even be worse if malloc is interposed).
|
||||
|
||||
- To avoid trigger swap from memory allocation the implementation
|
||||
relies on system information that might be virtualized (for instance
|
||||
VMs with overcommit memory) which might lead to potentially use of
|
||||
swap even if system advertise more memory than actually has. The
|
||||
check also have the downside of issuing syscalls where none is
|
||||
expected (although only once per execution).
|
||||
|
||||
- The mergesort is suboptimal on an already sorted array (BZ#21719).
|
||||
|
||||
The introsort implementation is already optimized to use constant extra
|
||||
space (due to the limit of total number of elements from maximum VM
|
||||
size) and thus can be used to avoid the malloc usage issues.
|
||||
|
||||
Resulting performance is slower due the usage of qsort, specially in the
|
||||
worst-case scenario (partialy or sorted arrays) and due the fact
|
||||
mergesort uses a slight improved swap operations.
|
||||
|
||||
This change also renders the BZ#21719 fix unrequired (since it is meant
|
||||
to fix the sorted input performance degradation for mergesort). The
|
||||
manual is also updated to indicate the function is now async-cancel
|
||||
safe.
|
||||
|
||||
Checked on x86_64-linux-gnu.
|
||||
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
|
||||
|
||||
Conflicts:
|
||||
stdlib/msort.c: Deletion had conflicts due to skipped backports.
|
||||
|
||||
diff --git a/include/stdlib.h b/include/stdlib.h
|
||||
index 22c9fb65c3074765..fd108df58ddf8b89 100644
|
||||
--- a/include/stdlib.h
|
||||
+++ b/include/stdlib.h
|
||||
@@ -107,8 +107,6 @@ extern int __posix_openpt (int __oflag) attribute_hidden;
|
||||
extern int __add_to_environ (const char *name, const char *value,
|
||||
const char *combines, int replace)
|
||||
attribute_hidden;
|
||||
-extern void _quicksort (void *const pbase, size_t total_elems,
|
||||
- size_t size, __compar_d_fn_t cmp, void *arg);
|
||||
|
||||
extern int __on_exit (void (*__func) (int __status, void *__arg), void *__arg);
|
||||
|
||||
diff --git a/manual/argp.texi b/manual/argp.texi
|
||||
index 0023441812d4e584..b77ad68285ecb732 100644
|
||||
--- a/manual/argp.texi
|
||||
+++ b/manual/argp.texi
|
||||
@@ -735,7 +735,7 @@ for options, bad phase of the moon, etc.
|
||||
@c hol_set_group ok
|
||||
@c hol_find_entry ok
|
||||
@c hol_sort @mtslocale @acucorrupt
|
||||
-@c qsort dup @acucorrupt
|
||||
+@c qsort dup
|
||||
@c hol_entry_qcmp @mtslocale
|
||||
@c hol_entry_cmp @mtslocale
|
||||
@c group_cmp ok
|
||||
diff --git a/manual/locale.texi b/manual/locale.texi
|
||||
index 720e0ca952a665bd..f6afa5dc44a2a016 100644
|
||||
--- a/manual/locale.texi
|
||||
+++ b/manual/locale.texi
|
||||
@@ -253,7 +253,7 @@ The symbols in this section are defined in the header file @file{locale.h}.
|
||||
@c calculate_head_size ok
|
||||
@c __munmap ok
|
||||
@c compute_hashval ok
|
||||
-@c qsort dup @acucorrupt
|
||||
+@c qsort dup
|
||||
@c rangecmp ok
|
||||
@c malloc @ascuheap @acsmem
|
||||
@c strdup @ascuheap @acsmem
|
||||
@@ -275,7 +275,6 @@ The symbols in this section are defined in the header file @file{locale.h}.
|
||||
@c realloc @ascuheap @acsmem
|
||||
@c realloc @ascuheap @acsmem
|
||||
@c fclose @ascuheap @asulock @acsmem @acsfd @aculock
|
||||
-@c qsort @ascuheap @acsmem
|
||||
@c alias_compare dup
|
||||
@c libc_lock_unlock @aculock
|
||||
@c _nl_explode_name @ascuheap @acsmem
|
||||
diff --git a/manual/search.texi b/manual/search.texi
|
||||
index 5691bf2f2b2bb861..a550858478f7fc83 100644
|
||||
--- a/manual/search.texi
|
||||
+++ b/manual/search.texi
|
||||
@@ -159,7 +159,7 @@ To sort an array using an arbitrary comparison function, use the
|
||||
|
||||
@deftypefun void qsort (void *@var{array}, size_t @var{count}, size_t @var{size}, comparison_fn_t @var{compare})
|
||||
@standards{ISO, stdlib.h}
|
||||
-@safety{@prelim{}@mtsafe{}@assafe{}@acunsafe{@acucorrupt{}}}
|
||||
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
|
||||
The @code{qsort} function sorts the array @var{array}. The array
|
||||
contains @var{count} elements, each of which is of size @var{size}.
|
||||
|
||||
@@ -199,9 +199,8 @@ Functions}):
|
||||
The @code{qsort} function derives its name from the fact that it was
|
||||
originally implemented using the ``quick sort'' algorithm.
|
||||
|
||||
-The implementation of @code{qsort} in this library might not be an
|
||||
-in-place sort and might thereby use an extra amount of memory to store
|
||||
-the array.
|
||||
+The implementation of @code{qsort} in this library is an in-place sort
|
||||
+and uses a constant extra space (allocated on the stack).
|
||||
@end deftypefun
|
||||
|
||||
@node Search/Sort Example
|
||||
diff --git a/stdlib/Makefile b/stdlib/Makefile
|
||||
index 03f8478c64408ed3..3b89bc2aa0307321 100644
|
||||
--- a/stdlib/Makefile
|
||||
+++ b/stdlib/Makefile
|
||||
@@ -94,7 +94,6 @@ routines := \
|
||||
mbtowc \
|
||||
mrand48 \
|
||||
mrand48_r \
|
||||
- msort \
|
||||
nrand48 \
|
||||
nrand48_r \
|
||||
old_atexit \
|
||||
@@ -370,7 +369,6 @@ generated += \
|
||||
# generated
|
||||
|
||||
CFLAGS-bsearch.c += $(uses-callbacks)
|
||||
-CFLAGS-msort.c += $(uses-callbacks)
|
||||
CFLAGS-qsort.c += $(uses-callbacks)
|
||||
CFLAGS-system.c += -fexceptions
|
||||
CFLAGS-system.os = -fomit-frame-pointer
|
||||
diff --git a/stdlib/msort.c b/stdlib/msort.c
|
||||
deleted file mode 100644
|
||||
index 8750cc59db2337cf..0000000000000000
|
||||
--- a/stdlib/msort.c
|
||||
+++ /dev/null
|
||||
@@ -1,310 +0,0 @@
|
||||
-/* An alternative to qsort, with an identical interface.
|
||||
- This file is part of the GNU C Library.
|
||||
- Copyright (C) 1992-2021 Free Software Foundation, Inc.
|
||||
- Written by Mike Haertel, September 1988.
|
||||
-
|
||||
- The GNU C Library is free software; you can redistribute it and/or
|
||||
- modify it under the terms of the GNU Lesser General Public
|
||||
- License as published by the Free Software Foundation; either
|
||||
- version 2.1 of the License, or (at your option) any later version.
|
||||
-
|
||||
- The GNU C Library is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
- Lesser General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Lesser General Public
|
||||
- License along with the GNU C Library; if not, see
|
||||
- <https://www.gnu.org/licenses/>. */
|
||||
-
|
||||
-#include <alloca.h>
|
||||
-#include <stdint.h>
|
||||
-#include <stdlib.h>
|
||||
-#include <string.h>
|
||||
-#include <unistd.h>
|
||||
-#include <memcopy.h>
|
||||
-#include <errno.h>
|
||||
-#include <atomic.h>
|
||||
-
|
||||
-struct msort_param
|
||||
-{
|
||||
- size_t s;
|
||||
- size_t var;
|
||||
- __compar_d_fn_t cmp;
|
||||
- void *arg;
|
||||
- char *t;
|
||||
-};
|
||||
-static void msort_with_tmp (const struct msort_param *p, void *b, size_t n);
|
||||
-
|
||||
-static void
|
||||
-msort_with_tmp (const struct msort_param *p, void *b, size_t n)
|
||||
-{
|
||||
- char *b1, *b2;
|
||||
- size_t n1, n2;
|
||||
-
|
||||
- if (n <= 1)
|
||||
- return;
|
||||
-
|
||||
- n1 = n / 2;
|
||||
- n2 = n - n1;
|
||||
- b1 = b;
|
||||
- b2 = (char *) b + (n1 * p->s);
|
||||
-
|
||||
- msort_with_tmp (p, b1, n1);
|
||||
- msort_with_tmp (p, b2, n2);
|
||||
-
|
||||
- char *tmp = p->t;
|
||||
- const size_t s = p->s;
|
||||
- __compar_d_fn_t cmp = p->cmp;
|
||||
- void *arg = p->arg;
|
||||
- switch (p->var)
|
||||
- {
|
||||
- case 0:
|
||||
- while (n1 > 0 && n2 > 0)
|
||||
- {
|
||||
- if ((*cmp) (b1, b2, arg) <= 0)
|
||||
- {
|
||||
- *(uint32_t *) tmp = *(uint32_t *) b1;
|
||||
- b1 += sizeof (uint32_t);
|
||||
- --n1;
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- *(uint32_t *) tmp = *(uint32_t *) b2;
|
||||
- b2 += sizeof (uint32_t);
|
||||
- --n2;
|
||||
- }
|
||||
- tmp += sizeof (uint32_t);
|
||||
- }
|
||||
- break;
|
||||
- case 1:
|
||||
- while (n1 > 0 && n2 > 0)
|
||||
- {
|
||||
- if ((*cmp) (b1, b2, arg) <= 0)
|
||||
- {
|
||||
- *(uint64_t *) tmp = *(uint64_t *) b1;
|
||||
- b1 += sizeof (uint64_t);
|
||||
- --n1;
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- *(uint64_t *) tmp = *(uint64_t *) b2;
|
||||
- b2 += sizeof (uint64_t);
|
||||
- --n2;
|
||||
- }
|
||||
- tmp += sizeof (uint64_t);
|
||||
- }
|
||||
- break;
|
||||
- case 2:
|
||||
- while (n1 > 0 && n2 > 0)
|
||||
- {
|
||||
- unsigned long *tmpl = (unsigned long *) tmp;
|
||||
- unsigned long *bl;
|
||||
-
|
||||
- tmp += s;
|
||||
- if ((*cmp) (b1, b2, arg) <= 0)
|
||||
- {
|
||||
- bl = (unsigned long *) b1;
|
||||
- b1 += s;
|
||||
- --n1;
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- bl = (unsigned long *) b2;
|
||||
- b2 += s;
|
||||
- --n2;
|
||||
- }
|
||||
- while (tmpl < (unsigned long *) tmp)
|
||||
- *tmpl++ = *bl++;
|
||||
- }
|
||||
- break;
|
||||
- case 3:
|
||||
- while (n1 > 0 && n2 > 0)
|
||||
- {
|
||||
- if ((*cmp) (*(const void **) b1, *(const void **) b2, arg) <= 0)
|
||||
- {
|
||||
- *(void **) tmp = *(void **) b1;
|
||||
- b1 += sizeof (void *);
|
||||
- --n1;
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- *(void **) tmp = *(void **) b2;
|
||||
- b2 += sizeof (void *);
|
||||
- --n2;
|
||||
- }
|
||||
- tmp += sizeof (void *);
|
||||
- }
|
||||
- break;
|
||||
- default:
|
||||
- while (n1 > 0 && n2 > 0)
|
||||
- {
|
||||
- if ((*cmp) (b1, b2, arg) <= 0)
|
||||
- {
|
||||
- tmp = (char *) __mempcpy (tmp, b1, s);
|
||||
- b1 += s;
|
||||
- --n1;
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- tmp = (char *) __mempcpy (tmp, b2, s);
|
||||
- b2 += s;
|
||||
- --n2;
|
||||
- }
|
||||
- }
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- if (n1 > 0)
|
||||
- memcpy (tmp, b1, n1 * s);
|
||||
- memcpy (b, p->t, (n - n2) * s);
|
||||
-}
|
||||
-
|
||||
-
|
||||
-void
|
||||
-__qsort_r (void *b, size_t n, size_t s, __compar_d_fn_t cmp, void *arg)
|
||||
-{
|
||||
- size_t size = n * s;
|
||||
- char *tmp = NULL;
|
||||
- struct msort_param p;
|
||||
-
|
||||
- /* For large object sizes use indirect sorting. */
|
||||
- if (s > 32)
|
||||
- size = 2 * n * sizeof (void *) + s;
|
||||
-
|
||||
- if (size < 1024)
|
||||
- /* The temporary array is small, so put it on the stack. */
|
||||
- p.t = __alloca (size);
|
||||
- else
|
||||
- {
|
||||
- /* We should avoid allocating too much memory since this might
|
||||
- have to be backed up by swap space. */
|
||||
- static long int phys_pages;
|
||||
- static int pagesize;
|
||||
-
|
||||
- if (pagesize == 0)
|
||||
- {
|
||||
- phys_pages = __sysconf (_SC_PHYS_PAGES);
|
||||
-
|
||||
- if (phys_pages == -1)
|
||||
- /* Error while determining the memory size. So let's
|
||||
- assume there is enough memory. Otherwise the
|
||||
- implementer should provide a complete implementation of
|
||||
- the `sysconf' function. */
|
||||
- phys_pages = (long int) (~0ul >> 1);
|
||||
-
|
||||
- /* The following determines that we will never use more than
|
||||
- a quarter of the physical memory. */
|
||||
- phys_pages /= 4;
|
||||
-
|
||||
- /* Make sure phys_pages is written to memory. */
|
||||
- atomic_write_barrier ();
|
||||
-
|
||||
- pagesize = __sysconf (_SC_PAGESIZE);
|
||||
- }
|
||||
-
|
||||
- /* Just a comment here. We cannot compute
|
||||
- phys_pages * pagesize
|
||||
- and compare the needed amount of memory against this value.
|
||||
- The problem is that some systems might have more physical
|
||||
- memory then can be represented with a `size_t' value (when
|
||||
- measured in bytes. */
|
||||
-
|
||||
- /* If the memory requirements are too high don't allocate memory. */
|
||||
- if (size / pagesize > (size_t) phys_pages)
|
||||
- {
|
||||
- _quicksort (b, n, s, cmp, arg);
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- /* It's somewhat large, so malloc it. */
|
||||
- int save = errno;
|
||||
- tmp = malloc (size);
|
||||
- __set_errno (save);
|
||||
- if (tmp == NULL)
|
||||
- {
|
||||
- /* Couldn't get space, so use the slower algorithm
|
||||
- that doesn't need a temporary array. */
|
||||
- _quicksort (b, n, s, cmp, arg);
|
||||
- return;
|
||||
- }
|
||||
- p.t = tmp;
|
||||
- }
|
||||
-
|
||||
- p.s = s;
|
||||
- p.var = 4;
|
||||
- p.cmp = cmp;
|
||||
- p.arg = arg;
|
||||
-
|
||||
- if (s > 32)
|
||||
- {
|
||||
- /* Indirect sorting. */
|
||||
- char *ip = (char *) b;
|
||||
- void **tp = (void **) (p.t + n * sizeof (void *));
|
||||
- void **t = tp;
|
||||
- void *tmp_storage = (void *) (tp + n);
|
||||
-
|
||||
- while ((void *) t < tmp_storage)
|
||||
- {
|
||||
- *t++ = ip;
|
||||
- ip += s;
|
||||
- }
|
||||
- p.s = sizeof (void *);
|
||||
- p.var = 3;
|
||||
- msort_with_tmp (&p, p.t + n * sizeof (void *), n);
|
||||
-
|
||||
- /* tp[0] .. tp[n - 1] is now sorted, copy around entries of
|
||||
- the original array. Knuth vol. 3 (2nd ed.) exercise 5.2-10. */
|
||||
- char *kp;
|
||||
- size_t i;
|
||||
- for (i = 0, ip = (char *) b; i < n; i++, ip += s)
|
||||
- if ((kp = tp[i]) != ip)
|
||||
- {
|
||||
- size_t j = i;
|
||||
- char *jp = ip;
|
||||
- memcpy (tmp_storage, ip, s);
|
||||
-
|
||||
- do
|
||||
- {
|
||||
- size_t k = (kp - (char *) b) / s;
|
||||
- tp[j] = jp;
|
||||
- memcpy (jp, kp, s);
|
||||
- j = k;
|
||||
- jp = kp;
|
||||
- kp = tp[k];
|
||||
- }
|
||||
- while (kp != ip);
|
||||
-
|
||||
- tp[j] = jp;
|
||||
- memcpy (jp, tmp_storage, s);
|
||||
- }
|
||||
- }
|
||||
- else
|
||||
- {
|
||||
- if ((s & (sizeof (uint32_t) - 1)) == 0
|
||||
- && ((char *) b - (char *) 0) % __alignof__ (uint32_t) == 0)
|
||||
- {
|
||||
- if (s == sizeof (uint32_t))
|
||||
- p.var = 0;
|
||||
- else if (s == sizeof (uint64_t)
|
||||
- && ((char *) b - (char *) 0) % __alignof__ (uint64_t) == 0)
|
||||
- p.var = 1;
|
||||
- else if ((s & (sizeof (unsigned long) - 1)) == 0
|
||||
- && ((char *) b - (char *) 0)
|
||||
- % __alignof__ (unsigned long) == 0)
|
||||
- p.var = 2;
|
||||
- }
|
||||
- msort_with_tmp (&p, b, n);
|
||||
- }
|
||||
- free (tmp);
|
||||
-}
|
||||
-libc_hidden_def (__qsort_r)
|
||||
-weak_alias (__qsort_r, qsort_r)
|
||||
-
|
||||
-
|
||||
-void
|
||||
-qsort (void *b, size_t n, size_t s, __compar_fn_t cmp)
|
||||
-{
|
||||
- return __qsort_r (b, n, s, (__compar_d_fn_t) cmp, NULL);
|
||||
-}
|
||||
-libc_hidden_def (qsort)
|
||||
diff --git a/stdlib/qsort.c b/stdlib/qsort.c
|
||||
index db299eb333cf0302..cb1619aa0ae7de72 100644
|
||||
--- a/stdlib/qsort.c
|
||||
+++ b/stdlib/qsort.c
|
||||
@@ -20,7 +20,6 @@
|
||||
Engineering a sort function; Jon Bentley and M. Douglas McIlroy;
|
||||
Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */
|
||||
|
||||
-#include <alloca.h>
|
||||
#include <limits.h>
|
||||
#include <memswap.h>
|
||||
#include <stdlib.h>
|
||||
@@ -266,8 +265,8 @@ insertion_sort_qsort_partitions (void *const pbase, size_t total_elems,
|
||||
stack size is needed (actually O(1) in this case)! */
|
||||
|
||||
void
|
||||
-_quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
- __compar_d_fn_t cmp, void *arg)
|
||||
+__qsort_r (void *const pbase, size_t total_elems, size_t size,
|
||||
+ __compar_d_fn_t cmp, void *arg)
|
||||
{
|
||||
char *base_ptr = (char *) pbase;
|
||||
|
||||
@@ -399,3 +398,12 @@ _quicksort (void *const pbase, size_t total_elems, size_t size,
|
||||
insertion_sort_qsort_partitions (pbase, total_elems, size, swap_type, cmp,
|
||||
arg);
|
||||
}
|
||||
+libc_hidden_def (__qsort_r)
|
||||
+weak_alias (__qsort_r, qsort_r)
|
||||
+
|
||||
+void
|
||||
+qsort (void *b, size_t n, size_t s, __compar_fn_t cmp)
|
||||
+{
|
||||
+ return __qsort_r (b, n, s, (__compar_d_fn_t) cmp, NULL);
|
||||
+}
|
||||
+libc_hidden_def (qsort)
|
||||
399
SOURCES/glibc-RHEL-24168-9.patch
Normal file
399
SOURCES/glibc-RHEL-24168-9.patch
Normal file
@ -0,0 +1,399 @@
|
||||
commit bc888a3976700a3607f6ec4a36dbf3030161cb3e
|
||||
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
Date: Tue Oct 3 09:22:51 2023 -0300
|
||||
|
||||
stdlib: Add more qsort{_r} coverage
|
||||
|
||||
This patch adds a qsort and qsort_r to trigger the worst case
|
||||
scenario for the quicksort (which glibc current lacks coverage).
|
||||
The test is done with random input, dfferent internal types (uint8_t,
|
||||
uint16_t, uint32_t, uint64_t, large size), and with
|
||||
different set of element numbers.
|
||||
|
||||
Checked on x86_64-linux-gnu and i686-linux-gnu.
|
||||
Reviewed-by: Noah Goldstein <goldstein.w.n@gmail.com>
|
||||
|
||||
diff --git a/stdlib/Makefile b/stdlib/Makefile
|
||||
index 3b89bc2aa0307321..4039e5395eeea2b0 100644
|
||||
--- a/stdlib/Makefile
|
||||
+++ b/stdlib/Makefile
|
||||
@@ -211,6 +211,7 @@ tests := \
|
||||
tst-on_exit \
|
||||
tst-qsort \
|
||||
tst-qsort2 \
|
||||
+ tst-qsort3 \
|
||||
tst-quick_exit \
|
||||
tst-rand48 \
|
||||
tst-rand48-2 \
|
||||
diff --git a/stdlib/tst-qsort3.c b/stdlib/tst-qsort3.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..421560d74434a116
|
||||
--- /dev/null
|
||||
+++ b/stdlib/tst-qsort3.c
|
||||
@@ -0,0 +1,366 @@
|
||||
+/* qsort(_r) tests to trigger worst case for quicksort.
|
||||
+ Copyright (C) 2023 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
|
||||
+ <http://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <array_length.h>
|
||||
+#include <errno.h>
|
||||
+#include <getopt.h>
|
||||
+#include <stdbool.h>
|
||||
+#include <stdint.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <string.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/support.h>
|
||||
+#include <support/test-driver.h>
|
||||
+
|
||||
+typedef enum
|
||||
+{
|
||||
+ Sorted,
|
||||
+ Random,
|
||||
+ Repeated,
|
||||
+ Bitonic,
|
||||
+ Duplicated,
|
||||
+} arraytype_t;
|
||||
+
|
||||
+/* Ratio of total of elements which will be repeated. */
|
||||
+static const double RepeatedRatio = 0.2;
|
||||
+
|
||||
+/* Ratio of duplicated element . */
|
||||
+static const double DuplicatedRatio = 0.4;
|
||||
+
|
||||
+struct array_t
|
||||
+{
|
||||
+ arraytype_t type;
|
||||
+ const char *name;
|
||||
+} static const arraytypes[] =
|
||||
+{
|
||||
+ { Sorted, "Sorted" },
|
||||
+ { Random, "Random" },
|
||||
+ { Repeated, "Repeated" },
|
||||
+ { Bitonic, "Bitonic" },
|
||||
+ { Duplicated, "Duplicated" },
|
||||
+};
|
||||
+
|
||||
+/* Return the index of BASE as interpreted as an array of elements
|
||||
+ of size SIZE. */
|
||||
+static inline void *
|
||||
+arr (void *base, size_t idx, size_t size)
|
||||
+{
|
||||
+ return (void*)((uintptr_t)base + (idx * size));
|
||||
+}
|
||||
+
|
||||
+/* Functions used to check qsort. */
|
||||
+static int
|
||||
+uint8_t_cmp (const void *a, const void *b)
|
||||
+{
|
||||
+ uint8_t ia = *(uint8_t*)a;
|
||||
+ uint8_t ib = *(uint8_t*)b;
|
||||
+ return (ia > ib) - (ia < ib);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+uint16_t_cmp (const void *a, const void *b)
|
||||
+{
|
||||
+ uint16_t ia = *(uint16_t*)a;
|
||||
+ uint16_t ib = *(uint16_t*)b;
|
||||
+ return (ia > ib) - (ia < ib);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+uint32_t_cmp (const void *a, const void *b)
|
||||
+{
|
||||
+ uint32_t ia = *(uint32_t*)a;
|
||||
+ uint32_t ib = *(uint32_t*)b;
|
||||
+ return (ia > ib) - (ia < ib);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+uint64_t_cmp (const void *a, const void *b)
|
||||
+{
|
||||
+ uint64_t ia = *(uint64_t*)a;
|
||||
+ uint64_t ib = *(uint64_t*)b;
|
||||
+ return (ia > ib) - (ia < ib);
|
||||
+}
|
||||
+
|
||||
+#define LARGE_SIZE 47
|
||||
+
|
||||
+static int
|
||||
+large_cmp (const void *a, const void *b)
|
||||
+{
|
||||
+ return memcmp (a, b, LARGE_SIZE);
|
||||
+}
|
||||
+
|
||||
+/* Function used to check qsort_r. */
|
||||
+typedef enum
|
||||
+{
|
||||
+ UINT8_CMP_T,
|
||||
+ UINT16_CMP_T,
|
||||
+ UINT32_CMP_T,
|
||||
+ UINT64_CMP_T,
|
||||
+ LARGE_CMP_T
|
||||
+} type_cmp_t;
|
||||
+
|
||||
+static type_cmp_t
|
||||
+uint_t_cmp_type (size_t sz)
|
||||
+{
|
||||
+ switch (sz)
|
||||
+ {
|
||||
+ case sizeof (uint8_t): return UINT8_CMP_T;
|
||||
+ case sizeof (uint16_t): return UINT16_CMP_T;
|
||||
+ case sizeof (uint64_t): return UINT64_CMP_T;
|
||||
+ case sizeof (uint32_t): return UINT32_CMP_T;
|
||||
+ default: return LARGE_CMP_T;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+uint_t_cmp (const void *a, const void *b, void *arg)
|
||||
+{
|
||||
+ type_cmp_t type = *(type_cmp_t*) arg;
|
||||
+ switch (type)
|
||||
+ {
|
||||
+ case UINT8_CMP_T: return uint8_t_cmp (a, b);
|
||||
+ case UINT32_CMP_T: return uint32_t_cmp (a, b);
|
||||
+ case UINT16_CMP_T: return uint16_t_cmp (a, b);
|
||||
+ case UINT64_CMP_T: return uint64_t_cmp (a, b);
|
||||
+ default: return large_cmp (a, b);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+seq (void *elem, size_t type_size, int value)
|
||||
+{
|
||||
+ if (type_size == sizeof (uint8_t))
|
||||
+ *(uint8_t*)elem = value;
|
||||
+ else if (type_size == sizeof (uint16_t))
|
||||
+ *(uint16_t*)elem = value;
|
||||
+ else if (type_size == sizeof (uint32_t))
|
||||
+ *(uint32_t*)elem = value;
|
||||
+ else if (type_size == sizeof (uint64_t))
|
||||
+ *(uint64_t*)elem = value;
|
||||
+ else
|
||||
+ memset (elem, value, type_size);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+fill_array (void *array, void *refarray, size_t nmemb, size_t type_size,
|
||||
+ arraytype_t type)
|
||||
+{
|
||||
+ size_t size = nmemb * type_size;
|
||||
+
|
||||
+ switch (type)
|
||||
+ {
|
||||
+ case Sorted:
|
||||
+ for (size_t i = 0; i < nmemb; i++)
|
||||
+ seq (arr (array, i, type_size), type_size, i);
|
||||
+ break;
|
||||
+
|
||||
+ case Random:
|
||||
+ arc4random_buf (array, size);
|
||||
+ break;
|
||||
+
|
||||
+ case Repeated:
|
||||
+ {
|
||||
+ arc4random_buf (array, size);
|
||||
+
|
||||
+ void *randelem = xmalloc (type_size);
|
||||
+ arc4random_buf (randelem, type_size);
|
||||
+
|
||||
+ /* Repeat REPEATED elements (based on RepeatRatio ratio) in the random
|
||||
+ array. */
|
||||
+ size_t repeated = (size_t)(nmemb * RepeatedRatio);
|
||||
+ for (size_t i = 0; i < repeated; i++)
|
||||
+ {
|
||||
+ size_t pos = arc4random_uniform (nmemb - 1);
|
||||
+ memcpy (arr (array, pos, type_size), randelem, type_size);
|
||||
+ }
|
||||
+ free (randelem);
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case Bitonic:
|
||||
+ {
|
||||
+ size_t i;
|
||||
+ for (i = 0; i < nmemb / 2; i++)
|
||||
+ seq (arr (array, i, type_size), type_size, i);
|
||||
+ for ( ; i < nmemb; i++)
|
||||
+ seq (arr (array, i, type_size), type_size, (nmemb - 1) - i);
|
||||
+ }
|
||||
+ break;
|
||||
+
|
||||
+ case Duplicated:
|
||||
+ {
|
||||
+ int randelem1 = arc4random ();
|
||||
+ for (size_t i = 0; i < nmemb; i++)
|
||||
+ seq (arr (array, i, type_size), type_size, randelem1);
|
||||
+
|
||||
+ size_t duplicates = (size_t)(nmemb * DuplicatedRatio);
|
||||
+ int randelem2 = arc4random ();
|
||||
+ for (size_t i = 0; i < duplicates; i++)
|
||||
+ {
|
||||
+ size_t pos = arc4random_uniform (nmemb - 1);
|
||||
+ seq (arr (array, pos, type_size), type_size, randelem2);
|
||||
+ }
|
||||
+ }
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ memcpy (refarray, array, size);
|
||||
+}
|
||||
+
|
||||
+typedef int (*cmpfunc_t)(const void *, const void *);
|
||||
+
|
||||
+/* Simple insertion sort to use as reference sort. */
|
||||
+static void
|
||||
+qsort_r_ref (void *p, size_t n, size_t s, __compar_d_fn_t cmp, void *arg)
|
||||
+{
|
||||
+ if (n <= 1)
|
||||
+ return;
|
||||
+
|
||||
+ int i = 1;
|
||||
+ char tmp[s];
|
||||
+ while (i < n)
|
||||
+ {
|
||||
+ memcpy (tmp, arr (p, i, s), s);
|
||||
+ int j = i - 1;
|
||||
+ while (j >= 0 && cmp (arr (p, j, s), tmp, arg) > 0)
|
||||
+ {
|
||||
+ memcpy (arr (p, j + 1, s), arr (p, j, s), s);
|
||||
+ j = j - 1;
|
||||
+ }
|
||||
+ memcpy (arr (p, j + 1, s), tmp, s);
|
||||
+ i = i + 1;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+qsort_ref (void *b, size_t n, size_t s, __compar_fn_t cmp)
|
||||
+{
|
||||
+ return qsort_r_ref (b, n, s, (__compar_d_fn_t) cmp, NULL);
|
||||
+}
|
||||
+
|
||||
+/* Check if ARRAY of total NMEMB element of size SIZE is sorted
|
||||
+ based on CMPFUNC. */
|
||||
+static void
|
||||
+check_array (void *array, void *refarray, size_t nmemb, size_t type_size,
|
||||
+ cmpfunc_t cmpfunc)
|
||||
+{
|
||||
+ for (size_t i = 1; i < nmemb; i++)
|
||||
+ {
|
||||
+ int ret = cmpfunc (arr (array, i, type_size),
|
||||
+ arr (array, i-1, type_size));
|
||||
+ TEST_VERIFY_EXIT (ret >= 0);
|
||||
+ }
|
||||
+
|
||||
+ size_t size = nmemb * type_size;
|
||||
+ TEST_COMPARE_BLOB (array, size, refarray, size);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+check_qsort (void *buf, void *refbuf, size_t nelem, size_t type_size,
|
||||
+ arraytype_t type, cmpfunc_t cmpfunc)
|
||||
+{
|
||||
+ fill_array (buf, refbuf, nelem, type_size, type);
|
||||
+
|
||||
+ qsort (buf, nelem, type_size, cmpfunc);
|
||||
+ qsort_ref (refbuf, nelem, type_size, cmpfunc);
|
||||
+
|
||||
+ check_array (buf, refbuf, nelem, type_size, cmpfunc);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+check_qsort_r (void *buf, void *refbuf, size_t nelem, size_t type_size,
|
||||
+ arraytype_t type, cmpfunc_t cmpfunc)
|
||||
+{
|
||||
+ fill_array (buf, refbuf, nelem, type_size, type);
|
||||
+
|
||||
+ type_cmp_t typecmp = uint_t_cmp_type (type_size);
|
||||
+
|
||||
+ qsort_r (buf, nelem, type_size, uint_t_cmp, &typecmp);
|
||||
+ qsort_r_ref (refbuf, nelem, type_size, uint_t_cmp, &typecmp);
|
||||
+
|
||||
+ check_array (buf, refbuf, nelem, type_size, cmpfunc);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ /* Some random sizes. */
|
||||
+ static const size_t nelems[] = { 0, 1, 7, 20, 32, 100, 256, 1024, 4256 };
|
||||
+ size_t max_nelems = 0;
|
||||
+ for (int i = 0; i < array_length (nelems); i++)
|
||||
+ if (nelems[i] > max_nelems)
|
||||
+ max_nelems = nelems[i];
|
||||
+
|
||||
+ static const struct test_t
|
||||
+ {
|
||||
+ size_t type_size;
|
||||
+ cmpfunc_t cmpfunc;
|
||||
+ }
|
||||
+ tests[] =
|
||||
+ {
|
||||
+ { sizeof (uint8_t), uint8_t_cmp },
|
||||
+ { sizeof (uint16_t), uint16_t_cmp },
|
||||
+ { sizeof (uint32_t), uint32_t_cmp },
|
||||
+ { sizeof (uint64_t), uint64_t_cmp },
|
||||
+ /* Test swap with large elements. */
|
||||
+ { LARGE_SIZE, large_cmp },
|
||||
+ };
|
||||
+ size_t max_type_size = 0;
|
||||
+ for (int i = 0; i < array_length (tests); i++)
|
||||
+ if (tests[i].type_size > max_type_size)
|
||||
+ max_type_size = tests[i].type_size;
|
||||
+
|
||||
+ void *buf = reallocarray (NULL, max_nelems, max_type_size);
|
||||
+ TEST_VERIFY_EXIT (buf != NULL);
|
||||
+ void *refbuf = reallocarray (NULL, max_nelems, max_type_size);
|
||||
+ TEST_VERIFY_EXIT (refbuf != NULL);
|
||||
+
|
||||
+ for (const struct test_t *test = tests; test < array_end (tests); ++test)
|
||||
+ {
|
||||
+ if (test_verbose > 0)
|
||||
+ printf ("info: testing qsort with type_size=%zu\n", test->type_size);
|
||||
+ for (const struct array_t *arraytype = arraytypes;
|
||||
+ arraytype < array_end (arraytypes);
|
||||
+ ++arraytype)
|
||||
+ {
|
||||
+ if (test_verbose > 0)
|
||||
+ printf (" distribution=%s\n", arraytype->name);
|
||||
+ for (const size_t *nelem = nelems;
|
||||
+ nelem < array_end (nelems);
|
||||
+ ++nelem)
|
||||
+ {
|
||||
+ if (test_verbose > 0)
|
||||
+ printf (" nelem=%zu, total size=%zu\n", *nelem,
|
||||
+ *nelem * test->type_size);
|
||||
+
|
||||
+ check_qsort (buf, refbuf, *nelem, test->type_size,
|
||||
+ arraytype->type, test->cmpfunc);
|
||||
+ check_qsort_r (buf, refbuf, *nelem, test->type_size,
|
||||
+ arraytype->type, test->cmpfunc);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ free (buf);
|
||||
+ free (refbuf);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
@ -22,7 +22,7 @@ Date: Fri Mar 15 19:08:24 2024 +0100
|
||||
Reviewed-by: Arjun Shankar <arjun@redhat.com>
|
||||
|
||||
diff --git a/sysdeps/unix/sysv/linux/sched_getcpu.c b/sysdeps/unix/sysv/linux/sched_getcpu.c
|
||||
index dfb884568d..72a3360550 100644
|
||||
index 6f78edaea1495342..a33e30a1cb8e161b 100644
|
||||
--- a/sysdeps/unix/sysv/linux/sched_getcpu.c
|
||||
+++ b/sysdeps/unix/sysv/linux/sched_getcpu.c
|
||||
@@ -33,17 +33,9 @@ vsyscall_sched_getcpu (void)
|
||||
@ -6,7 +6,7 @@ Date: Sat May 20 13:37:47 2023 +0000
|
||||
|
||||
Note on the changes:
|
||||
- Partial backport, the only file modified is `resolv/inet_pton.c` to
|
||||
ease further backport for RHEL-83984.
|
||||
ease further backport for RHEL-44920.
|
||||
|
||||
diff --git a/resolv/inet_pton.c b/resolv/inet_pton.c
|
||||
index f1d5db75d0d47501..835f364794c1be96 100644
|
||||
@ -26,11 +26,6 @@ Date: Tue Mar 25 09:40:20 2025 +0000
|
||||
|
||||
Reviewed-by: Joseph Myers <josmyers@redhat.com>
|
||||
|
||||
|
||||
Conflicts:
|
||||
stdio-common/Makefile
|
||||
(mising tst-fwrite-bz29459 downstream)
|
||||
|
||||
diff --git a/Makerules b/Makerules
|
||||
index 689842ba56c71b0d..2b5deadced0f8c08 100644
|
||||
--- a/Makerules
|
||||
@ -46,7 +41,7 @@ index 689842ba56c71b0d..2b5deadced0f8c08 100644
|
||||
# and 64-bit time support.
|
||||
include $(o-iterator)
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index fe69e48849cb9819..df7afefc7a1f9828 100644
|
||||
index 077c1062756971e3..15525a0f768244c6 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -51,6 +51,33 @@ nonfmt-xprintf-stems := \
|
||||
@ -83,23 +78,23 @@ index fe69e48849cb9819..df7afefc7a1f9828 100644
|
||||
headers := \
|
||||
bits/printf-ldbl.h \
|
||||
bits/stdio_lim.h \
|
||||
@@ -274,6 +301,7 @@ endif
|
||||
@@ -279,6 +306,7 @@ endif
|
||||
|
||||
test-srcs = \
|
||||
$(xprintf-srcs) \
|
||||
+ $(xscanf-srcs) \
|
||||
tst-fwrite-bz29459 \
|
||||
tst-printf \
|
||||
tst-printfsz-islongdouble \
|
||||
tst-unbputc \
|
||||
@@ -282,6 +310,7 @@ test-srcs = \
|
||||
@@ -288,6 +316,7 @@ test-srcs = \
|
||||
ifeq ($(run-built-tests),yes)
|
||||
tests-special += \
|
||||
$(foreach f,$(xprintf-stems),$(objpfx)$(f).out) \
|
||||
+ $(foreach f,$(xscanf-stems),$(objpfx)$(f).out) \
|
||||
$(objpfx)tst-fwrite-bz29459.out \
|
||||
$(objpfx)tst-printf.out \
|
||||
$(objpfx)tst-printfsz-islongdouble.out \
|
||||
$(objpfx)tst-setvbuf1-cmp.out \
|
||||
@@ -293,6 +322,7 @@ ifeq (yes,$(build-shared))
|
||||
@@ -300,6 +329,7 @@ ifeq (yes,$(build-shared))
|
||||
ifneq ($(PERL),no)
|
||||
tests-special += \
|
||||
$(foreach f,$(xprintf-stems),$(objpfx)$(f)-mem.out) \
|
||||
@ -107,7 +102,7 @@ index fe69e48849cb9819..df7afefc7a1f9828 100644
|
||||
$(objpfx)tst-freopen2-mem.out \
|
||||
$(objpfx)tst-freopen3-mem.out \
|
||||
$(objpfx)tst-freopen4-mem.out \
|
||||
@@ -314,6 +344,8 @@ tests-special += \
|
||||
@@ -321,6 +351,8 @@ tests-special += \
|
||||
generated += \
|
||||
$(foreach f,$(xprintf-stems),$(f)-mem.out) \
|
||||
$(foreach f,$(xprintf-stems),$(f).mtrace) \
|
||||
@ -116,7 +111,7 @@ index fe69e48849cb9819..df7afefc7a1f9828 100644
|
||||
tst-freopen2-mem.out \
|
||||
tst-freopen2.mtrace \
|
||||
tst-freopen3-mem.out \
|
||||
@@ -457,6 +489,26 @@ $(objpfx)tst-printf-format-%.out: \
|
||||
@@ -468,6 +500,26 @@ $(objpfx)tst-printf-format-%.out: \
|
||||
$(make-tst-printf-format-out) > $@; \
|
||||
$(evaluate-test)
|
||||
|
||||
@ -143,7 +138,7 @@ index fe69e48849cb9819..df7afefc7a1f9828 100644
|
||||
$(objpfx)tst-printfsz-islongdouble.out: \
|
||||
tst-printfsz-islongdouble.sh $(objpfx)tst-printfsz-islongdouble
|
||||
$(SHELL) $^ '$(test-program-prefix)' $@; \
|
||||
@@ -542,5 +594,13 @@ $(objpfx)tst-setvbuf1-cmp.out: tst-setvbuf1.expect $(objpfx)tst-setvbuf1.out
|
||||
@@ -553,5 +605,13 @@ $(objpfx)tst-setvbuf1-cmp.out: tst-setvbuf1.expect $(objpfx)tst-setvbuf1.out
|
||||
$(objpfx)tst-printf-round: $(libm)
|
||||
$(objpfx)tst-scanf-round: $(libm)
|
||||
|
||||
@ -10,7 +10,7 @@ Date: Fri Mar 28 12:35:52 2025 +0000
|
||||
Reviewed-by: Joseph Myers <josmyers@redhat.com>
|
||||
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index df7afefc7a1f9828..e8e06f194e6592b6 100644
|
||||
index 15525a0f768244c6..74d5e02c7206cabc 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -52,7 +52,7 @@ nonfmt-xprintf-stems := \
|
||||
@ -10,7 +10,7 @@ Date: Fri Mar 28 12:35:52 2025 +0000
|
||||
Reviewed-by: Joseph Myers <josmyers@redhat.com>
|
||||
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index e8e06f194e6592b6..2892f680c069836a 100644
|
||||
index 74d5e02c7206cabc..3e165685af09a1c3 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -52,7 +52,7 @@ nonfmt-xprintf-stems := \
|
||||
@ -10,7 +10,7 @@ Date: Fri Mar 28 12:35:52 2025 +0000
|
||||
Reviewed-by: Joseph Myers <josmyers@redhat.com>
|
||||
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index 2892f680c069836a..7cad1ddeb6f57998 100644
|
||||
index 3e165685af09a1c3..3a09b5bfbc930b45 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -52,7 +52,7 @@ nonfmt-xprintf-stems := \
|
||||
@ -10,7 +10,7 @@ Date: Fri Mar 28 12:35:53 2025 +0000
|
||||
Reviewed-by: Joseph Myers <josmyers@redhat.com>
|
||||
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index 7cad1ddeb6f57998..fd3c86cc7df68e57 100644
|
||||
index 3a09b5bfbc930b45..13c50f07ccfc86c3 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -52,7 +52,7 @@ nonfmt-xprintf-stems := \
|
||||
@ -10,7 +10,7 @@ Date: Fri Mar 28 12:35:53 2025 +0000
|
||||
Reviewed-by: Joseph Myers <josmyers@redhat.com>
|
||||
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index fd3c86cc7df68e57..3cb9da43d20bb31a 100644
|
||||
index 13c50f07ccfc86c3..1aefe3702800bd73 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -52,7 +52,7 @@ nonfmt-xprintf-stems := \
|
||||
@ -6,7 +6,7 @@ Also exclude failing nan(...) floating-point input patterns because
|
||||
swbz#30647 has not been fixed downstream.
|
||||
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index 3cb9da43d20bb31a..a18c87741ae42a61 100644
|
||||
index 1aefe3702800bd73..7573dee90d264e16 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -64,7 +64,7 @@ fmts-xscanf-int := d i
|
||||
562
SOURCES/glibc-RHEL-47403-1.patch
Normal file
562
SOURCES/glibc-RHEL-47403-1.patch
Normal file
@ -0,0 +1,562 @@
|
||||
commit a93d9e03a31ec14405cb3a09aa95413b67067380
|
||||
Author: H.J. Lu <hjl.tools@gmail.com>
|
||||
Date: Tue Aug 17 19:35:48 2021 -0700
|
||||
|
||||
Extend struct r_debug to support multiple namespaces [BZ #15971]
|
||||
|
||||
Glibc does not provide an interface for debugger to access libraries
|
||||
loaded in multiple namespaces via dlmopen.
|
||||
|
||||
The current rtld-debugger interface is described in the file:
|
||||
|
||||
elf/rtld-debugger-interface.txt
|
||||
|
||||
under the "Standard debugger interface" heading. This interface only
|
||||
provides access to the first link-map (LM_ID_BASE).
|
||||
|
||||
1. Bump r_version to 2 when multiple namespaces are used. This triggers
|
||||
the GDB bug:
|
||||
|
||||
https://sourceware.org/bugzilla/show_bug.cgi?id=28236
|
||||
|
||||
2. Add struct r_debug_extended to extend struct r_debug into a linked-list,
|
||||
where each element correlates to an unique namespace.
|
||||
3. Initialize the r_debug_extended structure. Bump r_version to 2 for
|
||||
the new namespace and add the new namespace to the namespace linked list.
|
||||
4. Add _dl_debug_update to return the address of struct r_debug' of a
|
||||
namespace.
|
||||
5. Add a hidden symbol, _r_debug_extended, for struct r_debug_extended.
|
||||
6. Provide the symbol, _r_debug, with size of struct r_debug, as an alias
|
||||
of _r_debug_extended, for programs which reference _r_debug.
|
||||
|
||||
This fixes BZ #15971.
|
||||
|
||||
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile (fixup context due to reordering)
|
||||
elf/dl-open.c (modified line didn't exist downstream)
|
||||
|
||||
diff --git a/csu/Makefile b/csu/Makefile
|
||||
index 3054329cea4a276d..e2390e4a7deb6941 100644
|
||||
--- a/csu/Makefile
|
||||
+++ b/csu/Makefile
|
||||
@@ -88,6 +88,9 @@ endif
|
||||
before-compile += $(objpfx)abi-tag.h
|
||||
generated += abi-tag.h
|
||||
|
||||
+# Put it here to generate it earlier.
|
||||
+gen-as-const-headers += rtld-sizes.sym
|
||||
+
|
||||
# These are the special initializer/finalizer files. They are always the
|
||||
# first and last file in the link. crti.o ... crtn.o define the global
|
||||
# "functions" _init and _fini to run the .init and .fini sections.
|
||||
diff --git a/csu/rtld-sizes.sym b/csu/rtld-sizes.sym
|
||||
new file mode 100644
|
||||
index 0000000000000000..13924d5efdbaa248
|
||||
--- /dev/null
|
||||
+++ b/csu/rtld-sizes.sym
|
||||
@@ -0,0 +1,6 @@
|
||||
+#include <link.h>
|
||||
+
|
||||
+--
|
||||
+R_DEBUG_SIZE sizeof (struct r_debug)
|
||||
+R_DEBUG_EXTENDED_SIZE sizeof (struct r_debug_extended)
|
||||
+R_DEBUG_EXTENDED_ALIGN __alignof (struct r_debug_extended)
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index 15bec14364266c77..b074cc29664b3e20 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -54,6 +54,7 @@ dl-routines = \
|
||||
dl-catch \
|
||||
dl-close \
|
||||
dl-debug \
|
||||
+ dl-debug-symbols \
|
||||
dl-deps \
|
||||
dl-exception \
|
||||
dl-execstack \
|
||||
@@ -399,6 +400,7 @@ tests += \
|
||||
tst-dlmodcount \
|
||||
tst-dlmopen1 \
|
||||
tst-dlmopen3 \
|
||||
+ tst-dlmopen4 \
|
||||
tst-dlmopen-dlerror \
|
||||
tst-dlmopen-gethostbyname \
|
||||
tst-dlmopen-twice \
|
||||
@@ -1973,6 +1975,8 @@ $(objpfx)tst-dlmopen2.out: $(objpfx)tst-dlmopen1mod.so
|
||||
|
||||
$(objpfx)tst-dlmopen3.out: $(objpfx)tst-dlmopen1mod.so
|
||||
|
||||
+$(objpfx)tst-dlmopen4.out: $(objpfx)tst-dlmopen1mod.so
|
||||
+
|
||||
$(objpfx)tst-audit1.out: $(objpfx)tst-auditmod1.so
|
||||
tst-audit1-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
|
||||
|
||||
diff --git a/elf/dl-close.c b/elf/dl-close.c
|
||||
index 9d158c25498fd8ae..bf699e58d753a1e2 100644
|
||||
--- a/elf/dl-close.c
|
||||
+++ b/elf/dl-close.c
|
||||
@@ -469,7 +469,7 @@ _dl_close_worker (struct link_map *map, bool force)
|
||||
#endif
|
||||
|
||||
/* Notify the debugger we are about to remove some loaded objects. */
|
||||
- struct r_debug *r = _dl_debug_initialize (0, nsid);
|
||||
+ struct r_debug *r = _dl_debug_update (nsid);
|
||||
r->r_state = RT_DELETE;
|
||||
_dl_debug_state ();
|
||||
LIBC_PROBE (unmap_start, 2, nsid, r);
|
||||
diff --git a/elf/dl-debug-symbols.S b/elf/dl-debug-symbols.S
|
||||
new file mode 100644
|
||||
index 0000000000000000..b7e9f5d9470c4da2
|
||||
--- /dev/null
|
||||
+++ b/elf/dl-debug-symbols.S
|
||||
@@ -0,0 +1,36 @@
|
||||
+/* Define symbols used to communicate dynamic linker state to the
|
||||
+ debugger at runtime.
|
||||
+ 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
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <rtld-sizes.h>
|
||||
+
|
||||
+/* Define 2 symbols, _r_debug_extended and _r_debug, which is an alias
|
||||
+ of _r_debug_extended, but with the size of struct r_debug. */
|
||||
+
|
||||
+ .globl _r_debug
|
||||
+ .type _r_debug, %object
|
||||
+ .size _r_debug, R_DEBUG_SIZE
|
||||
+ .hidden _r_debug_extended
|
||||
+ .globl _r_debug_extended
|
||||
+ .type _r_debug_extended, %object
|
||||
+ .size _r_debug_extended, R_DEBUG_EXTENDED_SIZE
|
||||
+ .section .bss
|
||||
+ .balign R_DEBUG_EXTENDED_ALIGN
|
||||
+_r_debug:
|
||||
+_r_debug_extended:
|
||||
+ .zero R_DEBUG_EXTENDED_SIZE
|
||||
diff --git a/elf/dl-debug.c b/elf/dl-debug.c
|
||||
index 2cd5f0975380445c..f637d4bb8de3db8c 100644
|
||||
--- a/elf/dl-debug.c
|
||||
+++ b/elf/dl-debug.c
|
||||
@@ -30,37 +30,80 @@ extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr)
|
||||
&& VERIFY_MEMBER (l_prev))
|
||||
? 1 : -1];
|
||||
|
||||
-/* This structure communicates dl state to the debugger. The debugger
|
||||
- normally finds it via the DT_DEBUG entry in the dynamic section, but in
|
||||
- a statically-linked program there is no dynamic section for the debugger
|
||||
- to examine and it looks for this particular symbol name. */
|
||||
-struct r_debug _r_debug;
|
||||
+/* Update the `r_map' member and return the address of `struct r_debug'
|
||||
+ of the namespace NS. */
|
||||
|
||||
+struct r_debug *
|
||||
+_dl_debug_update (Lmid_t ns)
|
||||
+{
|
||||
+ struct r_debug_extended *r;
|
||||
+ if (ns == LM_ID_BASE)
|
||||
+ r = &_r_debug_extended;
|
||||
+ else
|
||||
+ r = &GL(dl_ns)[ns]._ns_debug;
|
||||
+ if (r->base.r_map == NULL)
|
||||
+ atomic_store_release (&r->base.r_map,
|
||||
+ (void *) GL(dl_ns)[ns]._ns_loaded);
|
||||
+ return &r->base;
|
||||
+}
|
||||
|
||||
-/* Initialize _r_debug if it has not already been done. The argument is
|
||||
- the run-time load address of the dynamic linker, to be put in
|
||||
- _r_debug.r_ldbase. Returns the address of _r_debug. */
|
||||
+/* Initialize _r_debug_extended for the namespace NS. LDBASE is the
|
||||
+ run-time load address of the dynamic linker, to be put in
|
||||
+ _r_debug_extended.r_ldbase. Return the address of _r_debug. */
|
||||
|
||||
struct r_debug *
|
||||
_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
|
||||
{
|
||||
- struct r_debug *r;
|
||||
+ struct r_debug_extended *r, **pp = NULL;
|
||||
|
||||
if (ns == LM_ID_BASE)
|
||||
- r = &_r_debug;
|
||||
- else
|
||||
- r = &GL(dl_ns)[ns]._ns_debug;
|
||||
+ {
|
||||
+ r = &_r_debug_extended;
|
||||
+ /* Initialize r_version to 1. */
|
||||
+ if (_r_debug_extended.base.r_version == 0)
|
||||
+ _r_debug_extended.base.r_version = 1;
|
||||
+ }
|
||||
+ else if (DL_NNS > 1)
|
||||
+ {
|
||||
+ r = &GL(dl_ns)[ns]._ns_debug;
|
||||
+ if (r->base.r_brk == 0)
|
||||
+ {
|
||||
+ /* Add the new namespace to the linked list. After a namespace
|
||||
+ is initialized, r_brk becomes non-zero. A namespace becomes
|
||||
+ empty (r_map == NULL) when it is unused. But it is never
|
||||
+ removed from the linked list. */
|
||||
+ struct r_debug_extended *p;
|
||||
+ for (pp = &_r_debug_extended.r_next;
|
||||
+ (p = *pp) != NULL;
|
||||
+ pp = &p->r_next)
|
||||
+ ;
|
||||
+
|
||||
+ r->base.r_version = 2;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (r->base.r_brk == 0)
|
||||
+ {
|
||||
+ /* Tell the debugger where to find the map of loaded objects.
|
||||
+ This function is called from dlopen. Initialize the namespace
|
||||
+ only once. */
|
||||
+ r->base.r_ldbase = ldbase ?: _r_debug_extended.base.r_ldbase;
|
||||
+ r->base.r_brk = (ElfW(Addr)) &_dl_debug_state;
|
||||
+ r->r_next = NULL;
|
||||
+ }
|
||||
+
|
||||
+ if (r->base.r_map == NULL)
|
||||
+ atomic_store_release (&r->base.r_map,
|
||||
+ (void *) GL(dl_ns)[ns]._ns_loaded);
|
||||
|
||||
- if (r->r_map == NULL || ldbase != 0)
|
||||
+ if (pp != NULL)
|
||||
{
|
||||
- /* Tell the debugger where to find the map of loaded objects. */
|
||||
- r->r_version = 1 /* R_DEBUG_VERSION XXX */;
|
||||
- r->r_ldbase = ldbase ?: _r_debug.r_ldbase;
|
||||
- r->r_map = (void *) GL(dl_ns)[ns]._ns_loaded;
|
||||
- r->r_brk = (ElfW(Addr)) &_dl_debug_state;
|
||||
+ atomic_store_release (pp, r);
|
||||
+ /* Bump r_version to 2 for the new namespace. */
|
||||
+ atomic_store_release (&_r_debug_extended.base.r_version, 2);
|
||||
}
|
||||
|
||||
- return r;
|
||||
+ return &r->base;
|
||||
}
|
||||
|
||||
|
||||
diff --git a/elf/dl-load.c b/elf/dl-load.c
|
||||
index eb6b658b698f5694..5b0734c816b351f0 100644
|
||||
--- a/elf/dl-load.c
|
||||
+++ b/elf/dl-load.c
|
||||
@@ -950,7 +950,7 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
/* Initialize to keep the compiler happy. */
|
||||
const char *errstring = NULL;
|
||||
int errval = 0;
|
||||
- struct r_debug *r = _dl_debug_initialize (0, nsid);
|
||||
+ struct r_debug *r = _dl_debug_update (nsid);
|
||||
bool make_consistent = false;
|
||||
|
||||
/* Get file information. To match the kernel behavior, do not fill
|
||||
diff --git a/elf/dl-open.c b/elf/dl-open.c
|
||||
index 80af6518d4395906..eef724f7e9b2211d 100644
|
||||
--- a/elf/dl-open.c
|
||||
+++ b/elf/dl-open.c
|
||||
@@ -596,7 +596,7 @@ dl_open_worker_begin (void *a)
|
||||
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
|
||||
add_to_global_update (new);
|
||||
|
||||
- assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
|
||||
+ assert (_dl_debug_update (args->nsid)->r_state == RT_CONSISTENT);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -634,7 +634,7 @@ dl_open_worker_begin (void *a)
|
||||
#endif
|
||||
|
||||
/* Notify the debugger all new objects are now ready to go. */
|
||||
- struct r_debug *r = _dl_debug_initialize (0, args->nsid);
|
||||
+ struct r_debug *r = _dl_debug_update (args->nsid);
|
||||
r->r_state = RT_CONSISTENT;
|
||||
_dl_debug_state ();
|
||||
LIBC_PROBE (map_complete, 3, args->nsid, r, new);
|
||||
@@ -840,7 +840,7 @@ no more namespaces available for dlmopen()"));
|
||||
}
|
||||
|
||||
GL(dl_ns)[nsid].libc_map = NULL;
|
||||
- _dl_debug_initialize (0, nsid)->r_state = RT_CONSISTENT;
|
||||
+ _dl_debug_update (nsid)->r_state = RT_CONSISTENT;
|
||||
}
|
||||
/* Never allow loading a DSO in a namespace which is empty. Such
|
||||
direct placements is only causing problems. Also don't allow
|
||||
@@ -916,7 +916,7 @@ no more namespaces available for dlmopen()"));
|
||||
_dl_signal_exception (errcode, &exception, NULL);
|
||||
}
|
||||
|
||||
- assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
|
||||
+ assert (_dl_debug_update (args.nsid)->r_state == RT_CONSISTENT);
|
||||
|
||||
/* Release the lock. */
|
||||
__rtld_lock_unlock_recursive (GL(dl_load_lock));
|
||||
diff --git a/elf/dl-reloc-static-pie.c b/elf/dl-reloc-static-pie.c
|
||||
index 757205affe65d9e1..5b85df8a2eed1888 100644
|
||||
--- a/elf/dl-reloc-static-pie.c
|
||||
+++ b/elf/dl-reloc-static-pie.c
|
||||
@@ -62,7 +62,7 @@ _dl_relocate_static_pie (void)
|
||||
ELF_DYNAMIC_RELOCATE (main_map, NULL, 0, 0, 0);
|
||||
main_map->l_relocated = 1;
|
||||
|
||||
- /* Initialize _r_debug. */
|
||||
+ /* Initialize _r_debug_extended. */
|
||||
struct r_debug *r = _dl_debug_initialize (0, LM_ID_BASE);
|
||||
r->r_state = RT_CONSISTENT;
|
||||
|
||||
diff --git a/elf/link.h b/elf/link.h
|
||||
index 21a351686b9bf7c8..0906bbe33fbd9f8f 100644
|
||||
--- a/elf/link.h
|
||||
+++ b/elf/link.h
|
||||
@@ -34,14 +34,13 @@
|
||||
#include <bits/elfclass.h> /* Defines __ELF_NATIVE_CLASS. */
|
||||
#include <bits/link.h>
|
||||
|
||||
-/* Rendezvous structure used by the run-time dynamic linker to communicate
|
||||
- details of shared object loading to the debugger. If the executable's
|
||||
- dynamic section has a DT_DEBUG element, the run-time linker sets that
|
||||
- element's value to the address where this structure can be found. */
|
||||
+/* The legacy rendezvous structure used by the run-time dynamic linker to
|
||||
+ communicate details of shared object loading to the debugger. */
|
||||
|
||||
struct r_debug
|
||||
{
|
||||
- int r_version; /* Version number for this protocol. */
|
||||
+ /* Version number for this protocol. It should be greater than 0. */
|
||||
+ int r_version;
|
||||
|
||||
struct link_map *r_map; /* Head of the chain of loaded objects. */
|
||||
|
||||
@@ -63,16 +62,34 @@ struct r_debug
|
||||
ElfW(Addr) r_ldbase; /* Base address the linker is loaded at. */
|
||||
};
|
||||
|
||||
-/* This is the instance of that structure used by the dynamic linker. */
|
||||
+/* This is the symbol of that structure provided by the dynamic linker. */
|
||||
extern struct r_debug _r_debug;
|
||||
|
||||
+/* The extended rendezvous structure used by the run-time dynamic linker
|
||||
+ to communicate details of shared object loading to the debugger. If
|
||||
+ the executable's dynamic section has a DT_DEBUG element, the run-time
|
||||
+ linker sets that element's value to the address where this structure
|
||||
+ can be found. */
|
||||
+
|
||||
+struct r_debug_extended
|
||||
+ {
|
||||
+ struct r_debug base;
|
||||
+
|
||||
+ /* The following field is added by r_version == 2. */
|
||||
+
|
||||
+ /* Link to the next r_debug_extended structure. Each r_debug_extended
|
||||
+ structure represents a different namespace. The first
|
||||
+ r_debug_extended structure is for the default namespace. */
|
||||
+ struct r_debug_extended *r_next;
|
||||
+ };
|
||||
+
|
||||
/* This symbol refers to the "dynamic structure" in the `.dynamic' section
|
||||
of whatever module refers to `_DYNAMIC'. So, to find its own
|
||||
- `struct r_debug', a program could do:
|
||||
+ `struct r_debug_extended', a program could do:
|
||||
for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; ++dyn)
|
||||
if (dyn->d_tag == DT_DEBUG)
|
||||
- r_debug = (struct r_debug *) dyn->d_un.d_ptr;
|
||||
- */
|
||||
+ r_debug_extended = (struct r_debug_extended *) dyn->d_un.d_ptr;
|
||||
+ */
|
||||
extern ElfW(Dyn) _DYNAMIC[];
|
||||
|
||||
/* Structure describing a loaded shared object. The `l_next' and `l_prev'
|
||||
diff --git a/elf/rtld-debugger-interface.txt b/elf/rtld-debugger-interface.txt
|
||||
index 61bc99e4b086612f..f3476d8308616f84 100644
|
||||
--- a/elf/rtld-debugger-interface.txt
|
||||
+++ b/elf/rtld-debugger-interface.txt
|
||||
@@ -9,6 +9,9 @@ structure can be found.
|
||||
|
||||
The r_debug structure contains (amongst others) the following fields:
|
||||
|
||||
+ int r_version:
|
||||
+ Version number for this protocol. It should be greater than 0.
|
||||
+
|
||||
struct link_map *r_map:
|
||||
A linked list of loaded objects.
|
||||
|
||||
@@ -32,6 +35,18 @@ but there is no way for the debugger to discover whether any of the
|
||||
objects in the link-map have been relocated or not.
|
||||
|
||||
|
||||
+Extension to the r_debug structure
|
||||
+==================================
|
||||
+
|
||||
+The r_debug_extended structure is an extension of the r_debug interface.
|
||||
+If r_version is 2, one additional field is available:
|
||||
+
|
||||
+ struct r_debug_extended *r_next;
|
||||
+ Link to the next r_debug_extended structure. Each r_debug_extended
|
||||
+ structure represents a different namespace. A namespace is active
|
||||
+ if its r_map field isn't NULL. The first r_debug_extended structure
|
||||
+ is for the default namespace.
|
||||
+
|
||||
Probe-based debugger interface
|
||||
==============================
|
||||
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index d3d9e6b904ac78fd..7d8ed0ac1188d527 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -1725,7 +1725,7 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
objects. */
|
||||
call_init_paths (&state);
|
||||
|
||||
- /* Initialize _r_debug. */
|
||||
+ /* Initialize _r_debug_extended. */
|
||||
struct r_debug *r = _dl_debug_initialize (GL(dl_rtld_map).l_addr,
|
||||
LM_ID_BASE);
|
||||
r->r_state = RT_CONSISTENT;
|
||||
@@ -2531,7 +2531,7 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
|
||||
/* Notify the debugger all new objects are now ready to go. We must re-get
|
||||
the address since by now the variable might be in another object. */
|
||||
- r = _dl_debug_initialize (0, LM_ID_BASE);
|
||||
+ r = _dl_debug_update (LM_ID_BASE);
|
||||
r->r_state = RT_CONSISTENT;
|
||||
_dl_debug_state ();
|
||||
LIBC_PROBE (init_complete, 2, LM_ID_BASE, r);
|
||||
diff --git a/elf/tst-dlmopen4.c b/elf/tst-dlmopen4.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..3fe150e50bc259f0
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dlmopen4.c
|
||||
@@ -0,0 +1,72 @@
|
||||
+/* Test struct r_debug_extended via DT_DEBUG.
|
||||
+ 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
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <link.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <string.h>
|
||||
+#include <gnu/lib-names.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/test-driver.h>
|
||||
+
|
||||
+#ifndef ELF_MACHINE_GET_R_DEBUG
|
||||
+# define ELF_MACHINE_GET_R_DEBUG(d) \
|
||||
+ (__extension__ ({ \
|
||||
+ struct r_debug_extended *debug; \
|
||||
+ if ((d)->d_tag == DT_DEBUG) \
|
||||
+ debug = (struct r_debug_extended *) (d)->d_un.d_ptr; \
|
||||
+ else \
|
||||
+ debug = NULL; \
|
||||
+ debug; }))
|
||||
+#endif
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ ElfW(Dyn) *d;
|
||||
+ struct r_debug_extended *debug = NULL;
|
||||
+
|
||||
+ for (d = _DYNAMIC; d->d_tag != DT_NULL; ++d)
|
||||
+ {
|
||||
+ debug = ELF_MACHINE_GET_R_DEBUG (d);
|
||||
+ if (debug != NULL)
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ TEST_VERIFY_EXIT (debug != NULL);
|
||||
+ TEST_COMPARE (debug->base.r_version, 1);
|
||||
+ TEST_VERIFY_EXIT (debug->r_next == NULL);
|
||||
+
|
||||
+ void *h = xdlmopen (LM_ID_NEWLM, "$ORIGIN/tst-dlmopen1mod.so",
|
||||
+ RTLD_LAZY);
|
||||
+
|
||||
+ TEST_COMPARE (debug->base.r_version, 2);
|
||||
+ TEST_VERIFY_EXIT (debug->r_next != NULL);
|
||||
+ TEST_VERIFY_EXIT (debug->r_next->r_next == NULL);
|
||||
+ TEST_VERIFY_EXIT (debug->r_next->base.r_map != NULL);
|
||||
+ TEST_VERIFY_EXIT (debug->r_next->base.r_map->l_name != NULL);
|
||||
+ const char *name = basename (debug->r_next->base.r_map->l_name);
|
||||
+ TEST_COMPARE_STRING (name, "tst-dlmopen1mod.so");
|
||||
+
|
||||
+ xdlclose (h);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
diff --git a/include/link.h b/include/link.h
|
||||
index 0dc63ef37d6a5666..0cf130ddb8af2e89 100644
|
||||
--- a/include/link.h
|
||||
+++ b/include/link.h
|
||||
@@ -362,6 +362,10 @@ struct auditstate
|
||||
};
|
||||
|
||||
|
||||
+/* This is the hidden instance of struct r_debug_extended used by the
|
||||
+ dynamic linker. */
|
||||
+extern struct r_debug_extended _r_debug_extended attribute_hidden;
|
||||
+
|
||||
#if __ELF_NATIVE_CLASS == 32
|
||||
# define symbind symbind32
|
||||
# define LA_SYMBIND "la_symbind32"
|
||||
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
||||
index dc4e0555e4ed7f3c..695f3910234e87da 100644
|
||||
--- a/sysdeps/generic/ldsodefs.h
|
||||
+++ b/sysdeps/generic/ldsodefs.h
|
||||
@@ -376,7 +376,7 @@ struct rtld_global
|
||||
void (*free) (void *);
|
||||
} _ns_unique_sym_table;
|
||||
/* Keep track of changes to each namespace' list. */
|
||||
- struct r_debug _ns_debug;
|
||||
+ struct r_debug_extended _ns_debug;
|
||||
} _dl_ns[DL_NNS];
|
||||
/* One higher than index of last used namespace. */
|
||||
EXTERN size_t _dl_nns;
|
||||
@@ -1128,12 +1128,16 @@ extern void _dl_sort_maps (struct link_map **maps, unsigned int nmaps,
|
||||
extern void _dl_debug_state (void);
|
||||
rtld_hidden_proto (_dl_debug_state)
|
||||
|
||||
-/* Initialize `struct r_debug' if it has not already been done. The
|
||||
- argument is the run-time load address of the dynamic linker, to be put
|
||||
- in the `r_ldbase' member. Returns the address of the structure. */
|
||||
+/* Initialize `struct r_debug_extended' for the namespace NS. LDBASE
|
||||
+ is the run-time load address of the dynamic linker, to be put in the
|
||||
+ `r_ldbase' member. Return the address of the structure. */
|
||||
extern struct r_debug *_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
|
||||
attribute_hidden;
|
||||
|
||||
+/* Update the `r_map' member and return the address of `struct r_debug'
|
||||
+ of the namespace NS. */
|
||||
+extern struct r_debug *_dl_debug_update (Lmid_t ns) attribute_hidden;
|
||||
+
|
||||
/* Initialize the basic data structure for the search paths. SOURCE
|
||||
is either "LD_LIBRARY_PATH" or "--library-path".
|
||||
GLIBC_HWCAPS_PREPEND adds additional glibc-hwcaps subdirectories to
|
||||
158
SOURCES/glibc-RHEL-47403-10.patch
Normal file
158
SOURCES/glibc-RHEL-47403-10.patch
Normal file
@ -0,0 +1,158 @@
|
||||
commit 7278d11f3a0cd528188c719bab75575b0aea2c6e
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Jul 4 21:46:05 2025 +0200
|
||||
|
||||
elf: Introduce separate _r_debug_array variable
|
||||
|
||||
It replaces the ns_debug member of the namespaces. Previously,
|
||||
the base namespace had an unused ns_debug member.
|
||||
|
||||
This change also fixes a concurrency issue: Now _dl_debug_initialize
|
||||
only updates r_next of the previous namespace's r_debug after the new
|
||||
r_debug is initialized, so that only the initialized version is
|
||||
observed. (Client code accessing _r_debug will benefit from load
|
||||
dependency tracking in CPUs even without explicit barriers.)
|
||||
|
||||
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
|
||||
|
||||
diff --git a/elf/dl-debug.c b/elf/dl-debug.c
|
||||
index f637d4bb8de3db8c..649386d5a6b885ed 100644
|
||||
--- a/elf/dl-debug.c
|
||||
+++ b/elf/dl-debug.c
|
||||
@@ -30,17 +30,37 @@ extern const int verify_link_map_members[(VERIFY_MEMBER (l_addr)
|
||||
&& VERIFY_MEMBER (l_prev))
|
||||
? 1 : -1];
|
||||
|
||||
+#ifdef SHARED
|
||||
+/* r_debug structs for secondary namespaces. The first namespace is
|
||||
+ handled separately because its r_debug structure must overlap with
|
||||
+ the public _r_debug symbol, so the first array element corresponds
|
||||
+ to LM_ID_BASE + 1. See elf/dl-debug-symbols.S. */
|
||||
+struct r_debug_extended _r_debug_array[DL_NNS - 1];
|
||||
+
|
||||
+/* Return the r_debug object for the namespace NS. */
|
||||
+static inline struct r_debug_extended *
|
||||
+get_rdebug (Lmid_t ns)
|
||||
+{
|
||||
+ if (ns == LM_ID_BASE)
|
||||
+ return &_r_debug_extended;
|
||||
+ else
|
||||
+ return &_r_debug_array[ns - 1];
|
||||
+}
|
||||
+#else /* !SHARED */
|
||||
+static inline struct r_debug_extended *
|
||||
+get_rdebug (Lmid_t ns)
|
||||
+{
|
||||
+ return &_r_debug_extended; /* There is just one namespace. */
|
||||
+}
|
||||
+#endif /* !SHARED */
|
||||
+
|
||||
/* Update the `r_map' member and return the address of `struct r_debug'
|
||||
of the namespace NS. */
|
||||
|
||||
struct r_debug *
|
||||
_dl_debug_update (Lmid_t ns)
|
||||
{
|
||||
- struct r_debug_extended *r;
|
||||
- if (ns == LM_ID_BASE)
|
||||
- r = &_r_debug_extended;
|
||||
- else
|
||||
- r = &GL(dl_ns)[ns]._ns_debug;
|
||||
+ struct r_debug_extended *r = get_rdebug (ns);
|
||||
if (r->base.r_map == NULL)
|
||||
atomic_store_release (&r->base.r_map,
|
||||
(void *) GL(dl_ns)[ns]._ns_loaded);
|
||||
@@ -54,34 +74,7 @@ _dl_debug_update (Lmid_t ns)
|
||||
struct r_debug *
|
||||
_dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
|
||||
{
|
||||
- struct r_debug_extended *r, **pp = NULL;
|
||||
-
|
||||
- if (ns == LM_ID_BASE)
|
||||
- {
|
||||
- r = &_r_debug_extended;
|
||||
- /* Initialize r_version to 1. */
|
||||
- if (_r_debug_extended.base.r_version == 0)
|
||||
- _r_debug_extended.base.r_version = 1;
|
||||
- }
|
||||
- else if (DL_NNS > 1)
|
||||
- {
|
||||
- r = &GL(dl_ns)[ns]._ns_debug;
|
||||
- if (r->base.r_brk == 0)
|
||||
- {
|
||||
- /* Add the new namespace to the linked list. After a namespace
|
||||
- is initialized, r_brk becomes non-zero. A namespace becomes
|
||||
- empty (r_map == NULL) when it is unused. But it is never
|
||||
- removed from the linked list. */
|
||||
- struct r_debug_extended *p;
|
||||
- for (pp = &_r_debug_extended.r_next;
|
||||
- (p = *pp) != NULL;
|
||||
- pp = &p->r_next)
|
||||
- ;
|
||||
-
|
||||
- r->base.r_version = 2;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
+ struct r_debug_extended *r = get_rdebug (ns);
|
||||
if (r->base.r_brk == 0)
|
||||
{
|
||||
/* Tell the debugger where to find the map of loaded objects.
|
||||
@@ -89,20 +82,36 @@ _dl_debug_initialize (ElfW(Addr) ldbase, Lmid_t ns)
|
||||
only once. */
|
||||
r->base.r_ldbase = ldbase ?: _r_debug_extended.base.r_ldbase;
|
||||
r->base.r_brk = (ElfW(Addr)) &_dl_debug_state;
|
||||
- r->r_next = NULL;
|
||||
+
|
||||
+#ifdef SHARED
|
||||
+ /* Add the new namespace to the linked list. This assumes that
|
||||
+ namespaces are allocated in increasing order. After a
|
||||
+ namespace is initialized, r_brk becomes non-zero. A
|
||||
+ namespace becomes empty (r_map == NULL) when it is unused.
|
||||
+ But it is never removed from the linked list. */
|
||||
+
|
||||
+ if (ns != LM_ID_BASE)
|
||||
+ {
|
||||
+ r->base.r_version = 2;
|
||||
+ if (ns - 1 == LM_ID_BASE)
|
||||
+ {
|
||||
+ atomic_store_release (&_r_debug_extended.r_next, r);
|
||||
+ /* Now there are multiple namespaces. */
|
||||
+ atomic_store_release (&_r_debug_extended.base.r_version, 2);
|
||||
+ }
|
||||
+ else
|
||||
+ /* Update r_debug_extended of the previous namespace. */
|
||||
+ atomic_store_release (&_r_debug_array[ns - 2].r_next, r);
|
||||
+ }
|
||||
+ else
|
||||
+#endif /* SHARED */
|
||||
+ r->base.r_version = 1;
|
||||
}
|
||||
|
||||
if (r->base.r_map == NULL)
|
||||
atomic_store_release (&r->base.r_map,
|
||||
(void *) GL(dl_ns)[ns]._ns_loaded);
|
||||
|
||||
- if (pp != NULL)
|
||||
- {
|
||||
- atomic_store_release (pp, r);
|
||||
- /* Bump r_version to 2 for the new namespace. */
|
||||
- atomic_store_release (&_r_debug_extended.base.r_version, 2);
|
||||
- }
|
||||
-
|
||||
return &r->base;
|
||||
}
|
||||
|
||||
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
||||
index 695f3910234e87da..e09edb01da3b5b90 100644
|
||||
--- a/sysdeps/generic/ldsodefs.h
|
||||
+++ b/sysdeps/generic/ldsodefs.h
|
||||
@@ -375,8 +375,6 @@ struct rtld_global
|
||||
size_t n_elements;
|
||||
void (*free) (void *);
|
||||
} _ns_unique_sym_table;
|
||||
- /* Keep track of changes to each namespace' list. */
|
||||
- struct r_debug_extended _ns_debug;
|
||||
} _dl_ns[DL_NNS];
|
||||
/* One higher than index of last used namespace. */
|
||||
EXTERN size_t _dl_nns;
|
||||
17
SOURCES/glibc-RHEL-47403-11.patch
Normal file
17
SOURCES/glibc-RHEL-47403-11.patch
Normal file
@ -0,0 +1,17 @@
|
||||
Downstream patch to keep ABI stable.
|
||||
Bring back a dummy `struct r_debug` member in `GL (dl_ns)`, to preserve
|
||||
`_rtld_global` layout.
|
||||
|
||||
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
||||
index e09edb01da3b5b90..3d9b90a22bfa6a7d 100644
|
||||
--- a/sysdeps/generic/ldsodefs.h
|
||||
+++ b/sysdeps/generic/ldsodefs.h
|
||||
@@ -375,6 +375,8 @@ struct rtld_global
|
||||
size_t n_elements;
|
||||
void (*free) (void *);
|
||||
} _ns_unique_sym_table;
|
||||
+ /* Dummy structure to keep the ABI stable. */
|
||||
+ struct r_debug _ns_debug_unused;
|
||||
} _dl_ns[DL_NNS];
|
||||
/* One higher than index of last used namespace. */
|
||||
EXTERN size_t _dl_nns;
|
||||
26
SOURCES/glibc-RHEL-47403-2.patch
Normal file
26
SOURCES/glibc-RHEL-47403-2.patch
Normal file
@ -0,0 +1,26 @@
|
||||
commit 7e84ac3a3ac9e7c4dc10de2ce65db971b9650e4d
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Mon Sep 20 15:50:00 2021 +0200
|
||||
|
||||
elf: Include <sysdep.h> in elf/dl-debug-symbols.S
|
||||
|
||||
This is necessary to generate assembler marker sections on some
|
||||
targets.
|
||||
|
||||
Reviewed-by: Szabolcs Nagy <szabolcs.nagy@arm.com>
|
||||
|
||||
diff --git a/elf/dl-debug-symbols.S b/elf/dl-debug-symbols.S
|
||||
index b7e9f5d9470c4da2..28456ab1f237ea87 100644
|
||||
--- a/elf/dl-debug-symbols.S
|
||||
+++ b/elf/dl-debug-symbols.S
|
||||
@@ -18,6 +18,10 @@
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <rtld-sizes.h>
|
||||
+#include <sysdep.h>
|
||||
+
|
||||
+/* Some targets define a macro to denote the zero register. */
|
||||
+#undef zero
|
||||
|
||||
/* Define 2 symbols, _r_debug_extended and _r_debug, which is an alias
|
||||
of _r_debug_extended, but with the size of struct r_debug. */
|
||||
32
SOURCES/glibc-RHEL-47403-3.patch
Normal file
32
SOURCES/glibc-RHEL-47403-3.patch
Normal file
@ -0,0 +1,32 @@
|
||||
commit 1b5e65ef6a442fdccf88d43c3048f98292d85631
|
||||
Author: Paul Pluzhnikov <ppluzhnikov@google.com>
|
||||
Date: Sat Mar 25 21:27:01 2023 +0000
|
||||
|
||||
Minor: don't call _dl_debug_update (which can have side effects) inside assert
|
||||
|
||||
diff --git a/elf/dl-open.c b/elf/dl-open.c
|
||||
index eef724f7e9b2211d..0d2b4cd4785a226a 100644
|
||||
--- a/elf/dl-open.c
|
||||
+++ b/elf/dl-open.c
|
||||
@@ -596,7 +596,9 @@ dl_open_worker_begin (void *a)
|
||||
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
|
||||
add_to_global_update (new);
|
||||
|
||||
- assert (_dl_debug_update (args->nsid)->r_state == RT_CONSISTENT);
|
||||
+ const int r_state __attribute__ ((unused))
|
||||
+ = _dl_debug_update (args->nsid)->r_state;
|
||||
+ assert (r_state == RT_CONSISTENT);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -916,7 +918,9 @@ no more namespaces available for dlmopen()"));
|
||||
_dl_signal_exception (errcode, &exception, NULL);
|
||||
}
|
||||
|
||||
- assert (_dl_debug_update (args.nsid)->r_state == RT_CONSISTENT);
|
||||
+ const int r_state __attribute__ ((unused))
|
||||
+ = _dl_debug_update (args.nsid)->r_state;
|
||||
+ assert (r_state == RT_CONSISTENT);
|
||||
|
||||
/* Release the lock. */
|
||||
__rtld_lock_unlock_recursive (GL(dl_load_lock));
|
||||
275
SOURCES/glibc-RHEL-47403-4.patch
Normal file
275
SOURCES/glibc-RHEL-47403-4.patch
Normal file
@ -0,0 +1,275 @@
|
||||
commit 9897ced8e78db5d813166a7ccccfd5a42c69ef20
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Oct 25 16:50:10 2024 +0200
|
||||
|
||||
elf: Run constructors on cyclic recursive dlopen (bug 31986)
|
||||
|
||||
This is conceptually similar to the reported bug, but does not
|
||||
depend on auditing. The fix is simple: just complete execution
|
||||
of the constructors. This exposed the fact that the link map
|
||||
for statically linked executables does not have l_init_called
|
||||
set, even though constructors have run.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile (fix local test re-ordering)
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index b074cc29664b3e20..dc774b083eda202b 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -398,12 +398,15 @@ tests += \
|
||||
tst-dl-is_dso \
|
||||
tst-dlclose-lazy \
|
||||
tst-dlmodcount \
|
||||
- tst-dlmopen1 \
|
||||
- tst-dlmopen3 \
|
||||
- tst-dlmopen4 \
|
||||
tst-dlmopen-dlerror \
|
||||
tst-dlmopen-gethostbyname \
|
||||
tst-dlmopen-twice \
|
||||
+ tst-dlmopen1 \
|
||||
+ tst-dlmopen3 \
|
||||
+ tst-dlmopen4 \
|
||||
+ tst-dlopen-recurse \
|
||||
+ tst-dlopen-self \
|
||||
+ tst-dlopen-tlsmodid \
|
||||
tst-dlopen-tlsreinit1 \
|
||||
tst-dlopen-tlsreinit2 \
|
||||
tst-dlopen-tlsreinit3 \
|
||||
@@ -411,8 +414,6 @@ tests += \
|
||||
tst-dlopenfail \
|
||||
tst-dlopenfail-2 \
|
||||
tst-dlopenrpath \
|
||||
- tst-dlopen-self \
|
||||
- tst-dlopen-tlsmodid \
|
||||
tst-dlsym-error \
|
||||
tst-filterobj \
|
||||
tst-filterobj-dlopen \
|
||||
@@ -775,13 +776,15 @@ modules-names = \
|
||||
tst-deep1mod1 \
|
||||
tst-deep1mod2 \
|
||||
tst-deep1mod3 \
|
||||
- tst-dlmopen1mod \
|
||||
tst-dlclose-lazy-mod1 \
|
||||
tst-dlclose-lazy-mod2 \
|
||||
tst-dlmopen-dlerror-mod \
|
||||
tst-dlmopen-gethostbyname-mod \
|
||||
tst-dlmopen-twice-mod1 \
|
||||
tst-dlmopen-twice-mod2 \
|
||||
+ tst-dlmopen1mod \
|
||||
+ tst-dlopen-recursemod1 \
|
||||
+ tst-dlopen-recursemod2 \
|
||||
tst-dlopen-sgid-mod \
|
||||
tst-dlopen-tlsreinitmod1 \
|
||||
tst-dlopen-tlsreinitmod2 \
|
||||
@@ -2856,6 +2859,9 @@ tst-dlopen-tlsreinit3-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
|
||||
$(objpfx)tst-dlopen-tlsreinit4.out: $(objpfx)tst-auditmod1.so
|
||||
tst-dlopen-tlsreinit4-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
|
||||
|
||||
+$(objpfx)tst-dlopen-recurse.out: $(objpfx)tst-dlopen-recursemod1.so
|
||||
+$(objpfx)tst-dlopen-recursemod1.so: $(objpfx)tst-dlopen-recursemod2.so
|
||||
+
|
||||
LDFLAGS-tst-hash-collision1-mod.so = -Wl,--hash-style=both
|
||||
$(objpfx)tst-hash-collision1: $(objpfx)tst-hash-collision1-mod.so
|
||||
LDFLAGS-tst-hash-collision1-mod-gnu.so = -Wl,--hash-style=gnu
|
||||
diff --git a/elf/dl-open.c b/elf/dl-open.c
|
||||
index 0d2b4cd4785a226a..0b0bfb8acda28caa 100644
|
||||
--- a/elf/dl-open.c
|
||||
+++ b/elf/dl-open.c
|
||||
@@ -600,6 +600,14 @@ dl_open_worker_begin (void *a)
|
||||
= _dl_debug_update (args->nsid)->r_state;
|
||||
assert (r_state == RT_CONSISTENT);
|
||||
|
||||
+ /* Do not return without calling the (supposedly new) map's
|
||||
+ constructor. This case occurs if a dependency of a directly
|
||||
+ opened map has a constructor that calls dlopen again on the
|
||||
+ initially opened map. The new map is initialized last, so
|
||||
+ checking only it is enough. */
|
||||
+ if (!new->l_init_called)
|
||||
+ _dl_catch_exception (NULL, call_dl_init, args);
|
||||
+
|
||||
return;
|
||||
}
|
||||
|
||||
diff --git a/elf/dl-support.c b/elf/dl-support.c
|
||||
index 00abc2d8056c78b0..f4dc9c61a2637f8b 100644
|
||||
--- a/elf/dl-support.c
|
||||
+++ b/elf/dl-support.c
|
||||
@@ -103,6 +103,7 @@ static struct link_map _dl_main_map =
|
||||
.l_used = 1,
|
||||
.l_tls_offset = NO_TLS_OFFSET,
|
||||
.l_serial = 1,
|
||||
+ .l_init_called = 1,
|
||||
};
|
||||
|
||||
/* Namespace information. */
|
||||
diff --git a/elf/tst-dlopen-recurse.c b/elf/tst-dlopen-recurse.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..c7fb379d373c6e77
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dlopen-recurse.c
|
||||
@@ -0,0 +1,34 @@
|
||||
+/* Test that recursive dlopen runs constructors before return (bug 31986).
|
||||
+ Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ void *handle = xdlopen ("tst-dlopen-recursemod1.so", RTLD_NOW);
|
||||
+ int *status = dlsym (handle, "recursemod1_status");
|
||||
+ printf ("info: recursemod1_status == %d (from main)\n", *status);
|
||||
+ TEST_COMPARE (*status, 2);
|
||||
+ xdlclose (handle);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
diff --git a/elf/tst-dlopen-recursemod1.c b/elf/tst-dlopen-recursemod1.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..5e0cc0eb8c32d6d4
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dlopen-recursemod1.c
|
||||
@@ -0,0 +1,50 @@
|
||||
+/* Directly opened test module that gets recursively opened again.
|
||||
+ Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+
|
||||
+int recursemod1_status;
|
||||
+
|
||||
+/* Force linking against st-dlopen-recursemod2.so. Also allows
|
||||
+ checking for relocation. */
|
||||
+extern int recursemod2_status;
|
||||
+int *force_recursemod2_reference = &recursemod2_status;
|
||||
+
|
||||
+static void __attribute__ ((constructor))
|
||||
+init (void)
|
||||
+{
|
||||
+ ++recursemod1_status;
|
||||
+ printf ("info: tst-dlopen-recursemod1.so constructor called (status %d)\n",
|
||||
+ recursemod1_status);
|
||||
+}
|
||||
+
|
||||
+static void __attribute__ ((destructor))
|
||||
+fini (void)
|
||||
+{
|
||||
+ /* The recursemod1_status variable was incremented in the
|
||||
+ tst-dlopen-recursemod2.so constructor. */
|
||||
+ printf ("info: tst-dlopen-recursemod1.so destructor called (status %d)\n",
|
||||
+ recursemod1_status);
|
||||
+ if (recursemod1_status != 2)
|
||||
+ {
|
||||
+ puts ("error: recursemod1_status == 2 expected");
|
||||
+ exit (1);
|
||||
+ }
|
||||
+}
|
||||
diff --git a/elf/tst-dlopen-recursemod2.c b/elf/tst-dlopen-recursemod2.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..edd2f2526b877810
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dlopen-recursemod2.c
|
||||
@@ -0,0 +1,66 @@
|
||||
+/* Indirectly opened module that recursively opens the directly opened module.
|
||||
+ Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <dlfcn.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+
|
||||
+int recursemod2_status;
|
||||
+
|
||||
+static void __attribute__ ((constructor))
|
||||
+init (void)
|
||||
+{
|
||||
+ ++recursemod2_status;
|
||||
+ printf ("info: tst-dlopen-recursemod2.so constructor called (status %d)\n",
|
||||
+ recursemod2_status);
|
||||
+ void *handle = dlopen ("tst-dlopen-recursemod1.so", RTLD_NOW);
|
||||
+ if (handle == NULL)
|
||||
+ {
|
||||
+ printf ("error: dlopen: %s\n", dlerror ());
|
||||
+ exit (1);
|
||||
+ }
|
||||
+ int *status = dlsym (handle, "recursemod1_status");
|
||||
+ if (status == NULL)
|
||||
+ {
|
||||
+ printf ("error: dlsym: %s\n", dlerror ());
|
||||
+ exit (1);
|
||||
+ }
|
||||
+ printf ("info: recursemod1_status == %d\n", *status);
|
||||
+ if (*status != 1)
|
||||
+ {
|
||||
+ puts ("error: recursemod1_status == 1 expected");
|
||||
+ exit (1);
|
||||
+ }
|
||||
+ ++*status;
|
||||
+ printf ("info: recursemod1_status == %d\n", *status);
|
||||
+
|
||||
+ int **mod2_status = dlsym (handle, "force_recursemod2_reference");
|
||||
+ if (mod2_status == NULL || *mod2_status != &recursemod2_status)
|
||||
+ {
|
||||
+ puts ("error: invalid recursemod2_status address in"
|
||||
+ " tst-dlopen-recursemod1.so");
|
||||
+ exit (1);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void __attribute__ ((destructor))
|
||||
+fini (void)
|
||||
+{
|
||||
+ printf ("info: tst-dlopen-recursemod2.so destructor called (status %d)\n",
|
||||
+ recursemod2_status);
|
||||
+}
|
||||
103
SOURCES/glibc-RHEL-47403-5.patch
Normal file
103
SOURCES/glibc-RHEL-47403-5.patch
Normal file
@ -0,0 +1,103 @@
|
||||
commit e096b7a1896886eb7dd2732ccbf1184b0eec9a63
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Oct 25 16:50:10 2024 +0200
|
||||
|
||||
elf: Signal LA_ACT_CONSISTENT to auditors after RT_CONSISTENT switch
|
||||
|
||||
Auditors can call into the dynamic loader again if
|
||||
LA_ACT_CONSISTENT, and those recursive calls could observe
|
||||
r_state != RT_CONSISTENT.
|
||||
|
||||
We should consider failing dlopen/dlmopen/dlclose if
|
||||
r_state != RT_CONSISTENT. The dynamic linker is probably not
|
||||
in a state in which it can handle reentrant calls. This
|
||||
needs further investigation.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
Conflicts:
|
||||
elf/rtld.c (kept SHARED guard downstream)
|
||||
|
||||
diff --git a/elf/dl-close.c b/elf/dl-close.c
|
||||
index bf699e58d753a1e2..8a4c3528a124d4e7 100644
|
||||
--- a/elf/dl-close.c
|
||||
+++ b/elf/dl-close.c
|
||||
@@ -759,6 +759,11 @@ _dl_close_worker (struct link_map *map, bool force)
|
||||
/* TLS is cleaned up for the unloaded modules. */
|
||||
__rtld_lock_unlock_recursive (GL(dl_load_tls_lock));
|
||||
|
||||
+ /* Notify the debugger those objects are finalized and gone. */
|
||||
+ r->r_state = RT_CONSISTENT;
|
||||
+ _dl_debug_state ();
|
||||
+ LIBC_PROBE (unmap_complete, 2, nsid, r);
|
||||
+
|
||||
#ifdef SHARED
|
||||
/* Auditing checkpoint: we have deleted all objects. Also, do not notify
|
||||
auditors of the cleanup of a failed audit module loading attempt. */
|
||||
@@ -771,11 +776,6 @@ _dl_close_worker (struct link_map *map, bool force)
|
||||
--GL(dl_nns);
|
||||
while (GL(dl_ns)[GL(dl_nns) - 1]._ns_loaded == NULL);
|
||||
|
||||
- /* Notify the debugger those objects are finalized and gone. */
|
||||
- r->r_state = RT_CONSISTENT;
|
||||
- _dl_debug_state ();
|
||||
- LIBC_PROBE (unmap_complete, 2, nsid, r);
|
||||
-
|
||||
/* Recheck if we need to retry, release the lock. */
|
||||
out:
|
||||
if (dl_close_state == rerun)
|
||||
diff --git a/elf/dl-open.c b/elf/dl-open.c
|
||||
index 0b0bfb8acda28caa..5095ea4f96b6cf49 100644
|
||||
--- a/elf/dl-open.c
|
||||
+++ b/elf/dl-open.c
|
||||
@@ -638,17 +638,17 @@ dl_open_worker_begin (void *a)
|
||||
#endif
|
||||
}
|
||||
|
||||
-#ifdef SHARED
|
||||
- /* Auditing checkpoint: we have added all objects. */
|
||||
- _dl_audit_activity_nsid (new->l_ns, LA_ACT_CONSISTENT);
|
||||
-#endif
|
||||
-
|
||||
/* Notify the debugger all new objects are now ready to go. */
|
||||
struct r_debug *r = _dl_debug_update (args->nsid);
|
||||
r->r_state = RT_CONSISTENT;
|
||||
_dl_debug_state ();
|
||||
LIBC_PROBE (map_complete, 3, args->nsid, r, new);
|
||||
|
||||
+#ifdef SHARED
|
||||
+ /* Auditing checkpoint: we have added all objects. */
|
||||
+ _dl_audit_activity_nsid (new->l_ns, LA_ACT_CONSISTENT);
|
||||
+#endif
|
||||
+
|
||||
_dl_open_check (new);
|
||||
|
||||
/* Print scope information. */
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index 7d8ed0ac1188d527..cd233174c9d944b2 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -2524,11 +2524,6 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
_dl_relocate_object might need to call `mprotect' for DT_TEXTREL. */
|
||||
_dl_sysdep_start_cleanup ();
|
||||
|
||||
-#ifdef SHARED
|
||||
- /* Auditing checkpoint: we have added all objects. */
|
||||
- _dl_audit_activity_nsid (LM_ID_BASE, LA_ACT_CONSISTENT);
|
||||
-#endif
|
||||
-
|
||||
/* Notify the debugger all new objects are now ready to go. We must re-get
|
||||
the address since by now the variable might be in another object. */
|
||||
r = _dl_debug_update (LM_ID_BASE);
|
||||
@@ -2536,6 +2531,11 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
_dl_debug_state ();
|
||||
LIBC_PROBE (init_complete, 2, LM_ID_BASE, r);
|
||||
|
||||
+#ifdef SHARED
|
||||
+ /* Auditing checkpoint: we have added all objects. */
|
||||
+ _dl_audit_activity_nsid (LM_ID_BASE, LA_ACT_CONSISTENT);
|
||||
+#endif
|
||||
+
|
||||
#if defined USE_LDCONFIG && !defined MAP_COPY
|
||||
/* We must munmap() the cache file. */
|
||||
_dl_unload_cache ();
|
||||
342
SOURCES/glibc-RHEL-47403-6.patch
Normal file
342
SOURCES/glibc-RHEL-47403-6.patch
Normal file
@ -0,0 +1,342 @@
|
||||
commit 43db5e2c0672cae7edea7c9685b22317eae25471
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Oct 25 16:50:10 2024 +0200
|
||||
|
||||
elf: Signal RT_CONSISTENT after relocation processing in dlopen (bug 31986)
|
||||
|
||||
Previously, a la_activity audit event was generated before
|
||||
relocation processing completed. This does did not match what
|
||||
happened during initial startup in elf/rtld.c (towards the end
|
||||
of dl_main). It also caused various problems if an auditor
|
||||
tried to open the same shared object again using dlmopen:
|
||||
If it was the directly loaded object, it had a search scope
|
||||
associated with it, so the early exit in dl_open_worker_begin
|
||||
was taken even though the object was unrelocated. This caused
|
||||
the r_state == RT_CONSISTENT assert to fail. Avoidance of the
|
||||
assert also depends on reversing the order of r_state update
|
||||
and auditor event (already implemented in a previous commit).
|
||||
|
||||
At the later point, args->map can be NULL due to failure,
|
||||
so use the assigned namespace ID instead if that is available.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile (fixup context)
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index dc774b083eda202b..73deb69f5a3c9150 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -404,6 +404,7 @@ tests += \
|
||||
tst-dlmopen1 \
|
||||
tst-dlmopen3 \
|
||||
tst-dlmopen4 \
|
||||
+ tst-dlopen-auditdup \
|
||||
tst-dlopen-recurse \
|
||||
tst-dlopen-self \
|
||||
tst-dlopen-tlsmodid \
|
||||
@@ -783,6 +784,8 @@ modules-names = \
|
||||
tst-dlmopen-twice-mod1 \
|
||||
tst-dlmopen-twice-mod2 \
|
||||
tst-dlmopen1mod \
|
||||
+ tst-dlopen-auditdup-auditmod \
|
||||
+ tst-dlopen-auditdupmod \
|
||||
tst-dlopen-recursemod1 \
|
||||
tst-dlopen-recursemod2 \
|
||||
tst-dlopen-sgid-mod \
|
||||
@@ -2861,6 +2864,9 @@ tst-dlopen-tlsreinit4-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
|
||||
|
||||
$(objpfx)tst-dlopen-recurse.out: $(objpfx)tst-dlopen-recursemod1.so
|
||||
$(objpfx)tst-dlopen-recursemod1.so: $(objpfx)tst-dlopen-recursemod2.so
|
||||
+tst-dlopen-auditdup-ENV = LD_AUDIT=$(objpfx)tst-dlopen-auditdup-auditmod.so
|
||||
+$(objpfx)tst-dlopen-auditdup.out: \
|
||||
+ $(objpfx)tst-dlopen-auditdupmod.so $(objpfx)tst-dlopen-auditdup-auditmod.so
|
||||
|
||||
LDFLAGS-tst-hash-collision1-mod.so = -Wl,--hash-style=both
|
||||
$(objpfx)tst-hash-collision1: $(objpfx)tst-hash-collision1-mod.so
|
||||
diff --git a/elf/dl-open.c b/elf/dl-open.c
|
||||
index 5095ea4f96b6cf49..6ec1ca033bbe7859 100644
|
||||
--- a/elf/dl-open.c
|
||||
+++ b/elf/dl-open.c
|
||||
@@ -575,6 +575,14 @@ dl_open_worker_begin (void *a)
|
||||
_dl_debug_printf ("opening file=%s [%lu]; direct_opencount=%u\n\n",
|
||||
new->l_name, new->l_ns, new->l_direct_opencount);
|
||||
|
||||
+#ifdef SHARED
|
||||
+ /* No relocation processing on this execution path. But
|
||||
+ relocation has not been performed for static
|
||||
+ position-dependent executables, so disable the assert for
|
||||
+ static linking. */
|
||||
+ assert (new->l_relocated);
|
||||
+#endif
|
||||
+
|
||||
/* If the user requested the object to be in the global
|
||||
namespace but it is not so far, prepare to add it now. This
|
||||
can raise an exception to do a malloc failure. */
|
||||
@@ -596,10 +604,6 @@ dl_open_worker_begin (void *a)
|
||||
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
|
||||
add_to_global_update (new);
|
||||
|
||||
- const int r_state __attribute__ ((unused))
|
||||
- = _dl_debug_update (args->nsid)->r_state;
|
||||
- assert (r_state == RT_CONSISTENT);
|
||||
-
|
||||
/* Do not return without calling the (supposedly new) map's
|
||||
constructor. This case occurs if a dependency of a directly
|
||||
opened map has a constructor that calls dlopen again on the
|
||||
@@ -638,17 +642,6 @@ dl_open_worker_begin (void *a)
|
||||
#endif
|
||||
}
|
||||
|
||||
- /* Notify the debugger all new objects are now ready to go. */
|
||||
- struct r_debug *r = _dl_debug_update (args->nsid);
|
||||
- r->r_state = RT_CONSISTENT;
|
||||
- _dl_debug_state ();
|
||||
- LIBC_PROBE (map_complete, 3, args->nsid, r, new);
|
||||
-
|
||||
-#ifdef SHARED
|
||||
- /* Auditing checkpoint: we have added all objects. */
|
||||
- _dl_audit_activity_nsid (new->l_ns, LA_ACT_CONSISTENT);
|
||||
-#endif
|
||||
-
|
||||
_dl_open_check (new);
|
||||
|
||||
/* Print scope information. */
|
||||
@@ -695,6 +688,7 @@ dl_open_worker_begin (void *a)
|
||||
created dlmopen namespaces. Do not do this for static dlopen
|
||||
because libc has relocations against ld.so, which may not have
|
||||
been relocated at this point. */
|
||||
+ struct r_debug *r = _dl_debug_update (args->nsid);
|
||||
#ifdef SHARED
|
||||
if (GL(dl_ns)[args->nsid].libc_map != NULL)
|
||||
_dl_open_relocate_one_object (args, r, GL(dl_ns)[args->nsid].libc_map,
|
||||
@@ -782,6 +776,26 @@ dl_open_worker (void *a)
|
||||
|
||||
__rtld_lock_unlock_recursive (GL(dl_load_tls_lock));
|
||||
|
||||
+ /* Auditing checkpoint and debugger signalling. Do this even on
|
||||
+ error, so that dlopen exists with consistent state. */
|
||||
+ if (args->nsid >= 0 || args->map != NULL)
|
||||
+ {
|
||||
+ Lmid_t nsid = args->map != NULL ? args->map->l_ns : args->nsid;
|
||||
+ struct r_debug *r = _dl_debug_update (nsid);
|
||||
+#ifdef SHARED
|
||||
+ bool was_not_consistent = r->r_state != RT_CONSISTENT;
|
||||
+#endif
|
||||
+ r->r_state = RT_CONSISTENT;
|
||||
+ _dl_debug_state ();
|
||||
+ LIBC_PROBE (map_complete, 3, nsid, r, new);
|
||||
+
|
||||
+#ifdef SHARED
|
||||
+ if (was_not_consistent)
|
||||
+ /* Avoid redudant/recursive signalling. */
|
||||
+ _dl_audit_activity_nsid (nsid, LA_ACT_CONSISTENT);
|
||||
+#endif
|
||||
+ }
|
||||
+
|
||||
if (__glibc_unlikely (ex.errstring != NULL))
|
||||
/* Reraise the error. */
|
||||
_dl_signal_exception (err, &ex, NULL);
|
||||
diff --git a/elf/tst-dlopen-auditdup-auditmod.c b/elf/tst-dlopen-auditdup-auditmod.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..9b67295e94d03e7a
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dlopen-auditdup-auditmod.c
|
||||
@@ -0,0 +1,100 @@
|
||||
+/* Auditor that opens again an object that just has been opened.
|
||||
+ Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <dlfcn.h>
|
||||
+#include <link.h>
|
||||
+#include <stdbool.h>
|
||||
+#include <stdio.h>
|
||||
+#include <string.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+unsigned int
|
||||
+la_version (unsigned int v)
|
||||
+{
|
||||
+ return LAV_CURRENT;
|
||||
+}
|
||||
+
|
||||
+static bool trigger_on_la_activity;
|
||||
+
|
||||
+unsigned int
|
||||
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
|
||||
+{
|
||||
+ printf ("info: la_objopen: \"%s\"\n", map->l_name);
|
||||
+ if (strstr (map->l_name, "/tst-dlopen-auditdupmod.so") != NULL)
|
||||
+ trigger_on_la_activity = true;
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+la_activity (uintptr_t *cookie, unsigned int flag)
|
||||
+{
|
||||
+ static unsigned int calls;
|
||||
+ ++calls;
|
||||
+ printf ("info: la_activity: call %u (flag %u)\n", calls, flag);
|
||||
+ fflush (stdout);
|
||||
+ if (trigger_on_la_activity)
|
||||
+ {
|
||||
+ /* Avoid triggering on the dlmopen call below. */
|
||||
+ static bool recursion;
|
||||
+ if (recursion)
|
||||
+ return;
|
||||
+ recursion = true;
|
||||
+
|
||||
+ puts ("info: about to dlmopen tst-dlopen-auditdupmod.so");
|
||||
+ fflush (stdout);
|
||||
+ void *handle = dlmopen (LM_ID_BASE, "tst-dlopen-auditdupmod.so",
|
||||
+ RTLD_NOW);
|
||||
+ if (handle == NULL)
|
||||
+ {
|
||||
+ printf ("error: dlmopen: %s\n", dlerror ());
|
||||
+ fflush (stdout);
|
||||
+ _exit (1);
|
||||
+ }
|
||||
+
|
||||
+ /* Check that the constructor has run. */
|
||||
+ int *status = dlsym (handle, "auditdupmod_status");
|
||||
+ if (status == NULL)
|
||||
+ {
|
||||
+ printf ("error: dlsym: %s\n", dlerror ());
|
||||
+ fflush (stdout);
|
||||
+ _exit (1);
|
||||
+ }
|
||||
+ printf ("info: auditdupmod_status == %d\n", *status);
|
||||
+ if (*status != 1)
|
||||
+ {
|
||||
+ puts ("error: auditdupmod_status == 1 expected");
|
||||
+ fflush (stdout);
|
||||
+ _exit (1);
|
||||
+ }
|
||||
+ /* Checked in the destructor and the main program. */
|
||||
+ ++*status;
|
||||
+ printf ("info: auditdupmod_status == %d\n", *status);
|
||||
+
|
||||
+ /* Check that the module has been relocated. */
|
||||
+ int **status_address = dlsym (handle, "auditdupmod_status_address");
|
||||
+ if (status_address == NULL || *status_address != status)
|
||||
+ {
|
||||
+ puts ("error: invalid auditdupmod_status address in"
|
||||
+ " tst-dlopen-auditdupmod.so");
|
||||
+ fflush (stdout);
|
||||
+ _exit (1);
|
||||
+ }
|
||||
+
|
||||
+ fflush (stdout);
|
||||
+ }
|
||||
+}
|
||||
diff --git a/elf/tst-dlopen-auditdup.c b/elf/tst-dlopen-auditdup.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..d022c58ae3091da1
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dlopen-auditdup.c
|
||||
@@ -0,0 +1,36 @@
|
||||
+/* Test that recursive dlopen from auditor works (bug 31986).
|
||||
+ Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ puts ("info: about to dlopen tst-dlopen-auditdupmod.so");
|
||||
+ fflush (stdout);
|
||||
+ void *handle = xdlopen ("tst-dlopen-auditdupmod.so", RTLD_NOW);
|
||||
+ int *status = xdlsym (handle, "auditdupmod_status");
|
||||
+ printf ("info: auditdupmod_status == %d (from main)\n", *status);
|
||||
+ TEST_COMPARE (*status, 2);
|
||||
+ xdlclose (handle);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
diff --git a/elf/tst-dlopen-auditdupmod.c b/elf/tst-dlopen-auditdupmod.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..59b7e21daa8212df
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dlopen-auditdupmod.c
|
||||
@@ -0,0 +1,48 @@
|
||||
+/* Directly opened test module that gets reopened from the auditor.
|
||||
+ Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <support/xdlfcn.h>
|
||||
+
|
||||
+int auditdupmod_status;
|
||||
+
|
||||
+/* Used to check for successful relocation processing. */
|
||||
+int *auditdupmod_status_address = &auditdupmod_status;
|
||||
+
|
||||
+static void __attribute__ ((constructor))
|
||||
+init (void)
|
||||
+{
|
||||
+ ++auditdupmod_status;
|
||||
+ printf ("info: tst-dlopen-auditdupmod.so constructor called (status %d)\n",
|
||||
+ auditdupmod_status);
|
||||
+}
|
||||
+
|
||||
+static void __attribute__ ((destructor))
|
||||
+fini (void)
|
||||
+{
|
||||
+ /* The tst-dlopen-auditdup-auditmod.so auditor incremented
|
||||
+ auditdupmod_status. */
|
||||
+ printf ("info: tst-dlopen-auditdupmod.so destructor called (status %d)\n",
|
||||
+ auditdupmod_status);
|
||||
+ if (auditdupmod_status != 2)
|
||||
+ {
|
||||
+ puts ("error: auditdupmod_status == 2 expected");
|
||||
+ exit (1);
|
||||
+ }
|
||||
+}
|
||||
272
SOURCES/glibc-RHEL-47403-7.patch
Normal file
272
SOURCES/glibc-RHEL-47403-7.patch
Normal file
@ -0,0 +1,272 @@
|
||||
commit 95129e6b8fabdaa8cd8a4a5cc20be0f4cb0ba59f
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Mon Oct 28 14:45:30 2024 +0100
|
||||
|
||||
Revert "elf: Run constructors on cyclic recursive dlopen (bug 31986)"
|
||||
|
||||
This reverts commit 9897ced8e78db5d813166a7ccccfd5a42c69ef20.
|
||||
|
||||
Adjust the test expectations in elf/tst-dlopen-auditdup-auditmod.c
|
||||
accordingly.
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile (fixup context)
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index 73deb69f5a3c9150..a358ad7ff0eb2af7 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -405,7 +405,6 @@ tests += \
|
||||
tst-dlmopen3 \
|
||||
tst-dlmopen4 \
|
||||
tst-dlopen-auditdup \
|
||||
- tst-dlopen-recurse \
|
||||
tst-dlopen-self \
|
||||
tst-dlopen-tlsmodid \
|
||||
tst-dlopen-tlsreinit1 \
|
||||
@@ -786,8 +785,6 @@ modules-names = \
|
||||
tst-dlmopen1mod \
|
||||
tst-dlopen-auditdup-auditmod \
|
||||
tst-dlopen-auditdupmod \
|
||||
- tst-dlopen-recursemod1 \
|
||||
- tst-dlopen-recursemod2 \
|
||||
tst-dlopen-sgid-mod \
|
||||
tst-dlopen-tlsreinitmod1 \
|
||||
tst-dlopen-tlsreinitmod2 \
|
||||
@@ -2862,8 +2859,6 @@ tst-dlopen-tlsreinit3-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
|
||||
$(objpfx)tst-dlopen-tlsreinit4.out: $(objpfx)tst-auditmod1.so
|
||||
tst-dlopen-tlsreinit4-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
|
||||
|
||||
-$(objpfx)tst-dlopen-recurse.out: $(objpfx)tst-dlopen-recursemod1.so
|
||||
-$(objpfx)tst-dlopen-recursemod1.so: $(objpfx)tst-dlopen-recursemod2.so
|
||||
tst-dlopen-auditdup-ENV = LD_AUDIT=$(objpfx)tst-dlopen-auditdup-auditmod.so
|
||||
$(objpfx)tst-dlopen-auditdup.out: \
|
||||
$(objpfx)tst-dlopen-auditdupmod.so $(objpfx)tst-dlopen-auditdup-auditmod.so
|
||||
diff --git a/elf/dl-open.c b/elf/dl-open.c
|
||||
index 6ec1ca033bbe7859..6557c2fd7ca0bbfe 100644
|
||||
--- a/elf/dl-open.c
|
||||
+++ b/elf/dl-open.c
|
||||
@@ -604,14 +604,6 @@ dl_open_worker_begin (void *a)
|
||||
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
|
||||
add_to_global_update (new);
|
||||
|
||||
- /* Do not return without calling the (supposedly new) map's
|
||||
- constructor. This case occurs if a dependency of a directly
|
||||
- opened map has a constructor that calls dlopen again on the
|
||||
- initially opened map. The new map is initialized last, so
|
||||
- checking only it is enough. */
|
||||
- if (!new->l_init_called)
|
||||
- _dl_catch_exception (NULL, call_dl_init, args);
|
||||
-
|
||||
return;
|
||||
}
|
||||
|
||||
diff --git a/elf/dl-support.c b/elf/dl-support.c
|
||||
index f4dc9c61a2637f8b..00abc2d8056c78b0 100644
|
||||
--- a/elf/dl-support.c
|
||||
+++ b/elf/dl-support.c
|
||||
@@ -103,7 +103,6 @@ static struct link_map _dl_main_map =
|
||||
.l_used = 1,
|
||||
.l_tls_offset = NO_TLS_OFFSET,
|
||||
.l_serial = 1,
|
||||
- .l_init_called = 1,
|
||||
};
|
||||
|
||||
/* Namespace information. */
|
||||
diff --git a/elf/tst-dlopen-auditdup-auditmod.c b/elf/tst-dlopen-auditdup-auditmod.c
|
||||
index 9b67295e94d03e7a..270a595ec4de1439 100644
|
||||
--- a/elf/tst-dlopen-auditdup-auditmod.c
|
||||
+++ b/elf/tst-dlopen-auditdup-auditmod.c
|
||||
@@ -66,7 +66,11 @@ la_activity (uintptr_t *cookie, unsigned int flag)
|
||||
_exit (1);
|
||||
}
|
||||
|
||||
- /* Check that the constructor has run. */
|
||||
+ /* Check that the constructor has not run. Running the
|
||||
+ constructor would require constructing its dependencies, but
|
||||
+ the constructor call that triggered this auditing activity
|
||||
+ has not completed, and constructors among the dependencies
|
||||
+ may not be able to deal with that. */
|
||||
int *status = dlsym (handle, "auditdupmod_status");
|
||||
if (status == NULL)
|
||||
{
|
||||
@@ -75,9 +79,9 @@ la_activity (uintptr_t *cookie, unsigned int flag)
|
||||
_exit (1);
|
||||
}
|
||||
printf ("info: auditdupmod_status == %d\n", *status);
|
||||
- if (*status != 1)
|
||||
+ if (*status != 0)
|
||||
{
|
||||
- puts ("error: auditdupmod_status == 1 expected");
|
||||
+ puts ("error: auditdupmod_status == 0 expected");
|
||||
fflush (stdout);
|
||||
_exit (1);
|
||||
}
|
||||
diff --git a/elf/tst-dlopen-recurse.c b/elf/tst-dlopen-recurse.c
|
||||
deleted file mode 100644
|
||||
index c7fb379d373c6e77..0000000000000000
|
||||
--- a/elf/tst-dlopen-recurse.c
|
||||
+++ /dev/null
|
||||
@@ -1,34 +0,0 @@
|
||||
-/* Test that recursive dlopen runs constructors before return (bug 31986).
|
||||
- Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
- This file is part of the GNU C Library.
|
||||
-
|
||||
- The GNU C Library is free software; you can redistribute it and/or
|
||||
- modify it under the terms of the GNU Lesser General Public
|
||||
- License as published by the Free Software Foundation; either
|
||||
- version 2.1 of the License, or (at your option) any later version.
|
||||
-
|
||||
- The GNU C Library is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
- Lesser General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Lesser General Public
|
||||
- License along with the GNU C Library; if not, see
|
||||
- <https://www.gnu.org/licenses/>. */
|
||||
-
|
||||
-#include <stdio.h>
|
||||
-#include <support/check.h>
|
||||
-#include <support/xdlfcn.h>
|
||||
-
|
||||
-static int
|
||||
-do_test (void)
|
||||
-{
|
||||
- void *handle = xdlopen ("tst-dlopen-recursemod1.so", RTLD_NOW);
|
||||
- int *status = dlsym (handle, "recursemod1_status");
|
||||
- printf ("info: recursemod1_status == %d (from main)\n", *status);
|
||||
- TEST_COMPARE (*status, 2);
|
||||
- xdlclose (handle);
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-#include <support/test-driver.c>
|
||||
diff --git a/elf/tst-dlopen-recursemod1.c b/elf/tst-dlopen-recursemod1.c
|
||||
deleted file mode 100644
|
||||
index 5e0cc0eb8c32d6d4..0000000000000000
|
||||
--- a/elf/tst-dlopen-recursemod1.c
|
||||
+++ /dev/null
|
||||
@@ -1,50 +0,0 @@
|
||||
-/* Directly opened test module that gets recursively opened again.
|
||||
- Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
- This file is part of the GNU C Library.
|
||||
-
|
||||
- The GNU C Library is free software; you can redistribute it and/or
|
||||
- modify it under the terms of the GNU Lesser General Public
|
||||
- License as published by the Free Software Foundation; either
|
||||
- version 2.1 of the License, or (at your option) any later version.
|
||||
-
|
||||
- The GNU C Library is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
- Lesser General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Lesser General Public
|
||||
- License along with the GNU C Library; if not, see
|
||||
- <https://www.gnu.org/licenses/>. */
|
||||
-
|
||||
-#include <stdio.h>
|
||||
-#include <stdlib.h>
|
||||
-#include <support/xdlfcn.h>
|
||||
-
|
||||
-int recursemod1_status;
|
||||
-
|
||||
-/* Force linking against st-dlopen-recursemod2.so. Also allows
|
||||
- checking for relocation. */
|
||||
-extern int recursemod2_status;
|
||||
-int *force_recursemod2_reference = &recursemod2_status;
|
||||
-
|
||||
-static void __attribute__ ((constructor))
|
||||
-init (void)
|
||||
-{
|
||||
- ++recursemod1_status;
|
||||
- printf ("info: tst-dlopen-recursemod1.so constructor called (status %d)\n",
|
||||
- recursemod1_status);
|
||||
-}
|
||||
-
|
||||
-static void __attribute__ ((destructor))
|
||||
-fini (void)
|
||||
-{
|
||||
- /* The recursemod1_status variable was incremented in the
|
||||
- tst-dlopen-recursemod2.so constructor. */
|
||||
- printf ("info: tst-dlopen-recursemod1.so destructor called (status %d)\n",
|
||||
- recursemod1_status);
|
||||
- if (recursemod1_status != 2)
|
||||
- {
|
||||
- puts ("error: recursemod1_status == 2 expected");
|
||||
- exit (1);
|
||||
- }
|
||||
-}
|
||||
diff --git a/elf/tst-dlopen-recursemod2.c b/elf/tst-dlopen-recursemod2.c
|
||||
deleted file mode 100644
|
||||
index edd2f2526b877810..0000000000000000
|
||||
--- a/elf/tst-dlopen-recursemod2.c
|
||||
+++ /dev/null
|
||||
@@ -1,66 +0,0 @@
|
||||
-/* Indirectly opened module that recursively opens the directly opened module.
|
||||
- Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
- This file is part of the GNU C Library.
|
||||
-
|
||||
- The GNU C Library is free software; you can redistribute it and/or
|
||||
- modify it under the terms of the GNU Lesser General Public
|
||||
- License as published by the Free Software Foundation; either
|
||||
- version 2.1 of the License, or (at your option) any later version.
|
||||
-
|
||||
- The GNU C Library is distributed in the hope that it will be useful,
|
||||
- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
- Lesser General Public License for more details.
|
||||
-
|
||||
- You should have received a copy of the GNU Lesser General Public
|
||||
- License along with the GNU C Library; if not, see
|
||||
- <https://www.gnu.org/licenses/>. */
|
||||
-
|
||||
-#include <dlfcn.h>
|
||||
-#include <stdio.h>
|
||||
-#include <stdlib.h>
|
||||
-
|
||||
-int recursemod2_status;
|
||||
-
|
||||
-static void __attribute__ ((constructor))
|
||||
-init (void)
|
||||
-{
|
||||
- ++recursemod2_status;
|
||||
- printf ("info: tst-dlopen-recursemod2.so constructor called (status %d)\n",
|
||||
- recursemod2_status);
|
||||
- void *handle = dlopen ("tst-dlopen-recursemod1.so", RTLD_NOW);
|
||||
- if (handle == NULL)
|
||||
- {
|
||||
- printf ("error: dlopen: %s\n", dlerror ());
|
||||
- exit (1);
|
||||
- }
|
||||
- int *status = dlsym (handle, "recursemod1_status");
|
||||
- if (status == NULL)
|
||||
- {
|
||||
- printf ("error: dlsym: %s\n", dlerror ());
|
||||
- exit (1);
|
||||
- }
|
||||
- printf ("info: recursemod1_status == %d\n", *status);
|
||||
- if (*status != 1)
|
||||
- {
|
||||
- puts ("error: recursemod1_status == 1 expected");
|
||||
- exit (1);
|
||||
- }
|
||||
- ++*status;
|
||||
- printf ("info: recursemod1_status == %d\n", *status);
|
||||
-
|
||||
- int **mod2_status = dlsym (handle, "force_recursemod2_reference");
|
||||
- if (mod2_status == NULL || *mod2_status != &recursemod2_status)
|
||||
- {
|
||||
- puts ("error: invalid recursemod2_status address in"
|
||||
- " tst-dlopen-recursemod1.so");
|
||||
- exit (1);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static void __attribute__ ((destructor))
|
||||
-fini (void)
|
||||
-{
|
||||
- printf ("info: tst-dlopen-recursemod2.so destructor called (status %d)\n",
|
||||
- recursemod2_status);
|
||||
-}
|
||||
218
SOURCES/glibc-RHEL-47403-8.patch
Normal file
218
SOURCES/glibc-RHEL-47403-8.patch
Normal file
@ -0,0 +1,218 @@
|
||||
commit d604f9c500570e80febfcc6a52b63a002b466f35
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Tue Mar 11 15:30:52 2025 +0100
|
||||
|
||||
elf: Test dlopen (NULL, RTLD_LAZY) from an ELF constructor
|
||||
|
||||
This call must not complete initialization of all shared objects
|
||||
in the global scope because the ELF constructor which makes the call
|
||||
likely has not finished initialization. Calling more constructors
|
||||
at this point would expose those to a partially constructed
|
||||
dependency.
|
||||
|
||||
This completes the revert of commit 9897ced8e78db5d813166a7ccccfd5a
|
||||
("elf: Run constructors on cyclic recursive dlopen (bug 31986)").
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile (fixup context)
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index a358ad7ff0eb2af7..3a50ca90366aec94 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -405,6 +405,7 @@ tests += \
|
||||
tst-dlmopen3 \
|
||||
tst-dlmopen4 \
|
||||
tst-dlopen-auditdup \
|
||||
+ tst-dlopen-constructor-null \
|
||||
tst-dlopen-self \
|
||||
tst-dlopen-tlsmodid \
|
||||
tst-dlopen-tlsreinit1 \
|
||||
@@ -785,6 +786,8 @@ modules-names = \
|
||||
tst-dlmopen1mod \
|
||||
tst-dlopen-auditdup-auditmod \
|
||||
tst-dlopen-auditdupmod \
|
||||
+ tst-dlopen-constructor-null-mod1 \
|
||||
+ tst-dlopen-constructor-null-mod2 \
|
||||
tst-dlopen-sgid-mod \
|
||||
tst-dlopen-tlsreinitmod1 \
|
||||
tst-dlopen-tlsreinitmod2 \
|
||||
@@ -2937,3 +2940,9 @@ $(objpfx)tst-nolink-libc-2: $(objpfx)tst-nolink-libc.o
|
||||
-Wl,--dynamic-linker=$(objpfx)ld.so
|
||||
$(objpfx)tst-nolink-libc-2.out: $(objpfx)tst-nolink-libc-2 $(objpfx)ld.so
|
||||
$< > $@ 2>&1; $(evaluate-test)
|
||||
+
|
||||
+$(objpfx)tst-dlopen-constructor-null: \
|
||||
+ $(objpfx)tst-dlopen-constructor-null-mod1.so \
|
||||
+ $(objpfx)tst-dlopen-constructor-null-mod2.so
|
||||
+$(objpfx)tst-dlopen-constructor-null-mod2.so: \
|
||||
+ $(objpfx)tst-dlopen-constructor-null-mod1.so
|
||||
diff --git a/elf/dl-open.c b/elf/dl-open.c
|
||||
index 6557c2fd7ca0bbfe..c225654822ee3520 100644
|
||||
--- a/elf/dl-open.c
|
||||
+++ b/elf/dl-open.c
|
||||
@@ -604,6 +604,16 @@ dl_open_worker_begin (void *a)
|
||||
if ((mode & RTLD_GLOBAL) && new->l_global == 0)
|
||||
add_to_global_update (new);
|
||||
|
||||
+ /* It is not possible to run the ELF constructor for the new
|
||||
+ link map if it has not executed yet: If this dlopen call came
|
||||
+ from an ELF constructor that has not put that object into a
|
||||
+ consistent state, completing initialization for the entire
|
||||
+ scope will expose objects that have this partially
|
||||
+ constructed object among its dependencies to this
|
||||
+ inconsistent state. This could happen even with a benign
|
||||
+ dlopen (NULL, RTLD_LAZY) call from a constructor of an
|
||||
+ initially loaded shared object. */
|
||||
+
|
||||
return;
|
||||
}
|
||||
|
||||
diff --git a/elf/tst-dlopen-constructor-null-mod1.c b/elf/tst-dlopen-constructor-null-mod1.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..70a7a0ad46a1a666
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dlopen-constructor-null-mod1.c
|
||||
@@ -0,0 +1,55 @@
|
||||
+/* Module calling dlopen (NULL, RTLD_LAZY) to obtain the global scope.
|
||||
+ Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <dlfcn.h>
|
||||
+#include <stddef.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+
|
||||
+int mod1_status;
|
||||
+
|
||||
+static void __attribute__ ((constructor))
|
||||
+init (void)
|
||||
+{
|
||||
+ puts ("info: tst-dlopen-constructor-null-mod1.so constructor");
|
||||
+
|
||||
+ void *handle = dlopen (NULL, RTLD_LAZY);
|
||||
+ if (handle == NULL)
|
||||
+ {
|
||||
+ printf ("error: %s\n", dlerror ());
|
||||
+ exit (1);
|
||||
+ }
|
||||
+ puts ("info: dlopen returned");
|
||||
+ if (dlsym (handle, "malloc") != malloc)
|
||||
+ {
|
||||
+ puts ("error: dlsym did not produce expected result");
|
||||
+ exit (1);
|
||||
+ }
|
||||
+ dlclose (handle);
|
||||
+
|
||||
+ /* Check that the second module's constructor has not executed. */
|
||||
+ if (getenv ("mod2_status") != NULL)
|
||||
+ {
|
||||
+ printf ("error: mod2_status environment variable set: %s\n",
|
||||
+ getenv ("mod2_status"));
|
||||
+ exit (1);
|
||||
+ }
|
||||
+
|
||||
+ /* Communicate to the second module that the constructor executed. */
|
||||
+ mod1_status = 1;
|
||||
+}
|
||||
diff --git a/elf/tst-dlopen-constructor-null-mod2.c b/elf/tst-dlopen-constructor-null-mod2.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..d6e945beaec04815
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dlopen-constructor-null-mod2.c
|
||||
@@ -0,0 +1,37 @@
|
||||
+/* Module whose constructor should not be invoked by dlopen (NULL, RTLD_LAZY).
|
||||
+ Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+
|
||||
+extern int mod1_status;
|
||||
+int mod2_status;
|
||||
+
|
||||
+static void __attribute__ ((constructor))
|
||||
+init (void)
|
||||
+{
|
||||
+ printf ("info: tst-dlopen-constructor-null-mod2.so constructor"
|
||||
+ " (mod1_status=%d)", mod1_status);
|
||||
+ if (!(mod1_status == 1 && mod2_status == 0))
|
||||
+ {
|
||||
+ puts ("error: mod1_status == 1 && mod2_status == 0 expected");
|
||||
+ exit (1);
|
||||
+ }
|
||||
+ setenv ("mod2_status", "constructed", 1);
|
||||
+ mod2_status = 1;
|
||||
+}
|
||||
diff --git a/elf/tst-dlopen-constructor-null.c b/elf/tst-dlopen-constructor-null.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..db90643325c5235f
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-dlopen-constructor-null.c
|
||||
@@ -0,0 +1,38 @@
|
||||
+/* Verify that dlopen (NULL, RTLD_LAZY) does not complete initialization.
|
||||
+ Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+/* This test mimics what the glvndSetupPthreads function in libglvnd
|
||||
+ does. */
|
||||
+
|
||||
+#include <stdlib.h>
|
||||
+#include <support/check.h>
|
||||
+
|
||||
+/* Defined and initialized in the shared objects. */
|
||||
+extern int mod1_status;
|
||||
+extern int mod2_status;
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ TEST_COMPARE (mod1_status, 1);
|
||||
+ TEST_COMPARE (mod2_status, 1);
|
||||
+ TEST_COMPARE_STRING (getenv ("mod2_status"), "constructed");
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
24
SOURCES/glibc-RHEL-47403-9.patch
Normal file
24
SOURCES/glibc-RHEL-47403-9.patch
Normal file
@ -0,0 +1,24 @@
|
||||
commit ac73067cb7a328bf106ecd041c020fc61be7e087
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Oct 25 17:41:53 2024 +0200
|
||||
|
||||
elf: Fix map_complete Systemtap probe in dl_open_worker
|
||||
|
||||
The refactoring did not take the change of variable into account.
|
||||
Fixes commit 43db5e2c0672cae7edea7c9685b22317eae25471
|
||||
("elf: Signal RT_CONSISTENT after relocation processing in dlopen
|
||||
(bug 31986)").
|
||||
|
||||
diff --git a/elf/dl-open.c b/elf/dl-open.c
|
||||
index c225654822ee3520..1e61e402455da666 100644
|
||||
--- a/elf/dl-open.c
|
||||
+++ b/elf/dl-open.c
|
||||
@@ -789,7 +789,7 @@ dl_open_worker (void *a)
|
||||
#endif
|
||||
r->r_state = RT_CONSISTENT;
|
||||
_dl_debug_state ();
|
||||
- LIBC_PROBE (map_complete, 3, nsid, r, new);
|
||||
+ LIBC_PROBE (map_complete, 3, nsid, r, args->map);
|
||||
|
||||
#ifdef SHARED
|
||||
if (was_not_consistent)
|
||||
120
SOURCES/glibc-RHEL-48820-1.patch
Normal file
120
SOURCES/glibc-RHEL-48820-1.patch
Normal file
@ -0,0 +1,120 @@
|
||||
commit 8f8dd904c4a2207699bb666f30acceb5209c8d3f
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Wed Nov 6 10:33:44 2024 +0100
|
||||
|
||||
elf: rtld_multiple_ref is always true
|
||||
|
||||
For a long time, libc.so.6 has dependend on ld.so, which
|
||||
means that there is a reference to ld.so in all processes,
|
||||
and rtld_multiple_ref is always true. In fact, if
|
||||
rtld_multiple_ref were false, some of the ld.so setup code
|
||||
would not run.
|
||||
|
||||
Reviewed-by: DJ Delorie <dj@redhat.com>
|
||||
|
||||
Conflicts:
|
||||
elf/rtld.c (
|
||||
- prelink support not removed downstream
|
||||
- "elf: Add _dl_find_object function" not ported
|
||||
downstream
|
||||
)
|
||||
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index d02ecc834c9a4d43..711bb77d70da6563 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -2002,43 +2002,37 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
if (main_map->l_searchlist.r_list[i] == &GL(dl_rtld_map))
|
||||
break;
|
||||
|
||||
- bool rtld_multiple_ref = false;
|
||||
- if (__glibc_likely (i < main_map->l_searchlist.r_nlist))
|
||||
- {
|
||||
- /* Some DT_NEEDED entry referred to the interpreter object itself, so
|
||||
- put it back in the list of visible objects. We insert it into the
|
||||
- chain in symbol search order because gdb uses the chain's order as
|
||||
- its symbol search order. */
|
||||
- rtld_multiple_ref = true;
|
||||
+ /* Insert the link map for the dynamic loader into the chain in
|
||||
+ symbol search order because gdb uses the chain's order as its
|
||||
+ symbol search order. */
|
||||
|
||||
- GL(dl_rtld_map).l_prev = main_map->l_searchlist.r_list[i - 1];
|
||||
- if (__glibc_likely (state.mode == rtld_mode_normal))
|
||||
- {
|
||||
- GL(dl_rtld_map).l_next = (i + 1 < main_map->l_searchlist.r_nlist
|
||||
- ? main_map->l_searchlist.r_list[i + 1]
|
||||
- : NULL);
|
||||
+ GL(dl_rtld_map).l_prev = main_map->l_searchlist.r_list[i - 1];
|
||||
+ if (__glibc_likely (state.mode == rtld_mode_normal))
|
||||
+ {
|
||||
+ GL(dl_rtld_map).l_next = (i + 1 < main_map->l_searchlist.r_nlist
|
||||
+ ? main_map->l_searchlist.r_list[i + 1]
|
||||
+ : NULL);
|
||||
#ifdef NEED_DL_SYSINFO_DSO
|
||||
- if (GLRO(dl_sysinfo_map) != NULL
|
||||
- && GL(dl_rtld_map).l_prev->l_next == GLRO(dl_sysinfo_map)
|
||||
- && GL(dl_rtld_map).l_next != GLRO(dl_sysinfo_map))
|
||||
- GL(dl_rtld_map).l_prev = GLRO(dl_sysinfo_map);
|
||||
+ if (GLRO(dl_sysinfo_map) != NULL
|
||||
+ && GL(dl_rtld_map).l_prev->l_next == GLRO(dl_sysinfo_map)
|
||||
+ && GL(dl_rtld_map).l_next != GLRO(dl_sysinfo_map))
|
||||
+ GL(dl_rtld_map).l_prev = GLRO(dl_sysinfo_map);
|
||||
#endif
|
||||
- }
|
||||
- else
|
||||
- /* In trace mode there might be an invisible object (which we
|
||||
- could not find) after the previous one in the search list.
|
||||
- In this case it doesn't matter much where we put the
|
||||
- interpreter object, so we just initialize the list pointer so
|
||||
- that the assertion below holds. */
|
||||
- GL(dl_rtld_map).l_next = GL(dl_rtld_map).l_prev->l_next;
|
||||
-
|
||||
- assert (GL(dl_rtld_map).l_prev->l_next == GL(dl_rtld_map).l_next);
|
||||
- GL(dl_rtld_map).l_prev->l_next = &GL(dl_rtld_map);
|
||||
- if (GL(dl_rtld_map).l_next != NULL)
|
||||
- {
|
||||
- assert (GL(dl_rtld_map).l_next->l_prev == GL(dl_rtld_map).l_prev);
|
||||
- GL(dl_rtld_map).l_next->l_prev = &GL(dl_rtld_map);
|
||||
- }
|
||||
+ }
|
||||
+ else
|
||||
+ /* In trace mode there might be an invisible object (which we
|
||||
+ could not find) after the previous one in the search list.
|
||||
+ In this case it doesn't matter much where we put the
|
||||
+ interpreter object, so we just initialize the list pointer so
|
||||
+ that the assertion below holds. */
|
||||
+ GL(dl_rtld_map).l_next = GL(dl_rtld_map).l_prev->l_next;
|
||||
+
|
||||
+ assert (GL(dl_rtld_map).l_prev->l_next == GL(dl_rtld_map).l_next);
|
||||
+ GL(dl_rtld_map).l_prev->l_next = &GL(dl_rtld_map);
|
||||
+ if (GL(dl_rtld_map).l_next != NULL)
|
||||
+ {
|
||||
+ assert (GL(dl_rtld_map).l_next->l_prev == GL(dl_rtld_map).l_prev);
|
||||
+ GL(dl_rtld_map).l_next->l_prev = &GL(dl_rtld_map);
|
||||
}
|
||||
|
||||
/* Now let us see whether all libraries are available in the
|
||||
@@ -2212,8 +2206,7 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
}
|
||||
}
|
||||
|
||||
- if ((GLRO(dl_debug_mask) & DL_DEBUG_PRELINK)
|
||||
- && rtld_multiple_ref)
|
||||
+ if (GLRO(dl_debug_mask) & DL_DEBUG_PRELINK)
|
||||
{
|
||||
/* Mark the link map as not yet relocated again. */
|
||||
GL(dl_rtld_map).l_relocated = 0;
|
||||
@@ -2500,10 +2493,9 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
/* Make sure no new search directories have been added. */
|
||||
assert (GLRO(dl_init_all_dirs) == GL(dl_all_dirs));
|
||||
|
||||
- if (! prelinked && rtld_multiple_ref)
|
||||
+ if (! prelinked)
|
||||
{
|
||||
- /* There was an explicit ref to the dynamic linker as a shared lib.
|
||||
- Re-relocate ourselves with user-controlled symbol definitions.
|
||||
+ /* Re-relocate ourselves with user-controlled symbol definitions.
|
||||
|
||||
We must do this after TLS initialization in case after this
|
||||
re-relocation, we might call a user-supplied function
|
||||
52
SOURCES/glibc-RHEL-48820-2.patch
Normal file
52
SOURCES/glibc-RHEL-48820-2.patch
Normal file
@ -0,0 +1,52 @@
|
||||
commit a79642204537dec8a1e1c58d1e0a074b3c624f46
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Wed Nov 6 10:33:44 2024 +0100
|
||||
|
||||
elf: Do not define consider_profiling, consider_symbind as macros
|
||||
|
||||
This avoids surprises when refactoring the code if these identifiers
|
||||
are re-used later in the file.
|
||||
|
||||
Reviewed-by: DJ Delorie <dj@redhat.com>
|
||||
|
||||
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
|
||||
index 0254e589c06fbf4c..ded506da9e180eac 100644
|
||||
--- a/elf/dl-reloc.c
|
||||
+++ b/elf/dl-reloc.c
|
||||
@@ -207,8 +207,8 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
|
||||
int lazy = reloc_mode & RTLD_LAZY;
|
||||
int skip_ifunc = reloc_mode & __RTLD_NOIFUNC;
|
||||
|
||||
-#ifdef SHARED
|
||||
bool consider_symbind = false;
|
||||
+#ifdef SHARED
|
||||
/* If we are auditing, install the same handlers we need for profiling. */
|
||||
if ((reloc_mode & __RTLD_AUDIT) == 0)
|
||||
{
|
||||
@@ -227,9 +227,7 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
|
||||
}
|
||||
#elif defined PROF
|
||||
/* Never use dynamic linker profiling for gprof profiling code. */
|
||||
-# define consider_profiling 0
|
||||
-#else
|
||||
-# define consider_symbind 0
|
||||
+ consider_profiling = 0;
|
||||
#endif
|
||||
|
||||
/* If DT_BIND_NOW is set relocate all references in this object. We
|
||||
@@ -287,7 +285,6 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
|
||||
|
||||
ELF_DYNAMIC_RELOCATE (l, scope, lazy, consider_profiling, skip_ifunc);
|
||||
|
||||
-#ifndef PROF
|
||||
if ((consider_profiling || consider_symbind)
|
||||
&& l->l_info[DT_PLTRELSZ] != NULL)
|
||||
{
|
||||
@@ -308,7 +305,6 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
|
||||
_dl_fatal_printf (errstring, RTLD_PROGNAME, l->l_name);
|
||||
}
|
||||
}
|
||||
-#endif
|
||||
}
|
||||
|
||||
/* Mark the object so we know this work has been done. */
|
||||
77
SOURCES/glibc-RHEL-48820-3.patch
Normal file
77
SOURCES/glibc-RHEL-48820-3.patch
Normal file
@ -0,0 +1,77 @@
|
||||
commit f2326c2ec0a0a8db7bc7f4db8cce3002768fc3b6
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Wed Nov 6 10:33:44 2024 +0100
|
||||
|
||||
elf: Introduce _dl_relocate_object_no_relro
|
||||
|
||||
And make _dl_protect_relro apply RELRO conditionally.
|
||||
|
||||
Reviewed-by: DJ Delorie <dj@redhat.com>
|
||||
|
||||
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
|
||||
index ded506da9e180eac..239f5505f805b008 100644
|
||||
--- a/elf/dl-reloc.c
|
||||
+++ b/elf/dl-reloc.c
|
||||
@@ -189,12 +189,9 @@ _dl_nothread_init_static_tls (struct link_map *map)
|
||||
#include "dynamic-link.h"
|
||||
|
||||
void
|
||||
-_dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
|
||||
- int reloc_mode, int consider_profiling)
|
||||
+_dl_relocate_object_no_relro (struct link_map *l, struct r_scope_elem *scope[],
|
||||
+ int reloc_mode, int consider_profiling)
|
||||
{
|
||||
- if (l->l_relocated)
|
||||
- return;
|
||||
-
|
||||
struct textrels
|
||||
{
|
||||
caddr_t start;
|
||||
@@ -325,17 +322,24 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
|
||||
|
||||
textrels = textrels->next;
|
||||
}
|
||||
-
|
||||
- /* In case we can protect the data now that the relocations are
|
||||
- done, do it. */
|
||||
- if (l->l_relro_size != 0)
|
||||
- _dl_protect_relro (l);
|
||||
}
|
||||
|
||||
+void
|
||||
+_dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
|
||||
+ int reloc_mode, int consider_profiling)
|
||||
+{
|
||||
+ if (l->l_relocated)
|
||||
+ return;
|
||||
+ _dl_relocate_object_no_relro (l, scope, reloc_mode, consider_profiling);
|
||||
+ _dl_protect_relro (l);
|
||||
+}
|
||||
|
||||
void
|
||||
_dl_protect_relro (struct link_map *l)
|
||||
{
|
||||
+ if (l->l_relro_size == 0)
|
||||
+ return;
|
||||
+
|
||||
ElfW(Addr) start = ALIGN_DOWN((l->l_addr
|
||||
+ l->l_relro_addr),
|
||||
GLRO(dl_pagesize));
|
||||
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
||||
index 537d1293c7b5543b..dc4e0555e4ed7f3c 100644
|
||||
--- a/sysdeps/generic/ldsodefs.h
|
||||
+++ b/sysdeps/generic/ldsodefs.h
|
||||
@@ -1073,6 +1073,13 @@ extern void _dl_relocate_object (struct link_map *map,
|
||||
int reloc_mode, int consider_profiling)
|
||||
attribute_hidden;
|
||||
|
||||
+/* Perform relocation, but do not apply RELRO. Does not check
|
||||
+ L->relocated. Otherwise the same as _dl_relocate_object. */
|
||||
+void _dl_relocate_object_no_relro (struct link_map *map,
|
||||
+ struct r_scope_elem *scope[],
|
||||
+ int reloc_mode, int consider_profiling)
|
||||
+ attribute_hidden;
|
||||
+
|
||||
/* Protect PT_GNU_RELRO area. */
|
||||
extern void _dl_protect_relro (struct link_map *map) attribute_hidden;
|
||||
|
||||
203
SOURCES/glibc-RHEL-48820-4.patch
Normal file
203
SOURCES/glibc-RHEL-48820-4.patch
Normal file
@ -0,0 +1,203 @@
|
||||
commit c1560f3f75c0e892b5522c16f91b4e303f677094
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Wed Nov 6 10:33:44 2024 +0100
|
||||
|
||||
elf: Switch to main malloc after final ld.so self-relocation
|
||||
|
||||
Before commit ee1ada1bdb8074de6e1bdc956ab19aef7b6a7872
|
||||
("elf: Rework exception handling in the dynamic loader
|
||||
[BZ #25486]"), the previous order called the main calloc
|
||||
to allocate a shadow GOT/PLT array for auditing support.
|
||||
This happened before libc.so.6 ELF constructors were run, so
|
||||
a user malloc could run without libc.so.6 having been
|
||||
initialized fully. One observable effect was that
|
||||
environ was NULL at this point.
|
||||
|
||||
It does not seem to be possible at present to trigger such
|
||||
an allocation, but it seems more robust to delay switching
|
||||
to main malloc after ld.so self-relocation is complete.
|
||||
The elf/tst-rtld-no-malloc-audit test case fails with a
|
||||
2.34-era glibc that does not have this fix.
|
||||
|
||||
Reviewed-by: DJ Delorie <dj@redhat.com>
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile (fixup context)
|
||||
elf/rtld.c (Align change with glibc-RHEL-48820-1)
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index 41adea8d1c6d13ca..0c8e0d794bac640f 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -439,6 +439,9 @@ tests += \
|
||||
tst-recursive-tls \
|
||||
tst-relsort1 \
|
||||
tst-ro-dynamic \
|
||||
+ tst-rtld-no-malloc \
|
||||
+ tst-rtld-no-malloc-audit \
|
||||
+ tst-rtld-no-malloc-preload \
|
||||
tst-rtld-run-static \
|
||||
tst-single_threaded \
|
||||
tst-single_threaded-pthread \
|
||||
@@ -2896,3 +2899,9 @@ tst-tls22-mod2.so-no-z-defs = yes
|
||||
tst-tls22-mod2-gnu2.so-no-z-defs = yes
|
||||
|
||||
$(objpfx)tst-dlopen-sgid.out: $(objpfx)tst-dlopen-sgid-mod.so
|
||||
+
|
||||
+# Reuse an audit module which provides ample debug logging.
|
||||
+tst-rtld-no-malloc-audit-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
|
||||
+
|
||||
+# Any shared object should do.
|
||||
+tst-rtld-no-malloc-preload-ENV = LD_PRELOAD=$(objpfx)tst-auditmod1.so
|
||||
diff --git a/elf/dl-support.c b/elf/dl-support.c
|
||||
index 1fea55c443505890..00abc2d8056c78b0 100644
|
||||
--- a/elf/dl-support.c
|
||||
+++ b/elf/dl-support.c
|
||||
@@ -353,8 +353,7 @@ _dl_non_dynamic_init (void)
|
||||
}
|
||||
|
||||
/* Setup relro on the binary itself. */
|
||||
- if (_dl_main_map.l_relro_size != 0)
|
||||
- _dl_protect_relro (&_dl_main_map);
|
||||
+ _dl_protect_relro (&_dl_main_map);
|
||||
}
|
||||
|
||||
#ifdef DL_SYSINFO_IMPLEMENTATION
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index 711bb77d70da6563..3436dd918e699080 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -2495,26 +2495,23 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
|
||||
if (! prelinked)
|
||||
{
|
||||
- /* Re-relocate ourselves with user-controlled symbol definitions.
|
||||
-
|
||||
- We must do this after TLS initialization in case after this
|
||||
- re-relocation, we might call a user-supplied function
|
||||
- (e.g. calloc from _dl_relocate_object) that uses TLS data. */
|
||||
-
|
||||
- /* The malloc implementation has been relocated, so resolving
|
||||
- its symbols (and potentially calling IFUNC resolvers) is safe
|
||||
- at this point. */
|
||||
- __rtld_malloc_init_real (main_map);
|
||||
-
|
||||
/* Likewise for the locking implementation. */
|
||||
__rtld_mutex_init ();
|
||||
|
||||
+ /* Re-relocate ourselves with user-controlled symbol definitions. */
|
||||
+
|
||||
RTLD_TIMING_VAR (start);
|
||||
rtld_timer_start (&start);
|
||||
|
||||
- /* Mark the link map as not yet relocated again. */
|
||||
- GL(dl_rtld_map).l_relocated = 0;
|
||||
- _dl_relocate_object (&GL(dl_rtld_map), main_map->l_scope, 0, 0);
|
||||
+ _dl_relocate_object_no_relro (&GL(dl_rtld_map), main_map->l_scope, 0, 0);
|
||||
+
|
||||
+ /* The malloc implementation has been relocated, so resolving
|
||||
+ its symbols (and potentially calling IFUNC resolvers) is safe
|
||||
+ at this point. */
|
||||
+ __rtld_malloc_init_real (main_map);
|
||||
+
|
||||
+ if (GL(dl_rtld_map).l_relro_size != 0)
|
||||
+ _dl_protect_relro (&GL(dl_rtld_map));
|
||||
|
||||
rtld_timer_accum (&relocate_time, start);
|
||||
}
|
||||
diff --git a/elf/tst-rtld-no-malloc-audit.c b/elf/tst-rtld-no-malloc-audit.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..a028377ad1fea027
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-rtld-no-malloc-audit.c
|
||||
@@ -0,0 +1 @@
|
||||
+#include "tst-rtld-no-malloc.c"
|
||||
diff --git a/elf/tst-rtld-no-malloc-preload.c b/elf/tst-rtld-no-malloc-preload.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..a028377ad1fea027
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-rtld-no-malloc-preload.c
|
||||
@@ -0,0 +1 @@
|
||||
+#include "tst-rtld-no-malloc.c"
|
||||
diff --git a/elf/tst-rtld-no-malloc.c b/elf/tst-rtld-no-malloc.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..5f24d4bd72c4af0c
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-rtld-no-malloc.c
|
||||
@@ -0,0 +1,76 @@
|
||||
+/* Test that program loading does not call malloc.
|
||||
+ Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+
|
||||
+#include <string.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+static void
|
||||
+print (const char *s)
|
||||
+{
|
||||
+ const char *end = s + strlen (s);
|
||||
+ while (s < end)
|
||||
+ {
|
||||
+ ssize_t ret = write (STDOUT_FILENO, s, end - s);
|
||||
+ if (ret <= 0)
|
||||
+ _exit (2);
|
||||
+ s += ret;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void __attribute__ ((noreturn))
|
||||
+unexpected_call (const char *function)
|
||||
+{
|
||||
+ print ("error: unexpected call to ");
|
||||
+ print (function);
|
||||
+ print ("\n");
|
||||
+ _exit (1);
|
||||
+}
|
||||
+
|
||||
+/* These are the malloc functions implement in elf/dl-minimal.c. */
|
||||
+
|
||||
+void
|
||||
+free (void *ignored)
|
||||
+{
|
||||
+ unexpected_call ("free");
|
||||
+}
|
||||
+
|
||||
+void *
|
||||
+calloc (size_t ignored1, size_t ignored2)
|
||||
+{
|
||||
+ unexpected_call ("calloc");
|
||||
+}
|
||||
+
|
||||
+void *
|
||||
+malloc (size_t ignored)
|
||||
+{
|
||||
+ unexpected_call ("malloc");
|
||||
+}
|
||||
+
|
||||
+void *
|
||||
+realloc (void *ignored1, size_t ignored2)
|
||||
+{
|
||||
+ unexpected_call ("realloc");
|
||||
+}
|
||||
+
|
||||
+int
|
||||
+main (void)
|
||||
+{
|
||||
+ /* Do not use the test wrapper, to avoid spurious malloc calls from it. */
|
||||
+ return 0;
|
||||
+}
|
||||
228
SOURCES/glibc-RHEL-48820-5.patch
Normal file
228
SOURCES/glibc-RHEL-48820-5.patch
Normal file
@ -0,0 +1,228 @@
|
||||
commit 706209867f1ba89c458033408d419e92d8055f58
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Tue Jan 7 09:18:07 2025 +0100
|
||||
|
||||
elf: Second ld.so relocation only if libc.so has been loaded
|
||||
|
||||
Commit 8f8dd904c4a2207699bb666f30acceb5209c8d3f (“elf:
|
||||
rtld_multiple_ref is always true”) removed some code that happened
|
||||
to enable compatibility with programs that do not link against
|
||||
libc.so. Such programs cannot call dlopen or any dynamic linker
|
||||
functions (except __tls_get_addr), so this is not really useful.
|
||||
Still ld.so should not crash with a null-pointer dereference
|
||||
or undefined symbol reference in these cases.
|
||||
|
||||
In the main relocation loop, call _dl_relocate_object unconditionally
|
||||
because it already checks if the object has been relocated.
|
||||
|
||||
If libc.so was loaded, self-relocate ld.so against it and call
|
||||
__rtld_mutex_init and __rtld_malloc_init_real to activate the full
|
||||
implementations. Those are available only if libc.so is there,
|
||||
so skip these initialization steps if libc.so is absent. Without
|
||||
libc.so, the global scope can be completely empty. This can cause
|
||||
ld.so self-relocation to fail because if it uses symbol-based
|
||||
relocations, which is why the second ld.so self-relocation is not
|
||||
performed if libc.so is missing.
|
||||
|
||||
The previous concern regarding GOT updates through self-relocation
|
||||
no longer applies because function pointers are updated
|
||||
explicitly through __rtld_mutex_init and __rtld_malloc_init_real,
|
||||
and not through relocation. However, the second ld.so self-relocation
|
||||
is still delayed, in case there are other symbols being used.
|
||||
|
||||
Fixes commit 8f8dd904c4a2207699bb666f30acceb5209c8d3f (“elf:
|
||||
rtld_multiple_ref is always true”).
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile (fixup context)
|
||||
elf/rtld.c (
|
||||
- Patch off due to prelink code
|
||||
- "elf: Move _dl_rtld_map, _dl_rtld_audit_state out of GL" not
|
||||
ported downstream
|
||||
)
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index 0c8e0d794bac640f..d30f7f67e73a646e 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -2905,3 +2905,20 @@ tst-rtld-no-malloc-audit-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
|
||||
|
||||
# Any shared object should do.
|
||||
tst-rtld-no-malloc-preload-ENV = LD_PRELOAD=$(objpfx)tst-auditmod1.so
|
||||
+
|
||||
+# These rules link and run the special elf/tst-nolink-libc-* tests if
|
||||
+# a port adds them to the tests variables. Neither test variant is
|
||||
+# linked against libc.so, but tst-nolink-libc-1 is linked against
|
||||
+# ld.so. The test is always run directly, not under the dynamic
|
||||
+# linker.
|
||||
+CFLAGS-tst-nolink-libc.c += $(no-stack-protector)
|
||||
+$(objpfx)tst-nolink-libc-1: $(objpfx)tst-nolink-libc.o $(objpfx)ld.so
|
||||
+ $(LINK.o) -nostdlib -nostartfiles -o $@ $< \
|
||||
+ -Wl,--dynamic-linker=$(objpfx)ld.so,--no-as-needed $(objpfx)ld.so
|
||||
+$(objpfx)tst-nolink-libc-1.out: $(objpfx)tst-nolink-libc-1 $(objpfx)ld.so
|
||||
+ $< > $@ 2>&1; $(evaluate-test)
|
||||
+$(objpfx)tst-nolink-libc-2: $(objpfx)tst-nolink-libc.o
|
||||
+ $(LINK.o) -nostdlib -nostartfiles -o $@ $< \
|
||||
+ -Wl,--dynamic-linker=$(objpfx)ld.so
|
||||
+$(objpfx)tst-nolink-libc-2.out: $(objpfx)tst-nolink-libc-2 $(objpfx)ld.so
|
||||
+ $< > $@ 2>&1; $(evaluate-test)
|
||||
diff --git a/elf/rtld.c b/elf/rtld.c
|
||||
index 3436dd918e699080..d3d9e6b904ac78fd 100644
|
||||
--- a/elf/rtld.c
|
||||
+++ b/elf/rtld.c
|
||||
@@ -2410,25 +2410,25 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
}
|
||||
else
|
||||
{
|
||||
- /* Now we have all the objects loaded. Relocate them all except for
|
||||
- the dynamic linker itself. We do this in reverse order so that copy
|
||||
- relocs of earlier objects overwrite the data written by later
|
||||
- objects. We do not re-relocate the dynamic linker itself in this
|
||||
- loop because that could result in the GOT entries for functions we
|
||||
- call being changed, and that would break us. It is safe to relocate
|
||||
- the dynamic linker out of order because it has no copy relocations.
|
||||
- Likewise for libc, which is relocated early to ensure that IFUNC
|
||||
- resolvers in libc work. */
|
||||
+ /* Now we have all the objects loaded. */
|
||||
|
||||
int consider_profiling = GLRO(dl_profile) != NULL;
|
||||
|
||||
/* If we are profiling we also must do lazy reloaction. */
|
||||
GLRO(dl_lazy) |= consider_profiling;
|
||||
|
||||
+ /* If libc.so has been loaded, relocate it early, after the dynamic
|
||||
+ loader itself. The initial self-relocation of ld.so should be
|
||||
+ sufficient for IFUNC resolvers in libc.so. */
|
||||
if (GL(dl_ns)[LM_ID_BASE].libc_map != NULL)
|
||||
- _dl_relocate_object (GL(dl_ns)[LM_ID_BASE].libc_map,
|
||||
- GL(dl_ns)[LM_ID_BASE].libc_map->l_scope,
|
||||
- GLRO(dl_lazy) ? RTLD_LAZY : 0, consider_profiling);
|
||||
+ {
|
||||
+ RTLD_TIMING_VAR (start);
|
||||
+ rtld_timer_start (&start);
|
||||
+ _dl_relocate_object (GL(dl_ns)[LM_ID_BASE].libc_map,
|
||||
+ GL(dl_ns)[LM_ID_BASE].libc_map->l_scope,
|
||||
+ GLRO(dl_lazy) ? RTLD_LAZY : 0, consider_profiling);
|
||||
+ rtld_timer_accum (&relocate_time, start);
|
||||
+ }
|
||||
|
||||
RTLD_TIMING_VAR (start);
|
||||
rtld_timer_start (&start);
|
||||
@@ -2450,9 +2450,8 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
/* Also allocated with the fake malloc(). */
|
||||
l->l_free_initfini = 0;
|
||||
|
||||
- if (l != &GL(dl_rtld_map))
|
||||
- _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy) ? RTLD_LAZY : 0,
|
||||
- consider_profiling);
|
||||
+ _dl_relocate_object (l, l->l_scope, GLRO(dl_lazy) ? RTLD_LAZY : 0,
|
||||
+ consider_profiling);
|
||||
|
||||
/* Add object to slot information data if necessasy. */
|
||||
if (l->l_tls_blocksize != 0 && __rtld_tls_init_tp_called)
|
||||
@@ -2495,25 +2494,22 @@ dl_main (const ElfW(Phdr) *phdr,
|
||||
|
||||
if (! prelinked)
|
||||
{
|
||||
- /* Likewise for the locking implementation. */
|
||||
- __rtld_mutex_init ();
|
||||
-
|
||||
- /* Re-relocate ourselves with user-controlled symbol definitions. */
|
||||
-
|
||||
- RTLD_TIMING_VAR (start);
|
||||
- rtld_timer_start (&start);
|
||||
-
|
||||
- _dl_relocate_object_no_relro (&GL(dl_rtld_map), main_map->l_scope, 0, 0);
|
||||
-
|
||||
- /* The malloc implementation has been relocated, so resolving
|
||||
- its symbols (and potentially calling IFUNC resolvers) is safe
|
||||
- at this point. */
|
||||
- __rtld_malloc_init_real (main_map);
|
||||
+ /* 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. */
|
||||
+ if (GL(dl_ns)[LM_ID_BASE].libc_map != NULL)
|
||||
+ {
|
||||
+ RTLD_TIMING_VAR (start);
|
||||
+ rtld_timer_start (&start);
|
||||
+ _dl_relocate_object_no_relro (&GL(dl_rtld_map), main_map->l_scope, 0, 0);
|
||||
+ rtld_timer_accum (&relocate_time, start);
|
||||
|
||||
- if (GL(dl_rtld_map).l_relro_size != 0)
|
||||
- _dl_protect_relro (&GL(dl_rtld_map));
|
||||
+ __rtld_mutex_init ();
|
||||
+ __rtld_malloc_init_real (main_map);
|
||||
+ }
|
||||
|
||||
- rtld_timer_accum (&relocate_time, start);
|
||||
+ /* All ld.so initialization is complete. Apply RELRO. */
|
||||
+ _dl_protect_relro (&GL(dl_rtld_map));
|
||||
}
|
||||
|
||||
/* Relocation is complete. Perform early libc initialization. This
|
||||
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
|
||||
index 460ba54a8afcc515..94eb2665b27371a4 100644
|
||||
--- a/sysdeps/unix/sysv/linux/Makefile
|
||||
+++ b/sysdeps/unix/sysv/linux/Makefile
|
||||
@@ -393,7 +393,15 @@ libof-lddlibc4 = lddlibc4
|
||||
others += pldd
|
||||
install-bin += pldd
|
||||
$(objpfx)pldd: $(objpfx)xmalloc.o
|
||||
+
|
||||
+test-internal-extras += tst-nolink-libc
|
||||
+ifeq ($(run-built-tests),yes)
|
||||
+tests-special += \
|
||||
+ $(objpfx)tst-nolink-libc-1.out \
|
||||
+ $(objpfx)tst-nolink-libc-2.out \
|
||||
+ # tests-special
|
||||
endif
|
||||
+endif # $(subdir) == elf
|
||||
|
||||
ifeq ($(subdir),rt)
|
||||
CFLAGS-mq_send.c += -fexceptions
|
||||
diff --git a/sysdeps/unix/sysv/linux/arm/Makefile b/sysdeps/unix/sysv/linux/arm/Makefile
|
||||
index 32db854cbd6bdfd6..ab7ced85408ee515 100644
|
||||
--- a/sysdeps/unix/sysv/linux/arm/Makefile
|
||||
+++ b/sysdeps/unix/sysv/linux/arm/Makefile
|
||||
@@ -1,5 +1,8 @@
|
||||
ifeq ($(subdir),elf)
|
||||
sysdep-rtld-routines += aeabi_read_tp libc-do-syscall
|
||||
+# The test uses INTERNAL_SYSCALL_CALL. In thumb mode, this uses
|
||||
+# an undefined reference to __libc_do_syscall.
|
||||
+CFLAGS-tst-nolink-libc.c += -marm
|
||||
endif
|
||||
|
||||
ifeq ($(subdir),misc)
|
||||
diff --git a/sysdeps/unix/sysv/linux/tst-nolink-libc.c b/sysdeps/unix/sysv/linux/tst-nolink-libc.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..817f37784b4080f9
|
||||
--- /dev/null
|
||||
+++ b/sysdeps/unix/sysv/linux/tst-nolink-libc.c
|
||||
@@ -0,0 +1,25 @@
|
||||
+/* Test program not linked against libc.so and not using any glibc functions.
|
||||
+ Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <sysdep.h>
|
||||
+
|
||||
+void
|
||||
+_start (void)
|
||||
+{
|
||||
+ INTERNAL_SYSCALL_CALL (exit_group, 0);
|
||||
+}
|
||||
28
SOURCES/glibc-RHEL-48820-6.patch
Normal file
28
SOURCES/glibc-RHEL-48820-6.patch
Normal file
@ -0,0 +1,28 @@
|
||||
commit 39183f47d8bc9eda711c9797b18d69d7a02af91c
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Wed Jan 8 16:55:31 2025 +0100
|
||||
|
||||
elf: Minimize library dependencies of tst-nolink-libc.c
|
||||
|
||||
On 32-bit Arm, -fasynchronous-unwind-tables creates a reference
|
||||
to the symbol __aeabi_unwind_cpp_pr0. Compile the tests without
|
||||
this flag even if it is passed as part of CC, to avoid linker
|
||||
failures.
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index d30f7f67e73a646e..dc93f631a682a006 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -2910,8 +2910,10 @@ tst-rtld-no-malloc-preload-ENV = LD_PRELOAD=$(objpfx)tst-auditmod1.so
|
||||
# a port adds them to the tests variables. Neither test variant is
|
||||
# linked against libc.so, but tst-nolink-libc-1 is linked against
|
||||
# ld.so. The test is always run directly, not under the dynamic
|
||||
-# linker.
|
||||
-CFLAGS-tst-nolink-libc.c += $(no-stack-protector)
|
||||
+# linker. It is necessary to minimize run-time dependencies, by
|
||||
+# disabling stack protection and unwinding.
|
||||
+CFLAGS-tst-nolink-libc.c += $(no-stack-protector) \
|
||||
+ -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables
|
||||
$(objpfx)tst-nolink-libc-1: $(objpfx)tst-nolink-libc.o $(objpfx)ld.so
|
||||
$(LINK.o) -nostdlib -nostartfiles -o $@ $< \
|
||||
-Wl,--dynamic-linker=$(objpfx)ld.so,--no-as-needed $(objpfx)ld.so
|
||||
30
SOURCES/glibc-RHEL-48820-7.patch
Normal file
30
SOURCES/glibc-RHEL-48820-7.patch
Normal file
@ -0,0 +1,30 @@
|
||||
commit d30f41d2c9031b0540641af692e56002eab5599f
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Thu Jun 26 11:38:00 2025 +0200
|
||||
|
||||
elf: Add missing DSO dependencies for tst-rtld-no-malloc-{audit,preload}
|
||||
|
||||
Fixes commit c1560f3f75c0e892b5522c16f91b4e303f677094
|
||||
("elf: Switch to main malloc after final ld.so self-relocation").
|
||||
|
||||
Reviewed-by: Frédéric Bérat <fberat@redhat.com>
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile (fixup context)
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index dc93f631a682a006..15bec14364266c77 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -2902,9 +2902,11 @@ $(objpfx)tst-dlopen-sgid.out: $(objpfx)tst-dlopen-sgid-mod.so
|
||||
|
||||
# Reuse an audit module which provides ample debug logging.
|
||||
tst-rtld-no-malloc-audit-ENV = LD_AUDIT=$(objpfx)tst-auditmod1.so
|
||||
+$(objpfx)tst-rtld-no-malloc-audit.out: $(objpfx)tst-auditmod1.so
|
||||
|
||||
# Any shared object should do.
|
||||
tst-rtld-no-malloc-preload-ENV = LD_PRELOAD=$(objpfx)tst-auditmod1.so
|
||||
+$(objpfx)tst-rtld-no-malloc-preload.out: $(objpfx)tst-auditmod1.so
|
||||
|
||||
# These rules link and run the special elf/tst-nolink-libc-* tests if
|
||||
# a port adds them to the tests variables. Neither test variant is
|
||||
55
SOURCES/glibc-RHEL-49549-1.patch
Normal file
55
SOURCES/glibc-RHEL-49549-1.patch
Normal file
@ -0,0 +1,55 @@
|
||||
commit 152f863926e77c6f9c9a8b8779c8084eb844ec44
|
||||
Author: Andreas Schwab <schwab@suse.de>
|
||||
Date: Thu Mar 23 16:18:50 2023 +0100
|
||||
|
||||
_dl_map_object_from_fd: Remove unnecessary debugger notification in error path
|
||||
|
||||
After commit ed3ce71f5c ("elf: Move la_activity (LA_ACT_ADD) after
|
||||
_dl_add_to_namespace_list() (BZ #28062)") it is no longer necessary to
|
||||
reset the debugger state in the error case, since the debugger
|
||||
notification only happens after no more errors can occur.
|
||||
|
||||
diff --git a/elf/dl-load.c b/elf/dl-load.c
|
||||
index 5b0734c816b351f0..98a91d40b74cb76b 100644
|
||||
--- a/elf/dl-load.c
|
||||
+++ b/elf/dl-load.c
|
||||
@@ -950,8 +950,6 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
/* Initialize to keep the compiler happy. */
|
||||
const char *errstring = NULL;
|
||||
int errval = 0;
|
||||
- struct r_debug *r = _dl_debug_update (nsid);
|
||||
- bool make_consistent = false;
|
||||
|
||||
/* Get file information. To match the kernel behavior, do not fill
|
||||
in this information for the executable in case of an explicit
|
||||
@@ -983,14 +981,6 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
free ((void *) l->l_phdr);
|
||||
free (l);
|
||||
free (realname);
|
||||
-
|
||||
- if (make_consistent && r != NULL)
|
||||
- {
|
||||
- r->r_state = RT_CONSISTENT;
|
||||
- _dl_debug_state ();
|
||||
- LIBC_PROBE (map_failed, 2, nsid, r);
|
||||
- }
|
||||
-
|
||||
_dl_signal_error (errval, name, NULL, errstring);
|
||||
}
|
||||
|
||||
@@ -1476,6 +1466,7 @@ cannot enable executable stack as shared object requires");
|
||||
_dl_add_to_namespace_list (l, nsid);
|
||||
|
||||
/* Signal that we are going to add new objects. */
|
||||
+ struct r_debug *r = _dl_debug_update (nsid);
|
||||
if (r->r_state == RT_CONSISTENT)
|
||||
{
|
||||
#ifdef SHARED
|
||||
@@ -1492,7 +1483,6 @@ cannot enable executable stack as shared object requires");
|
||||
r->r_state = RT_ADD;
|
||||
_dl_debug_state ();
|
||||
LIBC_PROBE (map_start, 2, nsid, r);
|
||||
- make_consistent = true;
|
||||
}
|
||||
else
|
||||
assert (r->r_state == RT_ADD);
|
||||
174
SOURCES/glibc-RHEL-49549-2.patch
Normal file
174
SOURCES/glibc-RHEL-49549-2.patch
Normal file
@ -0,0 +1,174 @@
|
||||
commit ab5aa2ee3d3f978e474803cbbc5fe805ad30e293
|
||||
Author: Andreas Schwab <schwab@suse.de>
|
||||
Date: Thu Mar 23 16:46:20 2023 +0100
|
||||
|
||||
dlopen: skip debugger notification for DSO loaded from sprof (bug 30258)
|
||||
|
||||
Avoid inconsistent state in the debugger interface.
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile: Test differences.
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index 86d4dd9960088ee1..721f254d121118c0 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -528,6 +528,7 @@ tests-container += \
|
||||
|
||||
test-srcs = \
|
||||
tst-pathopt \
|
||||
+ tst-sprof-basic \
|
||||
# tests-srcs
|
||||
|
||||
ifeq (yes,$(have-fpie))
|
||||
@@ -860,6 +861,7 @@ modules-names = \
|
||||
tst-sonamemove-linkmod1 \
|
||||
tst-sonamemove-runmod1 \
|
||||
tst-sonamemove-runmod2 \
|
||||
+ tst-sprof-mod \
|
||||
tst-tls19mod1 \
|
||||
tst-tls19mod2 \
|
||||
tst-tls19mod3 \
|
||||
@@ -1114,6 +1116,7 @@ tests-special += \
|
||||
$(objpfx)tst-rtld-help.out \
|
||||
$(objpfx)tst-rtld-load-self.out \
|
||||
$(objpfx)tst-rtld-preload.out \
|
||||
+ $(objpfx)tst-sprof-basic.out \
|
||||
# tests-special
|
||||
endif
|
||||
tests-special += \
|
||||
@@ -2946,3 +2949,11 @@ $(objpfx)tst-dlopen-constructor-null: \
|
||||
$(objpfx)tst-dlopen-constructor-null-mod2.so
|
||||
$(objpfx)tst-dlopen-constructor-null-mod2.so: \
|
||||
$(objpfx)tst-dlopen-constructor-null-mod1.so
|
||||
+
|
||||
+LDFLAGS-tst-sprof-mod.so = -Wl,-soname,tst-sprof-mod.so
|
||||
+$(objpfx)tst-sprof-basic: $(objpfx)tst-sprof-mod.so
|
||||
+$(objpfx)tst-sprof-basic.out: tst-sprof-basic.sh $(objpfx)tst-sprof-basic
|
||||
+ $(SHELL) $< $(common-objpfx) '$(test-wrapper-env)' \
|
||||
+ '$(run-program-env)' > $@; \
|
||||
+ $(evaluate-test)
|
||||
+generated += tst-sprof-mod.so.profile
|
||||
diff --git a/elf/dl-load.c b/elf/dl-load.c
|
||||
index 98a91d40b74cb76b..5c1be6e80cd52e2f 100644
|
||||
--- a/elf/dl-load.c
|
||||
+++ b/elf/dl-load.c
|
||||
@@ -1465,6 +1465,10 @@ cannot enable executable stack as shared object requires");
|
||||
/* Now that the object is fully initialized add it to the object list. */
|
||||
_dl_add_to_namespace_list (l, nsid);
|
||||
|
||||
+ /* Skip auditing and debugger notification when called from 'sprof'. */
|
||||
+ if (mode & __RTLD_SPROF)
|
||||
+ return l;
|
||||
+
|
||||
/* Signal that we are going to add new objects. */
|
||||
struct r_debug *r = _dl_debug_update (nsid);
|
||||
if (r->r_state == RT_CONSISTENT)
|
||||
diff --git a/elf/tst-sprof-basic.c b/elf/tst-sprof-basic.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..5e4083305a4acdd6
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-sprof-basic.c
|
||||
@@ -0,0 +1,25 @@
|
||||
+/* Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+void hello (void);
|
||||
+
|
||||
+int
|
||||
+main (void)
|
||||
+{
|
||||
+ hello ();
|
||||
+ return 0;
|
||||
+}
|
||||
diff --git a/elf/tst-sprof-basic.sh b/elf/tst-sprof-basic.sh
|
||||
new file mode 100755
|
||||
index 0000000000000000..901db61708c1b8ec
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-sprof-basic.sh
|
||||
@@ -0,0 +1,41 @@
|
||||
+#!/bin/sh
|
||||
+# Test basic functionality of sprof
|
||||
+# Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
+# This file is part of the GNU C Library.
|
||||
+
|
||||
+# The GNU C Library is free software; you can redistribute it and/or
|
||||
+# modify it under the terms of the GNU Lesser General Public
|
||||
+# License as published by the Free Software Foundation; either
|
||||
+# version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+# The GNU C Library is distributed in the hope that it will be useful,
|
||||
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+# Lesser General Public License for more details.
|
||||
+
|
||||
+# You should have received a copy of the GNU Lesser General Public
|
||||
+# License along with the GNU C Library; if not, see
|
||||
+# <https://www.gnu.org/licenses/>.
|
||||
+
|
||||
+set -e
|
||||
+
|
||||
+common_objpfx=$1
|
||||
+test_wrapper_env=$2
|
||||
+run_program_env=$3
|
||||
+
|
||||
+sprof_mod=tst-sprof-mod.so
|
||||
+
|
||||
+${test_wrapper_env} \
|
||||
+${run_program_env} \
|
||||
+LD_PROFILE=$sprof_mod \
|
||||
+LD_PROFILE_OUTPUT=${common_objpfx}elf \
|
||||
+LD_LIBRARY_PATH=${common_objpfx}.:${common_objpfx}elf \
|
||||
+ ${common_objpfx}elf/ld.so ${common_objpfx}elf/tst-sprof-basic
|
||||
+
|
||||
+${test_wrapper_env} \
|
||||
+${run_program_env} \
|
||||
+LD_LIBRARY_PATH=${common_objpfx}.:${common_objpfx}elf \
|
||||
+ ${common_objpfx}elf/ld.so ${common_objpfx}elf/sprof -p $sprof_mod \
|
||||
+ ${common_objpfx}elf/${sprof_mod}.profile
|
||||
+
|
||||
+exit $?
|
||||
diff --git a/elf/tst-sprof-mod.c b/elf/tst-sprof-mod.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..8b3f5e8e07f42ae6
|
||||
--- /dev/null
|
||||
+++ b/elf/tst-sprof-mod.c
|
||||
@@ -0,0 +1,24 @@
|
||||
+/* Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+
|
||||
+void
|
||||
+hello (void)
|
||||
+{
|
||||
+ printf ("Hello World\n");
|
||||
+}
|
||||
30
SOURCES/glibc-RHEL-49549-3.patch
Normal file
30
SOURCES/glibc-RHEL-49549-3.patch
Normal file
@ -0,0 +1,30 @@
|
||||
commit f563971b5bf7191acfdd5702fe00878752c2056d
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Tue Sep 26 11:40:12 2023 +0200
|
||||
|
||||
elf: Add dummy declaration of _dl_audit_objclose for !SHARED
|
||||
|
||||
This allows us to avoid some #ifdef SHARED conditionals.
|
||||
|
||||
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
|
||||
|
||||
diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h
|
||||
index 3d9b90a22bfa6a7d..21dbe2d21ed8e605 100644
|
||||
--- a/sysdeps/generic/ldsodefs.h
|
||||
+++ b/sysdeps/generic/ldsodefs.h
|
||||
@@ -1457,7 +1457,14 @@ void DL_ARCH_FIXUP_ATTRIBUTE _dl_audit_pltexit (struct link_map *l,
|
||||
const void *inregs,
|
||||
void *outregs)
|
||||
attribute_hidden;
|
||||
-#endif /* SHARED */
|
||||
+
|
||||
+#else /* !SHARED */
|
||||
+static inline void
|
||||
+_dl_audit_objclose (struct link_map *l)
|
||||
+{
|
||||
+ /* No audit implementation for !SHARED. */
|
||||
+}
|
||||
+#endif /* !SHARED */
|
||||
|
||||
#if PTHREAD_IN_LIBC && defined SHARED
|
||||
/* Recursive locking implementation for use within the dynamic loader.
|
||||
102
SOURCES/glibc-RHEL-49549-4.patch
Normal file
102
SOURCES/glibc-RHEL-49549-4.patch
Normal file
@ -0,0 +1,102 @@
|
||||
commit ed2b8d3a866eb37e069f6a71bdf10421cd4c5e54
|
||||
Author: Adam Sampson <ats@offog.org>
|
||||
Date: Mon May 6 18:16:32 2024 +0100
|
||||
|
||||
ldconfig: Move endswithn into a new header file
|
||||
|
||||
is_gdb_python_file is doing a similar test, so it can use this helper
|
||||
function as well.
|
||||
|
||||
Signed-off-by: Adam Sampson <ats@offog.org>
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/elf/endswith.h b/elf/endswith.h
|
||||
new file mode 100644
|
||||
index 0000000000000000..c6430c48be0c1071
|
||||
--- /dev/null
|
||||
+++ b/elf/endswith.h
|
||||
@@ -0,0 +1,33 @@
|
||||
+/* Copyright (C) 2023-2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ This program is free software; you can redistribute it and/or modify
|
||||
+ it under the terms of the GNU General Public License as published
|
||||
+ by the Free Software Foundation; version 2 of the License, or
|
||||
+ (at your option) any later version.
|
||||
+
|
||||
+ This program 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 General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU General Public License
|
||||
+ along with this program; if not, see <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#ifndef _ENDSWITH_H
|
||||
+#define _ENDSWITH_H
|
||||
+
|
||||
+#include <string.h>
|
||||
+
|
||||
+/* Return true if the N bytes at NAME end with with the characters in
|
||||
+ the string SUFFIX. (NAME[N + 1] does not have to be a null byte.)
|
||||
+ Expected to be called with a string literal for SUFFIX. */
|
||||
+static inline bool
|
||||
+endswithn (const char *name, size_t n, const char *suffix)
|
||||
+{
|
||||
+ return (n >= strlen (suffix)
|
||||
+ && memcmp (name + n - strlen (suffix), suffix,
|
||||
+ strlen (suffix)) == 0);
|
||||
+}
|
||||
+
|
||||
+#endif /* _ENDSWITH_H */
|
||||
diff --git a/elf/ldconfig.c b/elf/ldconfig.c
|
||||
index 4a96c409994d96c8..185c8115ae6b4060 100644
|
||||
--- a/elf/ldconfig.c
|
||||
+++ b/elf/ldconfig.c
|
||||
@@ -41,6 +41,7 @@
|
||||
#include <libgen.h>
|
||||
|
||||
#include <ldconfig.h>
|
||||
+#include <endswith.h>
|
||||
#include <dl-cache.h>
|
||||
#include <dl-hwcaps.h>
|
||||
#include <dl-is_dso.h>
|
||||
@@ -778,17 +779,6 @@ struct dlib_entry
|
||||
struct dlib_entry *next;
|
||||
};
|
||||
|
||||
-/* Return true if the N bytes at NAME end with with the characters in
|
||||
- the string SUFFIX. (NAME[N + 1] does not have to be a null byte.)
|
||||
- Expected to be called with a string literal for SUFFIX. */
|
||||
-static inline bool
|
||||
-endswithn (const char *name, size_t n, const char *suffix)
|
||||
-{
|
||||
- return (n >= strlen (suffix)
|
||||
- && memcmp (name + n - strlen (suffix), suffix,
|
||||
- strlen (suffix)) == 0);
|
||||
-}
|
||||
-
|
||||
/* Skip some temporary DSO files. These files may be partially written
|
||||
and lead to ldconfig crashes when examined. */
|
||||
static bool
|
||||
diff --git a/elf/readlib.c b/elf/readlib.c
|
||||
index 8901de2684835653..2701535e3f24a201 100644
|
||||
--- a/elf/readlib.c
|
||||
+++ b/elf/readlib.c
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <gnu/lib-names.h>
|
||||
|
||||
#include <ldconfig.h>
|
||||
+#include <endswith.h>
|
||||
|
||||
#define Elf32_CLASS ELFCLASS32
|
||||
#define Elf64_CLASS ELFCLASS64
|
||||
@@ -68,7 +69,7 @@ static bool
|
||||
is_gdb_python_file (const char *name)
|
||||
{
|
||||
size_t len = strlen (name);
|
||||
- return len > 7 && strcmp (name + len - 7, "-gdb.py") == 0;
|
||||
+ return endswithn (name, len, "-gdb.py");
|
||||
}
|
||||
|
||||
/* Returns 0 if everything is ok, != 0 in case of error. */
|
||||
76
SOURCES/glibc-RHEL-49549-5.patch
Normal file
76
SOURCES/glibc-RHEL-49549-5.patch
Normal file
@ -0,0 +1,76 @@
|
||||
commit 4a50fdf8b2c1106b50cd9056b4c6f3a72cdeed5f
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Tue Sep 3 17:52:47 2024 +0200
|
||||
|
||||
elf: Update DSO list, write audit log to elf/tst-audit23.out
|
||||
|
||||
After commit 1d5024f4f052c12e404d42d3b5bfe9c3e9fd27c4
|
||||
("support: Build with exceptions and asynchronous unwind tables
|
||||
[BZ #30587]"), libgcc_s is expected to show up in the DSO
|
||||
list on 32-bit Arm. Do not update max_objs because vdso is not
|
||||
tracked (and which is the reason why the test currently passes
|
||||
even with libgcc_s present).
|
||||
|
||||
Also write the log output from the auditor to standard output,
|
||||
for easier test debugging.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c
|
||||
index 4904cf1340a97ee1..6ac1f20af60a2ebb 100644
|
||||
--- a/elf/tst-audit23.c
|
||||
+++ b/elf/tst-audit23.c
|
||||
@@ -85,13 +85,28 @@ do_test (int argc, char *argv[])
|
||||
= support_capture_subprogram (spargv[0], spargv);
|
||||
support_capture_subprocess_check (&result, "tst-audit22", 0, sc_allow_stderr);
|
||||
|
||||
+ {
|
||||
+ FILE *fp = fmemopen (result.err.buffer, result.err.length, "r");
|
||||
+ TEST_VERIFY (fp != NULL);
|
||||
+ unsigned int line = 0;
|
||||
+ char *buffer = NULL;
|
||||
+ size_t buffer_length = 0;
|
||||
+ puts ("info: *** audit log start ***");
|
||||
+ while (xgetline (&buffer, &buffer_length, fp))
|
||||
+ printf ("%6u\t%s", ++line, buffer);
|
||||
+ puts ("info: *** audit log end ***");
|
||||
+ free (buffer);
|
||||
+ xfclose (fp);
|
||||
+ }
|
||||
+
|
||||
/* The expected la_objopen/la_objclose:
|
||||
1. executable
|
||||
2. loader
|
||||
3. libc.so
|
||||
- 4. tst-audit23mod.so
|
||||
- 5. libc.so (LM_ID_NEWLM).
|
||||
- 6. vdso (optional and ignored). */
|
||||
+ 4. libgcc_s.so (one some architectures, for libsupport)
|
||||
+ 5. tst-audit23mod.so
|
||||
+ 6. libc.so (LM_ID_NEWLM).
|
||||
+ vdso (optional and ignored). */
|
||||
enum { max_objs = 6 };
|
||||
struct la_obj_t
|
||||
{
|
||||
@@ -115,8 +130,10 @@ do_test (int argc, char *argv[])
|
||||
TEST_VERIFY (out != NULL);
|
||||
char *buffer = NULL;
|
||||
size_t buffer_length = 0;
|
||||
+ unsigned int line = 0;
|
||||
while (xgetline (&buffer, &buffer_length, out))
|
||||
{
|
||||
+ ++line;
|
||||
if (startswith (buffer, "la_activity: "))
|
||||
{
|
||||
uintptr_t cookie;
|
||||
@@ -174,8 +191,8 @@ do_test (int argc, char *argv[])
|
||||
if (is_vdso (lname))
|
||||
continue;
|
||||
if (nobjs == max_objs)
|
||||
- FAIL_EXIT1 ("non expected la_objopen: %s %"PRIxPTR" %ld",
|
||||
- lname, laddr, lmid);
|
||||
+ FAIL_EXIT1 ("(line %u) non expected la_objopen: %s %"PRIxPTR" %ld",
|
||||
+ line, lname, laddr, lmid);
|
||||
objs[nobjs].lname = lname;
|
||||
objs[nobjs].laddr = laddr;
|
||||
objs[nobjs].lmid = lmid;
|
||||
34
SOURCES/glibc-RHEL-49549-6.patch
Normal file
34
SOURCES/glibc-RHEL-49549-6.patch
Normal file
@ -0,0 +1,34 @@
|
||||
commit a20bc2f6233a726c7df8eaa332b6e498bd59321f
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Nov 29 15:36:40 2024 +0100
|
||||
|
||||
elf: Add the endswith function to <endswith.h>
|
||||
|
||||
And include <stdbool.h> for a definition of bool.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/elf/endswith.h b/elf/endswith.h
|
||||
index c6430c48be0c1071..3954e57f8eff0faa 100644
|
||||
--- a/elf/endswith.h
|
||||
+++ b/elf/endswith.h
|
||||
@@ -17,6 +17,7 @@
|
||||
#ifndef _ENDSWITH_H
|
||||
#define _ENDSWITH_H
|
||||
|
||||
+#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Return true if the N bytes at NAME end with with the characters in
|
||||
@@ -30,4 +31,11 @@ endswithn (const char *name, size_t n, const char *suffix)
|
||||
strlen (suffix)) == 0);
|
||||
}
|
||||
|
||||
+/* Same as endswithn, but uses the entire SUBJECT for matching. */
|
||||
+static inline bool
|
||||
+endswith (const char *subject, const char *suffix)
|
||||
+{
|
||||
+ return endswithn (subject, strlen (subject), suffix);
|
||||
+}
|
||||
+
|
||||
#endif /* _ENDSWITH_H */
|
||||
131
SOURCES/glibc-RHEL-49549-7.patch
Normal file
131
SOURCES/glibc-RHEL-49549-7.patch
Normal file
@ -0,0 +1,131 @@
|
||||
commit 8f36b1469677afe37168f9af1b77402d7a70c673
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Aug 9 15:31:18 2024 +0200
|
||||
|
||||
elf: Signal la_objopen for the proxy link map in dlmopen (bug 31985)
|
||||
|
||||
Previously, the ld.so link map was silently added to the namespace.
|
||||
This change produces an auditing event for it.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/elf/dl-load.c b/elf/dl-load.c
|
||||
index 5c1be6e80cd52e2f..6714807946b60188 100644
|
||||
--- a/elf/dl-load.c
|
||||
+++ b/elf/dl-load.c
|
||||
@@ -928,6 +928,37 @@ _dl_process_pt_gnu_property (struct link_map *l, int fd, const ElfW(Phdr) *ph)
|
||||
}
|
||||
}
|
||||
|
||||
+static void
|
||||
+_dl_notify_new_object (int mode, Lmid_t nsid, struct link_map *l)
|
||||
+{
|
||||
+ /* Signal that we are going to add new objects. */
|
||||
+ struct r_debug *r = _dl_debug_update (nsid);
|
||||
+ if (r->r_state == RT_CONSISTENT)
|
||||
+ {
|
||||
+#ifdef SHARED
|
||||
+ /* Auditing checkpoint: we are going to add new objects. Since this
|
||||
+ is called after _dl_add_to_namespace_list the namespace is guaranteed
|
||||
+ to not be empty. */
|
||||
+ if ((mode & __RTLD_AUDIT) == 0)
|
||||
+ _dl_audit_activity_nsid (nsid, LA_ACT_ADD);
|
||||
+#endif
|
||||
+
|
||||
+ /* Notify the debugger we have added some objects. We need to
|
||||
+ call _dl_debug_initialize in a static program in case dynamic
|
||||
+ linking has not been used before. */
|
||||
+ r->r_state = RT_ADD;
|
||||
+ _dl_debug_state ();
|
||||
+ LIBC_PROBE (map_start, 2, nsid, r);
|
||||
+ }
|
||||
+ else
|
||||
+ assert (r->r_state == RT_ADD);
|
||||
+
|
||||
+#ifdef SHARED
|
||||
+ /* Auditing checkpoint: we have a new object. */
|
||||
+ if (!GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
|
||||
+ _dl_audit_objopen (l, nsid);
|
||||
+#endif
|
||||
+}
|
||||
|
||||
/* Map in the shared object NAME, actually located in REALNAME, and already
|
||||
opened on FD. */
|
||||
@@ -1024,6 +1055,8 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd,
|
||||
/* Add the map for the mirrored object to the object list. */
|
||||
_dl_add_to_namespace_list (l, nsid);
|
||||
|
||||
+ _dl_notify_new_object (mode, nsid, l);
|
||||
+
|
||||
return l;
|
||||
}
|
||||
#endif
|
||||
@@ -1469,33 +1502,7 @@ cannot enable executable stack as shared object requires");
|
||||
if (mode & __RTLD_SPROF)
|
||||
return l;
|
||||
|
||||
- /* Signal that we are going to add new objects. */
|
||||
- struct r_debug *r = _dl_debug_update (nsid);
|
||||
- if (r->r_state == RT_CONSISTENT)
|
||||
- {
|
||||
-#ifdef SHARED
|
||||
- /* Auditing checkpoint: we are going to add new objects. Since this
|
||||
- is called after _dl_add_to_namespace_list the namespace is guaranteed
|
||||
- to not be empty. */
|
||||
- if ((mode & __RTLD_AUDIT) == 0)
|
||||
- _dl_audit_activity_nsid (nsid, LA_ACT_ADD);
|
||||
-#endif
|
||||
-
|
||||
- /* Notify the debugger we have added some objects. We need to
|
||||
- call _dl_debug_initialize in a static program in case dynamic
|
||||
- linking has not been used before. */
|
||||
- r->r_state = RT_ADD;
|
||||
- _dl_debug_state ();
|
||||
- LIBC_PROBE (map_start, 2, nsid, r);
|
||||
- }
|
||||
- else
|
||||
- assert (r->r_state == RT_ADD);
|
||||
-
|
||||
-#ifdef SHARED
|
||||
- /* Auditing checkpoint: we have a new object. */
|
||||
- if (!GL(dl_ns)[l->l_ns]._ns_loaded->l_auditing)
|
||||
- _dl_audit_objopen (l, nsid);
|
||||
-#endif
|
||||
+ _dl_notify_new_object (mode, nsid, l);
|
||||
|
||||
return l;
|
||||
}
|
||||
diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c
|
||||
index 6ac1f20af60a2ebb..0c7ca3112381a31e 100644
|
||||
--- a/elf/tst-audit23.c
|
||||
+++ b/elf/tst-audit23.c
|
||||
@@ -17,6 +17,7 @@
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <array_length.h>
|
||||
+#include <endswith.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <link.h>
|
||||
@@ -106,8 +107,9 @@ do_test (int argc, char *argv[])
|
||||
4. libgcc_s.so (one some architectures, for libsupport)
|
||||
5. tst-audit23mod.so
|
||||
6. libc.so (LM_ID_NEWLM).
|
||||
+ 7. loader (proxy link map in new namespace)
|
||||
vdso (optional and ignored). */
|
||||
- enum { max_objs = 6 };
|
||||
+ enum { max_objs = 7 };
|
||||
struct la_obj_t
|
||||
{
|
||||
char *lname;
|
||||
@@ -236,7 +238,9 @@ do_test (int argc, char *argv[])
|
||||
|
||||
for (size_t i = 0; i < nobjs; i++)
|
||||
{
|
||||
- TEST_COMPARE (objs[i].closed, true);
|
||||
+ /* This subtest currently does not pass because of bug 32065. */
|
||||
+ if (! (endswith (objs[i].lname, LD_SO) && objs[i].lmid != LM_ID_BASE))
|
||||
+ TEST_COMPARE (objs[i].closed, true);
|
||||
free (objs[i].lname);
|
||||
}
|
||||
|
||||
76
SOURCES/glibc-RHEL-49549-8.patch
Normal file
76
SOURCES/glibc-RHEL-49549-8.patch
Normal file
@ -0,0 +1,76 @@
|
||||
commit c4b160744cb39eca20dc36b39c7fa6e10352706c
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Fri Aug 9 16:06:40 2024 +0200
|
||||
|
||||
elf: Call la_objclose for proxy link maps in _dl_fini (bug 32065)
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/elf/dl-fini.c b/elf/dl-fini.c
|
||||
index fa876da0ffa1cf97..133dbac385b34fbb 100644
|
||||
--- a/elf/dl-fini.c
|
||||
+++ b/elf/dl-fini.c
|
||||
@@ -74,6 +74,7 @@ _dl_fini (void)
|
||||
|
||||
unsigned int i;
|
||||
struct link_map *l;
|
||||
+ struct link_map *proxy_link_map = NULL;
|
||||
assert (nloaded != 0 || GL(dl_ns)[ns]._ns_loaded == NULL);
|
||||
for (l = GL(dl_ns)[ns]._ns_loaded, i = 0; l != NULL; l = l->l_next)
|
||||
/* Do not handle ld.so in secondary namespaces. */
|
||||
@@ -89,6 +90,11 @@ _dl_fini (void)
|
||||
are not dlclose()ed from underneath us. */
|
||||
++l->l_direct_opencount;
|
||||
}
|
||||
+ else
|
||||
+ /* Used below to call la_objclose for the ld.so proxy
|
||||
+ link map. */
|
||||
+ proxy_link_map = l;
|
||||
+
|
||||
assert (ns != LM_ID_BASE || i == nloaded);
|
||||
assert (ns == LM_ID_BASE || i == nloaded || i == nloaded - 1);
|
||||
unsigned int nmaps = i;
|
||||
@@ -158,6 +164,9 @@ _dl_fini (void)
|
||||
--l->l_direct_opencount;
|
||||
}
|
||||
|
||||
+ if (proxy_link_map != NULL)
|
||||
+ _dl_audit_objclose (proxy_link_map);
|
||||
+
|
||||
#ifdef SHARED
|
||||
_dl_audit_activity_nsid (ns, LA_ACT_CONSISTENT);
|
||||
#endif
|
||||
diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c
|
||||
index 0c7ca3112381a31e..d5137fd44dacde3c 100644
|
||||
--- a/elf/tst-audit23.c
|
||||
+++ b/elf/tst-audit23.c
|
||||
@@ -236,13 +236,26 @@ do_test (int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
+ Lmid_t lmid_other = LM_ID_NEWLM;
|
||||
+ unsigned int other_namespace_count = 0;
|
||||
for (size_t i = 0; i < nobjs; i++)
|
||||
{
|
||||
- /* This subtest currently does not pass because of bug 32065. */
|
||||
- if (! (endswith (objs[i].lname, LD_SO) && objs[i].lmid != LM_ID_BASE))
|
||||
- TEST_COMPARE (objs[i].closed, true);
|
||||
+ if (objs[i].lmid != LM_ID_BASE)
|
||||
+ {
|
||||
+ if (lmid_other == LM_ID_NEWLM)
|
||||
+ lmid_other = objs[i].lmid;
|
||||
+ TEST_COMPARE (objs[i].lmid, lmid_other);
|
||||
+ ++other_namespace_count;
|
||||
+ if (!(endswith (objs[i].lname, "/" LIBC_SO)
|
||||
+ || endswith (objs[i].lname, "/" LD_SO)))
|
||||
+ FAIL ("unexpected object in secondary namespace: %s",
|
||||
+ objs[i].lname);
|
||||
+ }
|
||||
+ TEST_COMPARE (objs[i].closed, true);
|
||||
free (objs[i].lname);
|
||||
}
|
||||
+ /* Both libc.so and ld.so should be present. */
|
||||
+ TEST_COMPARE (other_namespace_count, 2);
|
||||
|
||||
/* la_activity(LA_ACT_CONSISTENT) should be the last callback received.
|
||||
Since only one link map may be not-CONSISTENT at a time, this also
|
||||
140
SOURCES/glibc-RHEL-49549-9.patch
Normal file
140
SOURCES/glibc-RHEL-49549-9.patch
Normal file
@ -0,0 +1,140 @@
|
||||
commit 495b96e064da605630a23092d1e484ade4bdc093
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Tue Sep 3 17:57:46 2024 +0200
|
||||
|
||||
elf: Reorder audit events in dlcose to match _dl_fini (bug 32066)
|
||||
|
||||
This was discovered after extending elf/tst-audit23 to cover
|
||||
dlclose of the dlmopen namespace.
|
||||
|
||||
Auditors already experience the new order during process
|
||||
shutdown (_dl_fini), so no LAV_CURRENT bump or backwards
|
||||
compatibility code seems necessary.
|
||||
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/elf/dl-close.c b/elf/dl-close.c
|
||||
index 8a4c3528a124d4e7..236d89f67f3bf410 100644
|
||||
--- a/elf/dl-close.c
|
||||
+++ b/elf/dl-close.c
|
||||
@@ -303,6 +303,12 @@ _dl_close_worker (struct link_map *map, bool force)
|
||||
}
|
||||
|
||||
#ifdef SHARED
|
||||
+ /* Auditing checkpoint: we will start deleting objects.
|
||||
+ This is supposed to happen before la_objclose (see _dl_fini),
|
||||
+ but only once per non-recursive dlclose call. */
|
||||
+ if (!unload_any)
|
||||
+ _dl_audit_activity_nsid (nsid, LA_ACT_DELETE);
|
||||
+
|
||||
/* Auditing checkpoint: we remove an object. */
|
||||
_dl_audit_objclose (imap);
|
||||
#endif
|
||||
@@ -463,12 +469,8 @@ _dl_close_worker (struct link_map *map, bool force)
|
||||
if (!unload_any)
|
||||
goto out;
|
||||
|
||||
-#ifdef SHARED
|
||||
- /* Auditing checkpoint: we will start deleting objects. */
|
||||
- _dl_audit_activity_nsid (nsid, LA_ACT_DELETE);
|
||||
-#endif
|
||||
-
|
||||
- /* Notify the debugger we are about to remove some loaded objects. */
|
||||
+ /* Notify the debugger we are about to remove some loaded objects.
|
||||
+ LA_ACT_DELETE has already been signalled above for !unload_any. */
|
||||
struct r_debug *r = _dl_debug_update (nsid);
|
||||
r->r_state = RT_DELETE;
|
||||
_dl_debug_state ();
|
||||
diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c
|
||||
index d5137fd44dacde3c..5e3afd397bb2d3d1 100644
|
||||
--- a/elf/tst-audit23.c
|
||||
+++ b/elf/tst-audit23.c
|
||||
@@ -31,16 +31,21 @@
|
||||
#include <support/xstdio.h>
|
||||
#include <support/xdlfcn.h>
|
||||
#include <support/support.h>
|
||||
+#include <support/test-driver.h>
|
||||
|
||||
static int restart;
|
||||
+static int do_dlclose;
|
||||
#define CMDLINE_OPTIONS \
|
||||
- { "restart", no_argument, &restart, 1 },
|
||||
+ { "restart", no_argument, &restart, 1 }, \
|
||||
+ { "dlclose", no_argument, &do_dlclose, 1 }, \
|
||||
|
||||
static int
|
||||
handle_restart (void)
|
||||
{
|
||||
xdlopen ("tst-audit23mod.so", RTLD_NOW);
|
||||
- xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
|
||||
+ void *handle = xdlmopen (LM_ID_NEWLM, LIBC_SO, RTLD_NOW);
|
||||
+ if (do_dlclose)
|
||||
+ xdlclose (handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -60,8 +65,8 @@ is_vdso (const char *str)
|
||||
|| startswith (str, "linux-vdso");
|
||||
}
|
||||
|
||||
-static int
|
||||
-do_test (int argc, char *argv[])
|
||||
+static void
|
||||
+do_one_test (int argc, char *argv[], bool pass_dlclose_flag)
|
||||
{
|
||||
/* We must have either:
|
||||
- One or four parameters left if called initially:
|
||||
@@ -69,16 +74,15 @@ do_test (int argc, char *argv[])
|
||||
+ "--library-path" optional
|
||||
+ the library path optional
|
||||
+ the application name */
|
||||
- if (restart)
|
||||
- return handle_restart ();
|
||||
-
|
||||
- char *spargv[9];
|
||||
+ char *spargv[10];
|
||||
TEST_VERIFY_EXIT (((argc - 1) + 3) < array_length (spargv));
|
||||
int i = 0;
|
||||
for (; i < argc - 1; i++)
|
||||
spargv[i] = argv[i + 1];
|
||||
spargv[i++] = (char *) "--direct";
|
||||
spargv[i++] = (char *) "--restart";
|
||||
+ if (pass_dlclose_flag)
|
||||
+ spargv[i++] = (char *) "--dlclose";
|
||||
spargv[i] = NULL;
|
||||
|
||||
setenv ("LD_AUDIT", "tst-auditmod23.so", 0);
|
||||
@@ -146,8 +150,14 @@ do_test (int argc, char *argv[])
|
||||
|
||||
/* The cookie identifies the object at the head of the link map,
|
||||
so we only add a new namespace if it changes from the previous
|
||||
- one. This works since dlmopen is the last in the test body. */
|
||||
- if (cookie != last_act_cookie && last_act_cookie != -1)
|
||||
+ one. This works since dlmopen is the last in the test body.
|
||||
+
|
||||
+ Currently, this does not work as expected because there
|
||||
+ is no head link map if a namespace is completely deleted.
|
||||
+ No LA_ACT_CONSISTENT event is generated in that case.
|
||||
+ See the comment in _dl_audit_activity_nsid and bug 32068. */
|
||||
+ if (cookie != last_act_cookie && last_act_cookie != -1
|
||||
+ && !pass_dlclose_flag)
|
||||
TEST_COMPARE (last_act, LA_ACT_CONSISTENT);
|
||||
|
||||
if (this_act == LA_ACT_ADD && acts[nacts] != cookie)
|
||||
@@ -265,7 +275,16 @@ do_test (int argc, char *argv[])
|
||||
|
||||
free (buffer);
|
||||
xfclose (out);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+do_test (int argc, char *argv[])
|
||||
+{
|
||||
+ if (restart)
|
||||
+ return handle_restart ();
|
||||
|
||||
+ do_one_test (argc, argv, false);
|
||||
+ do_one_test (argc, argv, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
647
SOURCES/glibc-RHEL-50086-1.patch
Normal file
647
SOURCES/glibc-RHEL-50086-1.patch
Normal file
@ -0,0 +1,647 @@
|
||||
commit 4f6dae219578d6df475864e273da40bde9d30806
|
||||
Author: Stefan Liebler <stli@linux.ibm.com>
|
||||
Date: Tue Apr 29 13:28:58 2025 +0200
|
||||
|
||||
S390: Add new s390 platform z17.
|
||||
|
||||
The glibc-hwcaps subdirectories are extended by "z17". Libraries are loaded if
|
||||
the z17 facility bits are active:
|
||||
- Miscellaneous-instruction-extensions facility 4
|
||||
- Vector-enhancements-facility 3
|
||||
- Vector-Packed-Decimal-Enhancement Facility 3
|
||||
- CPU: Concurrent-Functions Facility
|
||||
|
||||
tst-glibc-hwcaps.c is extended in order to test z17 via new marker6.
|
||||
In case of running on a z17 with a kernel not recognizing z17 yet,
|
||||
AT_PLATFORM will be z900 but vector-bit in AT_HWCAP is set. This situation
|
||||
is now recognized and this testcase does not fail.
|
||||
|
||||
A fatal glibc error is dumped if glibc was build with architecture
|
||||
level set for z17, but run on an older machine (See dl-hwcap-check.h).
|
||||
Note, you might get an SIGILL before this check if you don't use:
|
||||
configure --with-rtld-early-cflags=-march=<older-machine>
|
||||
|
||||
ld.so --list-diagnostics now also dumps information about s390.cpu_features.
|
||||
|
||||
Independent from z17, the s390x kernel won't introduce new HWCAP-Bits if there
|
||||
is no special handling needed in kernel itself. For z17, we don't have new
|
||||
HWCAP flags, but have to check the facility bits retrieved by
|
||||
stfle-instruction.
|
||||
|
||||
Instead of storing all the stfle-bits (currently four 64bit values) in the
|
||||
cpu_features struct, we now only store those bits, which are needed within
|
||||
glibc itself. Note that we have this list twice, one with original values and
|
||||
the other one which can be filtered with GLIBC_TUNABLES=glibc.cpu.hwcaps.
|
||||
Those new fields are stored in so far reserved space in cpu_features struct.
|
||||
Thus processes started in between the update of glibc package and we e.g. have
|
||||
a new ld.so and an old libc.so, won't crash. The glibc internal ifunc-resolvers
|
||||
would not select the best optimized variant.
|
||||
|
||||
The users of stfle-bits are also updated:
|
||||
- parsing of GLIBC_TUNABLES=glibc.cpu.hwcaps
|
||||
- glibc internal ifunc-resolvers
|
||||
- __libc_ifunc_impl_list
|
||||
- sysconf
|
||||
|
||||
Note: Conflicts were resolved by Stefan Liebler.
|
||||
|
||||
Conflicts:
|
||||
elf/Makefile (fixup context)
|
||||
sysdeps/s390/cpu-features.c (partial re-implement downstream)
|
||||
sysdeps/s390/multiarch/ifunc-impl-list.c (fixup context)
|
||||
sysdeps/s390/s390-64/dl-hwcaps-subdirs.c (fixup context)
|
||||
|
||||
diff --git a/elf/Makefile b/elf/Makefile
|
||||
index ba11f3a8b81e7218..84151f2e59704a43 100644
|
||||
--- a/elf/Makefile
|
||||
+++ b/elf/Makefile
|
||||
@@ -667,6 +667,12 @@ modules-names = \
|
||||
libmarkermod5-3 \
|
||||
libmarkermod5-4 \
|
||||
libmarkermod5-5 \
|
||||
+ libmarkermod6-1 \
|
||||
+ libmarkermod6-2 \
|
||||
+ libmarkermod6-3 \
|
||||
+ libmarkermod6-4 \
|
||||
+ libmarkermod6-5 \
|
||||
+ libmarkermod6-6 \
|
||||
ltglobmod1 \
|
||||
ltglobmod2 \
|
||||
neededobj1 \
|
||||
@@ -2624,6 +2630,7 @@ LDFLAGS-libmarkermod2-1.so += -Wl,-soname,libmarkermod2.so
|
||||
LDFLAGS-libmarkermod3-1.so += -Wl,-soname,libmarkermod3.so
|
||||
LDFLAGS-libmarkermod4-1.so += -Wl,-soname,libmarkermod4.so
|
||||
LDFLAGS-libmarkermod5-1.so += -Wl,-soname,libmarkermod5.so
|
||||
+LDFLAGS-libmarkermod6-1.so += -Wl,-soname,libmarkermod6.so
|
||||
$(objpfx)libmarkermod%.os : markermodMARKER-VALUE.c
|
||||
$(compile-command.c) \
|
||||
-DMARKER=marker$(firstword $(subst -, ,$*)) \
|
||||
@@ -2638,6 +2645,8 @@ $(objpfx)libmarkermod4.so: $(objpfx)libmarkermod4-1.so
|
||||
cp $< $@
|
||||
$(objpfx)libmarkermod5.so: $(objpfx)libmarkermod5-1.so
|
||||
cp $< $@
|
||||
+$(objpfx)libmarkermod6.so: $(objpfx)libmarkermod6-1.so
|
||||
+ cp $< $@
|
||||
|
||||
# tst-glibc-hwcaps-prepend checks that --glibc-hwcaps-prepend is
|
||||
# preferred over auto-detected subdirectories.
|
||||
diff --git a/elf/tst-glibc-hwcaps-cache.script b/elf/tst-glibc-hwcaps-cache.script
|
||||
index d58fc8c5de3c5198..af89e9c6f811f483 100644
|
||||
--- a/elf/tst-glibc-hwcaps-cache.script
|
||||
+++ b/elf/tst-glibc-hwcaps-cache.script
|
||||
@@ -5,6 +5,7 @@ cp $B/elf/libmarkermod2-1.so $L/libmarkermod2.so
|
||||
cp $B/elf/libmarkermod3-1.so $L/libmarkermod3.so
|
||||
cp $B/elf/libmarkermod4-1.so $L/libmarkermod4.so
|
||||
cp $B/elf/libmarkermod5-1.so $L/libmarkermod5.so
|
||||
+cp $B/elf/libmarkermod6-1.so $L/libmarkermod6.so
|
||||
|
||||
mkdirp 0770 $L/glibc-hwcaps/power9
|
||||
cp $B/elf/libmarkermod2-2.so $L/glibc-hwcaps/power9/libmarkermod2.so
|
||||
@@ -26,6 +27,12 @@ cp $B/elf/libmarkermod5-2.so $L/glibc-hwcaps/z13/libmarkermod5.so
|
||||
cp $B/elf/libmarkermod5-3.so $L/glibc-hwcaps/z14/libmarkermod5.so
|
||||
cp $B/elf/libmarkermod5-4.so $L/glibc-hwcaps/z15/libmarkermod5.so
|
||||
cp $B/elf/libmarkermod5-5.so $L/glibc-hwcaps/z16/libmarkermod5.so
|
||||
+mkdirp 0770 $L/glibc-hwcaps/z17
|
||||
+cp $B/elf/libmarkermod6-2.so $L/glibc-hwcaps/z13/libmarkermod6.so
|
||||
+cp $B/elf/libmarkermod6-3.so $L/glibc-hwcaps/z14/libmarkermod6.so
|
||||
+cp $B/elf/libmarkermod6-4.so $L/glibc-hwcaps/z15/libmarkermod6.so
|
||||
+cp $B/elf/libmarkermod6-5.so $L/glibc-hwcaps/z16/libmarkermod6.so
|
||||
+cp $B/elf/libmarkermod6-6.so $L/glibc-hwcaps/z17/libmarkermod6.so
|
||||
|
||||
mkdirp 0770 $L/glibc-hwcaps/x86-64-v2
|
||||
cp $B/elf/libmarkermod2-2.so $L/glibc-hwcaps/x86-64-v2/libmarkermod2.so
|
||||
diff --git a/sysdeps/s390/cpu-features.c b/sysdeps/s390/cpu-features.c
|
||||
index afeb9b56382efa96..e9e7e726b71fb09a 100644
|
||||
--- a/sysdeps/s390/cpu-features.c
|
||||
+++ b/sysdeps/s390/cpu-features.c
|
||||
@@ -28,7 +28,7 @@ extern __typeof (memcmp) MEMCMP_DEFAULT;
|
||||
#if HAVE_TUNABLES
|
||||
# define S390_COPY_CPU_FEATURES(SRC_PTR, DEST_PTR) \
|
||||
(DEST_PTR)->hwcap = (SRC_PTR)->hwcap; \
|
||||
- (DEST_PTR)->stfle_bits[0] = (SRC_PTR)->stfle_bits[0];
|
||||
+ (DEST_PTR)->stfle_filtered = (SRC_PTR)->stfle_filtered;
|
||||
|
||||
static void
|
||||
TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
|
||||
@@ -100,7 +100,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
|
||||
disable = true;
|
||||
hwcap_mask = HWCAP_S390_VXRS | HWCAP_S390_VXRS_EXT
|
||||
| HWCAP_S390_VXRS_EXT2;
|
||||
- stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
|
||||
+ stfle_bits0_mask = S390_STFLE_BIT61_ARCH13_MIE3;
|
||||
}
|
||||
else if ((feature_len == 3 && *feature == 'z'
|
||||
&& MEMCMP_DEFAULT (feature, "z13", 3) == 0)
|
||||
@@ -110,7 +110,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
|
||||
reset_features = true;
|
||||
disable = true;
|
||||
hwcap_mask = HWCAP_S390_VXRS_EXT | HWCAP_S390_VXRS_EXT2;
|
||||
- stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
|
||||
+ stfle_bits0_mask = S390_STFLE_BIT61_ARCH13_MIE3;
|
||||
}
|
||||
else if ((feature_len == 3 && *feature == 'z'
|
||||
&& MEMCMP_DEFAULT (feature, "z14", 3) == 0)
|
||||
@@ -120,14 +120,16 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
|
||||
reset_features = true;
|
||||
disable = true;
|
||||
hwcap_mask = HWCAP_S390_VXRS_EXT2;
|
||||
- stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
|
||||
+ stfle_bits0_mask = S390_STFLE_BIT61_ARCH13_MIE3;
|
||||
}
|
||||
else if ((feature_len == 3 && *feature == 'z'
|
||||
&& (MEMCMP_DEFAULT (feature, "z15", 3) == 0
|
||||
- || MEMCMP_DEFAULT (feature, "z16", 3) == 0))
|
||||
+ || MEMCMP_DEFAULT (feature, "z16", 3) == 0
|
||||
+ || MEMCMP_DEFAULT (feature, "z17", 3) == 0))
|
||||
|| (feature_len == 6
|
||||
&& (MEMCMP_DEFAULT (feature, "arch13", 6) == 0
|
||||
- || MEMCMP_DEFAULT (feature, "arch14", 6) == 0)))
|
||||
+ || MEMCMP_DEFAULT (feature, "arch14", 6) == 0
|
||||
+ || MEMCMP_DEFAULT (feature, "arch15", 6) == 0)))
|
||||
{
|
||||
/* For z15 or newer we don't have to disable something,
|
||||
but we have to reset to the original values. */
|
||||
@@ -165,7 +167,7 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
|
||||
if (feature_len == 10
|
||||
&& MEMCMP_DEFAULT (feature, "STFLE_MIE3", 10) == 0)
|
||||
{
|
||||
- stfle_bits0_mask = S390_STFLE_MASK_ARCH13_MIE3;
|
||||
+ stfle_bits0_mask = S390_STFLE_BIT61_ARCH13_MIE3;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,9 +188,9 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
|
||||
if (stfle_bits0_mask != 0ULL)
|
||||
{
|
||||
if (disable)
|
||||
- cpu_features_curr.stfle_bits[0] &= ~stfle_bits0_mask;
|
||||
+ cpu_features_curr.stfle_filtered &= ~stfle_bits0_mask;
|
||||
else
|
||||
- cpu_features_curr.stfle_bits[0] |= stfle_bits0_mask;
|
||||
+ cpu_features_curr.stfle_filtered |= stfle_bits0_mask;
|
||||
}
|
||||
|
||||
/* Jump over current token ... */
|
||||
@@ -202,14 +204,18 @@ TUNABLE_CALLBACK (set_hwcaps) (tunable_val_t *valp)
|
||||
/* Copy back the features after checking that no unsupported features were
|
||||
enabled by user. */
|
||||
cpu_features->hwcap = cpu_features_curr.hwcap & cpu_features_orig.hwcap;
|
||||
- cpu_features->stfle_bits[0] = cpu_features_curr.stfle_bits[0]
|
||||
- & cpu_features_orig.stfle_bits[0];
|
||||
+ cpu_features->stfle_filtered = cpu_features_curr.stfle_filtered
|
||||
+ & cpu_features_orig.stfle_filtered;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void
|
||||
-init_cpu_features (struct cpu_features *cpu_features)
|
||||
+init_cpu_features_no_tunables (struct cpu_features *cpu_features)
|
||||
{
|
||||
+ /* Only initialize once. */
|
||||
+ if (cpu_features->hwcap != 0)
|
||||
+ return;
|
||||
+
|
||||
/* Fill cpu_features as passed by kernel and machine. */
|
||||
cpu_features->hwcap = GLRO(dl_hwcap);
|
||||
|
||||
@@ -218,20 +224,57 @@ init_cpu_features (struct cpu_features *cpu_features)
|
||||
&& (cpu_features->hwcap & HWCAP_S390_ZARCH)
|
||||
&& (cpu_features->hwcap & HWCAP_S390_HIGH_GPRS)))
|
||||
{
|
||||
- register unsigned long reg0 __asm__("0") = 0;
|
||||
+ unsigned long long stfle_bits[4] = { 0 };
|
||||
+ register unsigned long reg0 __asm__("0") = 3;
|
||||
__asm__ __volatile__(".machine push" "\n\t"
|
||||
".machine \"z9-109\"" "\n\t"
|
||||
".machinemode \"zarch_nohighgprs\"\n\t"
|
||||
"stfle %0" "\n\t"
|
||||
".machine pop" "\n"
|
||||
- : "=QS" (cpu_features->stfle_bits[0]),
|
||||
+ : "=QS" (stfle_bits[0]),
|
||||
"+d" (reg0)
|
||||
: : "cc");
|
||||
+
|
||||
+ unsigned long long internal_stfle_bits = 0;
|
||||
+
|
||||
+ /* Facility bit 34: z10: General instructions extension. */
|
||||
+ if ((stfle_bits[0] & (1ULL << (63 - 34))) != 0)
|
||||
+ internal_stfle_bits |= S390_STFLE_BIT34_Z10;
|
||||
+
|
||||
+ /* Facility bit 45: z196: Distinct operands, popcount, ... */
|
||||
+ if ((stfle_bits[0] & (1ULL << (63 - 45))) != 0)
|
||||
+ internal_stfle_bits |= S390_STFLE_BIT45_Z196;
|
||||
+
|
||||
+ /* Facility bit 61: arch13/z15: Miscellaneous-Instruction-Extensions
|
||||
+ Facility 3, e.g. mvcrl. */
|
||||
+ if ((stfle_bits[0] & (1ULL << (63 - 61))) != 0)
|
||||
+ internal_stfle_bits |= S390_STFLE_BIT61_ARCH13_MIE3;
|
||||
+
|
||||
+ /* Facility bit 84: arch15/z17: Miscellaneous-instruction-extensions 4 */
|
||||
+ if ((stfle_bits[1] & (1ULL << (127 - 84))) != 0)
|
||||
+ internal_stfle_bits |= S390_STFLE_BIT84_ARCH15_MIE4;
|
||||
+
|
||||
+ /* Facility bit 198: arch15/z17: Vector-enhancements-facility 3 */
|
||||
+ if ((stfle_bits[3] & (1ULL << (255 - 198))) != 0)
|
||||
+ internal_stfle_bits |= S390_STFLE_BIT198_ARCH15_VXRS_EXT3;
|
||||
+
|
||||
+ /* Facility bit 199: arch15/z17: Vector-Packed-Decimal-Enhancement 3 */
|
||||
+ if ((stfle_bits[3] & (1ULL << (255 - 199))) != 0)
|
||||
+ internal_stfle_bits |= S390_STFLE_BIT199_ARCH15_VXRS_PDE3;
|
||||
+
|
||||
+ /* Facility bit 201: arch15/z17: CPU: Concurrent-Functions Facility */
|
||||
+ if ((stfle_bits[3] & (1ULL << (255 - 201))) != 0)
|
||||
+ internal_stfle_bits |= S390_STFLE_BIT201_ARCH15_CON;
|
||||
+
|
||||
+ cpu_features->stfle_orig = internal_stfle_bits;
|
||||
+ cpu_features->stfle_filtered = internal_stfle_bits;
|
||||
}
|
||||
- else
|
||||
- {
|
||||
- cpu_features->stfle_bits[0] = 0ULL;
|
||||
- }
|
||||
+}
|
||||
+
|
||||
+static inline void
|
||||
+init_cpu_features (struct cpu_features *cpu_features)
|
||||
+{
|
||||
+ init_cpu_features_no_tunables (cpu_features);
|
||||
|
||||
#if HAVE_TUNABLES
|
||||
TUNABLE_GET (glibc, cpu, hwcaps, tunable_val_t *, TUNABLE_CALLBACK (set_hwcaps));
|
||||
diff --git a/sysdeps/s390/cpu-features.h b/sysdeps/s390/cpu-features.h
|
||||
index 5e6b58f7c5bb07e4..f98654a7223cfc5b 100644
|
||||
--- a/sysdeps/s390/cpu-features.h
|
||||
+++ b/sysdeps/s390/cpu-features.h
|
||||
@@ -18,29 +18,58 @@
|
||||
#ifndef __CPU_FEATURES_S390X_H
|
||||
# define __CPU_FEATURES_S390X_H
|
||||
|
||||
-#define S390_STFLE_BITS_Z10 34 /* General instructions extension */
|
||||
-#define S390_STFLE_BITS_Z196 45 /* Distinct operands, pop ... */
|
||||
-#define S390_STFLE_BITS_ARCH13_MIE3 61 /* Miscellaneous-Instruction-Extensions
|
||||
- Facility 3, e.g. mvcrl. */
|
||||
+/* The following stfle bit definitions are intended to be used for the
|
||||
+ glibc internal stfle_orig and stfle_filtered fields in cpu_features
|
||||
+ struct. They can't be used on the double words retrieved by the
|
||||
+ stfle-instruction. */
|
||||
|
||||
-#define S390_STFLE_MASK_ARCH13_MIE3 (1ULL << (63 - S390_STFLE_BITS_ARCH13_MIE3))
|
||||
+/* Facility bit 34: z10: General instructions extension. */
|
||||
+#define S390_STFLE_BIT34_Z10 (1ULL << 0)
|
||||
|
||||
+/* Facility bit 45: z196: Distinct operands, popcount, ... */
|
||||
+#define S390_STFLE_BIT45_Z196 (1ULL << 1)
|
||||
|
||||
-#define S390_IS_ARCH13_MIE3(STFLE_BITS_ARRAY) \
|
||||
- (((STFLE_BITS_ARRAY)[0] & S390_STFLE_MASK_ARCH13_MIE3) != 0)
|
||||
+/* Facility bit 61: arch13/z15: Miscellaneous-Instruction-Extensions
|
||||
+ Facility 3, e.g. mvcrl. */
|
||||
+#define S390_STFLE_BIT61_ARCH13_MIE3 (1ULL << 2)
|
||||
|
||||
-#define S390_IS_Z196(STFLE_BITS_ARRAY) \
|
||||
- (((STFLE_BITS_ARRAY)[0] & (1ULL << (63 - S390_STFLE_BITS_Z196))) != 0)
|
||||
+/* Facility bit 84: arch15/z17: Miscellaneous-instruction-extensions
|
||||
+ facility 4 */
|
||||
+#define S390_STFLE_BIT84_ARCH15_MIE4 (1ULL << 3)
|
||||
|
||||
-#define S390_IS_Z10(STFLE_BITS_ARRAY) \
|
||||
- (((STFLE_BITS_ARRAY)[0] & (1ULL << (63 - S390_STFLE_BITS_Z10))) != 0)
|
||||
+/* Facility bit 198: arch15/z17: Vector-enhancements-facility 3 */
|
||||
+#define S390_STFLE_BIT198_ARCH15_VXRS_EXT3 (1ULL << 4)
|
||||
+
|
||||
+/* Facility bit 199: arch15/z17: Vector-Packed-Decimal-Enhancement
|
||||
+ Facility 3 */
|
||||
+#define S390_STFLE_BIT199_ARCH15_VXRS_PDE3 (1ULL << 5)
|
||||
+
|
||||
+/* Facility bit 201: arch15/z17: CPU: Concurrent-Functions Facility */
|
||||
+#define S390_STFLE_BIT201_ARCH15_CON (1ULL << 6)
|
||||
+
|
||||
+#define S390_IS_ARCH15(STFLE_BITS) \
|
||||
+ ((((STFLE_BITS) & S390_STFLE_BIT84_ARCH15_MIE4) != 0) \
|
||||
+ && (((STFLE_BITS) & S390_STFLE_BIT198_ARCH15_VXRS_EXT3) != 0) \
|
||||
+ && (((STFLE_BITS) & S390_STFLE_BIT199_ARCH15_VXRS_PDE3) != 0) \
|
||||
+ && (((STFLE_BITS) & S390_STFLE_BIT201_ARCH15_CON) != 0))
|
||||
+
|
||||
+#define S390_IS_ARCH13_MIE3(STFLE_BITS) \
|
||||
+ (((STFLE_BITS) & S390_STFLE_BIT61_ARCH13_MIE3) != 0)
|
||||
+
|
||||
+#define S390_IS_Z196(STFLE_BITS) \
|
||||
+ (((STFLE_BITS) & S390_STFLE_BIT45_Z196) != 0)
|
||||
+
|
||||
+#define S390_IS_Z10(STFLE_BITS) \
|
||||
+ (((STFLE_BITS) & S390_STFLE_BIT34_Z10) != 0)
|
||||
|
||||
struct cpu_features
|
||||
{
|
||||
unsigned long int hwcap;
|
||||
unsigned long int __reserved_hwcap2;
|
||||
- unsigned long long stfle_bits[3];
|
||||
- unsigned long long __reserved[11];
|
||||
+ unsigned long long __reserved;
|
||||
+ unsigned long long stfle_orig;
|
||||
+ unsigned long long stfle_filtered;
|
||||
+ unsigned long long __reserved2[11];
|
||||
};
|
||||
|
||||
#endif /* __CPU_FEATURES_S390X_H */
|
||||
diff --git a/sysdeps/s390/dl-diagnostics-cpu.c b/sysdeps/s390/dl-diagnostics-cpu.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..426af2df7a34b55e
|
||||
--- /dev/null
|
||||
+++ b/sysdeps/s390/dl-diagnostics-cpu.c
|
||||
@@ -0,0 +1,37 @@
|
||||
+/* Print CPU diagnostics data in ld.so. s390 version.
|
||||
+ Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <dl-diagnostics.h>
|
||||
+#include <ldsodefs.h>
|
||||
+#include <cpu-features.h>
|
||||
+
|
||||
+static void
|
||||
+print_cpu_features_value (const char *label, uint64_t value)
|
||||
+{
|
||||
+ _dl_printf ("s390.cpu_features.");
|
||||
+ _dl_diagnostics_print_labeled_value (label, value);
|
||||
+}
|
||||
+
|
||||
+void
|
||||
+_dl_diagnostics_cpu (void)
|
||||
+{
|
||||
+ const struct cpu_features *cpu_features = &GLRO(dl_s390_cpu_features);
|
||||
+ print_cpu_features_value ("hwcap", cpu_features->hwcap);
|
||||
+ print_cpu_features_value ("stfle_orig", cpu_features->stfle_orig);
|
||||
+ print_cpu_features_value ("stfle_filtered", cpu_features->stfle_filtered);
|
||||
+}
|
||||
diff --git a/sysdeps/s390/multiarch/ifunc-impl-list.c b/sysdeps/s390/multiarch/ifunc-impl-list.c
|
||||
index af2c75f5df7c7e1d..4c5fdf6069c4027b 100644
|
||||
--- a/sysdeps/s390/multiarch/ifunc-impl-list.c
|
||||
+++ b/sysdeps/s390/multiarch/ifunc-impl-list.c
|
||||
@@ -83,9 +83,9 @@ __libc_ifunc_impl_list (const char *name, struct libc_ifunc_impl *array,
|
||||
|
||||
/* Get hardware information. */
|
||||
const struct cpu_features *features = &GLRO(dl_s390_cpu_features);
|
||||
- unsigned long int dl_hwcap = features->hwcap;
|
||||
- const unsigned long long * __attribute__((unused)) stfle_bits
|
||||
- = features->stfle_bits;
|
||||
+ unsigned long int dl_hwcap __attribute__ ((unused)) = features->hwcap;
|
||||
+ const unsigned long long __attribute__((unused)) stfle_bits
|
||||
+ = features->stfle_filtered;
|
||||
|
||||
#if HAVE_MEMSET_IFUNC
|
||||
IFUNC_IMPL (i, name, memset,
|
||||
diff --git a/sysdeps/s390/multiarch/ifunc-resolve.h b/sysdeps/s390/multiarch/ifunc-resolve.h
|
||||
index c22d59d2a341fff7..16b358ec2e895a4c 100644
|
||||
--- a/sysdeps/s390/multiarch/ifunc-resolve.h
|
||||
+++ b/sysdeps/s390/multiarch/ifunc-resolve.h
|
||||
@@ -22,7 +22,7 @@
|
||||
#include <cpu-features.h>
|
||||
|
||||
#define s390_libc_ifunc_expr_stfle_init() \
|
||||
- const unsigned long long *stfle_bits = features->stfle_bits;
|
||||
+ const unsigned long long stfle_bits = features->stfle_filtered;
|
||||
|
||||
#define s390_libc_ifunc_expr_init() \
|
||||
const struct cpu_features *features = &GLRO(dl_s390_cpu_features); \
|
||||
diff --git a/sysdeps/s390/s390-64/Makefile b/sysdeps/s390/s390-64/Makefile
|
||||
index 66ed844e68df5159..991025cd2a97c203 100644
|
||||
--- a/sysdeps/s390/s390-64/Makefile
|
||||
+++ b/sysdeps/s390/s390-64/Makefile
|
||||
@@ -11,7 +11,8 @@ $(objpfx)tst-glibc-hwcaps: \
|
||||
$(objpfx)libmarkermod2-1.so \
|
||||
$(objpfx)libmarkermod3-1.so \
|
||||
$(objpfx)libmarkermod4-1.so \
|
||||
- $(objpfx)libmarkermod5-1.so
|
||||
+ $(objpfx)libmarkermod5-1.so \
|
||||
+ $(objpfx)libmarkermod6-1.so
|
||||
$(objpfx)tst-glibc-hwcaps.out: \
|
||||
$(objpfx)libmarkermod2.so \
|
||||
$(objpfx)glibc-hwcaps/z13/libmarkermod2.so \
|
||||
@@ -26,7 +27,14 @@ $(objpfx)tst-glibc-hwcaps.out: \
|
||||
$(objpfx)glibc-hwcaps/z13/libmarkermod5.so \
|
||||
$(objpfx)glibc-hwcaps/z14/libmarkermod5.so \
|
||||
$(objpfx)glibc-hwcaps/z15/libmarkermod5.so \
|
||||
- $(objpfx)glibc-hwcaps/z16/libmarkermod5.so
|
||||
+ $(objpfx)glibc-hwcaps/z16/libmarkermod5.so \
|
||||
+ $(objpfx)libmarkermod6.so \
|
||||
+ $(objpfx)glibc-hwcaps/z13/libmarkermod6.so \
|
||||
+ $(objpfx)glibc-hwcaps/z14/libmarkermod6.so \
|
||||
+ $(objpfx)glibc-hwcaps/z15/libmarkermod6.so \
|
||||
+ $(objpfx)glibc-hwcaps/z16/libmarkermod6.so \
|
||||
+ $(objpfx)glibc-hwcaps/z17/libmarkermod6.so
|
||||
+
|
||||
|
||||
$(objpfx)glibc-hwcaps/z13/libmarkermod2.so: $(objpfx)libmarkermod2-2.so
|
||||
$(make-target-directory)
|
||||
@@ -58,6 +66,21 @@ $(objpfx)glibc-hwcaps/z15/libmarkermod5.so: $(objpfx)libmarkermod5-4.so
|
||||
$(objpfx)glibc-hwcaps/z16/libmarkermod5.so: $(objpfx)libmarkermod5-5.so
|
||||
$(make-target-directory)
|
||||
cp $< $@
|
||||
+$(objpfx)glibc-hwcaps/z13/libmarkermod6.so: $(objpfx)libmarkermod6-2.so
|
||||
+ $(make-target-directory)
|
||||
+ cp $< $@
|
||||
+$(objpfx)glibc-hwcaps/z14/libmarkermod6.so: $(objpfx)libmarkermod6-3.so
|
||||
+ $(make-target-directory)
|
||||
+ cp $< $@
|
||||
+$(objpfx)glibc-hwcaps/z15/libmarkermod6.so: $(objpfx)libmarkermod6-4.so
|
||||
+ $(make-target-directory)
|
||||
+ cp $< $@
|
||||
+$(objpfx)glibc-hwcaps/z16/libmarkermod6.so: $(objpfx)libmarkermod6-5.so
|
||||
+ $(make-target-directory)
|
||||
+ cp $< $@
|
||||
+$(objpfx)glibc-hwcaps/z17/libmarkermod6.so: $(objpfx)libmarkermod6-6.so
|
||||
+ $(make-target-directory)
|
||||
+ cp $< $@
|
||||
|
||||
|
||||
ifeq (no,$(build-hardcoded-path-in-tests))
|
||||
diff --git a/sysdeps/s390/s390-64/dl-hwcap-check.h b/sysdeps/s390/s390-64/dl-hwcap-check.h
|
||||
index 52c609571b32f4ab..d52444419b9a54c1 100644
|
||||
--- a/sysdeps/s390/s390-64/dl-hwcap-check.h
|
||||
+++ b/sysdeps/s390/s390-64/dl-hwcap-check.h
|
||||
@@ -25,8 +25,23 @@
|
||||
static inline void
|
||||
dl_hwcap_check (void)
|
||||
{
|
||||
-#if defined __ARCH__
|
||||
-# if GCCMACRO__ARCH__ >= 14
|
||||
+ /* Note: The s390x kernel won't introduce new HWCAP-Bits if there is
|
||||
+ no special handling needed in kernel itself. Thus we have have
|
||||
+ to check the facility-list retrieved with the stfle instruction.
|
||||
+ We already have a common storage of this list in cpu-features.c.
|
||||
+ This dl-hwcap-check.h file is included in
|
||||
+ sysdeps/unix/sysv/linux/dl-sysdep.c, where also dl-machine.h and
|
||||
+ cpu-features.c is included. Therefore we don't have a special
|
||||
+ include here. */
|
||||
+
|
||||
+#if defined GCCMACRO__ARCH__
|
||||
+# if GCCMACRO__ARCH__ >= 15
|
||||
+ init_cpu_features_no_tunables (&GLRO(dl_s390_cpu_features));
|
||||
+ if (!(S390_IS_ARCH15 (GLRO(dl_s390_cpu_features).stfle_orig)))
|
||||
+ _dl_fatal_printf ("\
|
||||
+Fatal glibc error: CPU lacks VXRS_EXT3/VXRS_PDE3/MIE4/Concurrent-functions \
|
||||
+support (z17 or later required)\n");
|
||||
+# elif GCCMACRO__ARCH__ >= 14
|
||||
if (!(GLRO(dl_hwcap) & HWCAP_S390_VXRS_PDE2))
|
||||
_dl_fatal_printf ("\
|
||||
Fatal glibc error: CPU lacks VXRS_PDE2 support (z16 or later required)\n");
|
||||
@@ -39,7 +54,7 @@ Fatal glibc error: CPU lacks VXRS_EXT2 support (z15 or later required)\n");
|
||||
_dl_fatal_printf ("\
|
||||
Fatal glibc error: CPU lacks VXE support (z14 or later required)\n");
|
||||
# endif
|
||||
-#endif /* __ARCH__ */
|
||||
+#endif /* GCCMACRO__ARCH__ */
|
||||
}
|
||||
|
||||
#endif /* _DL_HWCAP_CHECK_H */
|
||||
diff --git a/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c b/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c
|
||||
index 187d732d560c4a62..0e4aa067c0b87439 100644
|
||||
--- a/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c
|
||||
+++ b/sysdeps/s390/s390-64/dl-hwcaps-subdirs.c
|
||||
@@ -18,9 +18,10 @@
|
||||
|
||||
#include <dl-hwcaps.h>
|
||||
#include <ldsodefs.h>
|
||||
+#include <cpu-features.h>
|
||||
|
||||
-const char _dl_hwcaps_subdirs[] = "z16:z15:z14:z13";
|
||||
-enum { subdirs_count = 4 }; /* Number of components in _dl_hwcaps_subdirs. */
|
||||
+const char _dl_hwcaps_subdirs[] = "z17:z16:z15:z14:z13";
|
||||
+enum { subdirs_count = 5 }; /* Number of components in _dl_hwcaps_subdirs. */
|
||||
|
||||
uint32_t
|
||||
_dl_hwcaps_subdirs_active (void)
|
||||
@@ -57,5 +58,12 @@ _dl_hwcaps_subdirs_active (void)
|
||||
return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active);
|
||||
++active;
|
||||
|
||||
+ /* z17.
|
||||
+ Note: The kernel has not introduced new HWCAP bits as the new facilities do
|
||||
+ not require kernel interaction. Thus we check the features via stfle. */
|
||||
+ if (!(S390_IS_ARCH15 (GLRO(dl_s390_cpu_features).stfle_orig)))
|
||||
+ return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active);
|
||||
+ ++active;
|
||||
+
|
||||
return _dl_hwcaps_subdirs_build_bitmask (subdirs_count, active);
|
||||
}
|
||||
diff --git a/sysdeps/s390/s390-64/tst-glibc-hwcaps.c b/sysdeps/s390/s390-64/tst-glibc-hwcaps.c
|
||||
index f3b8ef3dec80d2d1..211aaf3052a1e12c 100644
|
||||
--- a/sysdeps/s390/s390-64/tst-glibc-hwcaps.c
|
||||
+++ b/sysdeps/s390/s390-64/tst-glibc-hwcaps.c
|
||||
@@ -26,35 +26,53 @@ extern int marker2 (void);
|
||||
extern int marker3 (void);
|
||||
extern int marker4 (void);
|
||||
extern int marker5 (void);
|
||||
+extern int marker6 (void);
|
||||
|
||||
/* Return the arch level, 10 for the baseline libmarkermod*.so's. */
|
||||
static int
|
||||
compute_level (void)
|
||||
{
|
||||
const char *platform = (const char *) getauxval (AT_PLATFORM);
|
||||
+ const unsigned long int hwcap = getauxval (AT_HWCAP);
|
||||
+ const int latest_level = 15;
|
||||
|
||||
/* The arch* versions refer to the edition of the Principles of
|
||||
Operation, and they are off by two when compared with the recent
|
||||
product names. (The code below should not be considered an
|
||||
accurate mapping to Principles of Operation editions for earlier
|
||||
AT_PLATFORM strings). */
|
||||
- if (strcmp (platform, "z900") == 0)
|
||||
- return 10;
|
||||
- if (strcmp (platform, "z990") == 0)
|
||||
- return 10;
|
||||
- if (strcmp (platform, "z9-109") == 0)
|
||||
- return 10;
|
||||
- if (strcmp (platform, "z10") == 0)
|
||||
- return 10;
|
||||
- if (strcmp (platform, "z196") == 0)
|
||||
- return 10;
|
||||
- if (strcmp (platform, "zEC12") == 0)
|
||||
- return 10;
|
||||
+ if ((strcmp (platform, "z900") == 0)
|
||||
+ || (strcmp (platform, "z990") == 0)
|
||||
+ || (strcmp (platform, "z9-109") == 0)
|
||||
+ || (strcmp (platform, "z10") == 0)
|
||||
+ || (strcmp (platform, "z196") == 0)
|
||||
+ || (strcmp (platform, "zEC12") == 0))
|
||||
+ {
|
||||
+ if ((hwcap & HWCAP_S390_VX) == 0)
|
||||
+ {
|
||||
+ /* As vector-support was introduced with the newer z13
|
||||
+ architecture, we are really on one of the tested older
|
||||
+ architectures. */
|
||||
+ return 10;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ /* According to AT_PLATFORM we are on an older architecture
|
||||
+ without vector-support, but according to HWCAPs vector
|
||||
+ registers are supported. This means we are running on a
|
||||
+ new architecture which is not yet known by the kernel.
|
||||
+ Thus the default AT_PLATFORM string is used, which is the
|
||||
+ oldest supported one. For this test, assume we are on
|
||||
+ the latest known architecture. See
|
||||
+ <kernel>/arch/s390/kernel/processor.c:setup_elf_platform().
|
||||
+ */
|
||||
+ return latest_level;
|
||||
+ }
|
||||
+ }
|
||||
|
||||
/* If we are running on z13 or newer and the kernel was booted with novx,
|
||||
then AT_PLATFORM is z13 or newer, but _dl_hwcaps_subdirs_active will
|
||||
return zero and the _dl_hwcaps_subdirs are not searched. */
|
||||
- const unsigned long int hwcap = getauxval (AT_HWCAP);
|
||||
if ((hwcap & HWCAP_S390_VX) == 0)
|
||||
return 10;
|
||||
|
||||
@@ -66,9 +84,12 @@ compute_level (void)
|
||||
return 13;
|
||||
if (strcmp (platform, "z16") == 0)
|
||||
return 14;
|
||||
+ if (strcmp (platform, "z17") == 0)
|
||||
+ return latest_level;
|
||||
+
|
||||
printf ("warning: unrecognized AT_PLATFORM value: %s\n", platform);
|
||||
- /* Assume that the new platform supports z16. */
|
||||
- return 14;
|
||||
+ /* Assume that the new platform supports the latest known architecture. */
|
||||
+ return latest_level;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -80,6 +101,7 @@ do_test (void)
|
||||
TEST_COMPARE (marker3 (), MIN (level - 9, 3));
|
||||
TEST_COMPARE (marker4 (), MIN (level - 9, 4));
|
||||
TEST_COMPARE (marker5 (), MIN (level - 9, 5));
|
||||
+ TEST_COMPARE (marker6 (), MIN (level - 9, 6));
|
||||
return 0;
|
||||
}
|
||||
|
||||
diff --git a/sysdeps/unix/sysv/linux/s390/sysconf.c b/sysdeps/unix/sysv/linux/s390/sysconf.c
|
||||
index 14821b5f248cd991..fc04a7ddedea0d7e 100644
|
||||
--- a/sysdeps/unix/sysv/linux/s390/sysconf.c
|
||||
+++ b/sysdeps/unix/sysv/linux/s390/sysconf.c
|
||||
@@ -65,7 +65,7 @@ get_cache_info (int level, int attr, int type)
|
||||
return 0L;
|
||||
}
|
||||
|
||||
- if (!S390_IS_Z10 (features->stfle_bits))
|
||||
+ if (!S390_IS_Z10 (features->stfle_orig))
|
||||
{
|
||||
/* We are at least on a z9 machine.
|
||||
Return 256byte for LINESIZE for L1 d/i-cache,
|
||||
42
SOURCES/glibc-RHEL-50086-2.patch
Normal file
42
SOURCES/glibc-RHEL-50086-2.patch
Normal file
@ -0,0 +1,42 @@
|
||||
Downstream only.
|
||||
|
||||
From: Stefan Liebler <stli@linux.ibm.com>
|
||||
Date: Tue, 6 May 2025 14:49:22 +0200
|
||||
Subject: [PATCH 2/2] S390: Add z17 to legacy hwcap/platform mechanism
|
||||
|
||||
As glibc 2.34 is still generating ld.so.cache entries for libraries
|
||||
in /usr/lib64/z16/libtest.so, do the same for z17 to have the same behavior.
|
||||
|
||||
See upstream glibc commits:
|
||||
commit b78ff5a25dc8ba9d8c6df10bb0a533254bdd193f
|
||||
'elf: Remove legacy hwcaps support from ldconfig'
|
||||
https://sourceware.org/git/?p=glibc.git;a=commit;h=b78ff5a25dc8ba9d8c6df10bb0a533254bdd193f
|
||||
|
||||
commit c5aa5fd40adc81c4f0b18e01f329aeaf86518c7b
|
||||
'elf: Remove loading legacy hwcaps/platform entries in dynamic loader'
|
||||
https://sourceware.org/git/?p=glibc.git;a=commit;h=c5aa5fd40adc81c4f0b18e01f329aeaf86518c7b
|
||||
|
||||
diff --git a/sysdeps/s390/dl-procinfo-s390.c b/sysdeps/s390/dl-procinfo-s390.c
|
||||
index 559f3827936cd017..7842f7742a829e16 100644
|
||||
--- a/sysdeps/s390/dl-procinfo-s390.c
|
||||
+++ b/sysdeps/s390/dl-procinfo-s390.c
|
||||
@@ -28,5 +28,5 @@ const char _dl_s390_cap_flags[_DL_HWCAP_COUNT][9] =
|
||||
const char _dl_s390_platforms[_DL_PLATFORMS_COUNT][7] =
|
||||
{
|
||||
"g5", "z900", "z990", "z9-109", "z10", "z196", "zEC12", "z13", "z14", "z15",
|
||||
- "z16"
|
||||
+ "z16", "z17"
|
||||
};
|
||||
diff --git a/sysdeps/s390/dl-procinfo.h b/sysdeps/s390/dl-procinfo.h
|
||||
index eb782fc3014cd012..3d78cacc98d79f7f 100644
|
||||
--- a/sysdeps/s390/dl-procinfo.h
|
||||
+++ b/sysdeps/s390/dl-procinfo.h
|
||||
@@ -24,7 +24,7 @@
|
||||
#define _DL_HWCAP_COUNT 23
|
||||
extern const char _dl_s390_cap_flags[_DL_HWCAP_COUNT][9] attribute_hidden;
|
||||
|
||||
-#define _DL_PLATFORMS_COUNT 11
|
||||
+#define _DL_PLATFORMS_COUNT 12
|
||||
extern const char _dl_s390_platforms[_DL_PLATFORMS_COUNT][7] attribute_hidden;
|
||||
|
||||
/* The kernel provides up to 32 capability bits with elf_hwcap. */
|
||||
67
SOURCES/glibc-RHEL-50546-1.patch
Normal file
67
SOURCES/glibc-RHEL-50546-1.patch
Normal file
@ -0,0 +1,67 @@
|
||||
commit 3b21166c4d34ee032093bcf599ffac42ad8a4371
|
||||
Author: Arjun Shankar <arjun@redhat.com>
|
||||
Date: Wed Jun 4 13:08:53 2025 +0200
|
||||
|
||||
manual: Expand Descriptor-Relative Access section
|
||||
|
||||
Improve the clarity of the paragraphs describing common flags and add a
|
||||
list of common error conditions for descriptor-relative functions.
|
||||
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
diff --git a/manual/filesys.texi b/manual/filesys.texi
|
||||
index 450d175e614d8834..28d38f23fc58c51f 100644
|
||||
--- a/manual/filesys.texi
|
||||
+++ b/manual/filesys.texi
|
||||
@@ -310,12 +310,17 @@ This is a GNU extension.
|
||||
The flags argument in @code{@dots{}at} functions can be a combination of
|
||||
the following flags, defined in @file{fcntl.h}. Not all such functions
|
||||
support all flags, and some (such as @code{openat}) do not accept a
|
||||
-flags argument at all.
|
||||
-
|
||||
-In the flag descriptions below, the @dfn{effective final path component}
|
||||
-refers to the final component (basename) of the full path constructed
|
||||
-from the descriptor and file name arguments, using file name lookup, as
|
||||
-described above.
|
||||
+flags argument at all. Although the flags specific to each function have
|
||||
+distinct values from each other, some flags (relevant to different
|
||||
+functions) might share the same value and therefore are not guaranteed to
|
||||
+have unique values.
|
||||
+
|
||||
+A non-exhaustive list of common flags and their descriptions follows. Flags
|
||||
+specific to a function are described alongside the function itself. In
|
||||
+these flag descriptions, the @dfn{effective final path component} refers to
|
||||
+the final component (basename) of the full path constructed from the
|
||||
+descriptor and file name arguments, using file name lookup, as described
|
||||
+above.
|
||||
|
||||
@vtable @code
|
||||
@item AT_EMPTY_PATH
|
||||
@@ -353,6 +358,28 @@ a non-final component of the file name are still followed.
|
||||
argument to the @code{getauxval} function (with @code{AT_@dots{}}
|
||||
constants defined in @file{elf.h}). @xref{Auxiliary Vector}.
|
||||
|
||||
+@cindex common errors in descriptor-relative functions
|
||||
+@cindex common errors in @code{@dots{}at} functions
|
||||
+
|
||||
+The @code{@dots{}at} functions have some common error conditions due to the
|
||||
+nature of descriptor-relative access. A list of common errors and their
|
||||
+descriptions follows. Errors specific to a function are described alongside
|
||||
+the function itself.
|
||||
+
|
||||
+@table @code
|
||||
+@item EBADF
|
||||
+The file name argument is a relative path but the descriptor argument
|
||||
+is neither @code{AT_FDCWD} nor a valid file descriptor.
|
||||
+
|
||||
+@item EINVAL
|
||||
+If the function accepts a @var{flags} argument, the flag combination passed
|
||||
+is not valid for the function.
|
||||
+
|
||||
+@item ENOTDIR
|
||||
+The file name argument is a relative file name but the descriptor
|
||||
+argument is associated with a file that is not a directory.
|
||||
+@end table
|
||||
+
|
||||
@node Accessing Directories
|
||||
@section Accessing Directories
|
||||
@cindex accessing directories
|
||||
50
SOURCES/glibc-RHEL-50546-2.patch
Normal file
50
SOURCES/glibc-RHEL-50546-2.patch
Normal file
@ -0,0 +1,50 @@
|
||||
commit 941157dbcdf1c410960bde991206bfb6d9bb292f
|
||||
Author: Arjun Shankar <arjun@redhat.com>
|
||||
Date: Wed Jun 4 13:08:54 2025 +0200
|
||||
|
||||
manual: Document faccessat
|
||||
|
||||
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
diff --git a/manual/filesys.texi b/manual/filesys.texi
|
||||
index 28d38f23fc58c51f..17c15b54037e719d 100644
|
||||
--- a/manual/filesys.texi
|
||||
+++ b/manual/filesys.texi
|
||||
@@ -3069,6 +3069,29 @@ Flag meaning test for execute/search permission.
|
||||
Flag meaning test for existence of the file.
|
||||
@end deftypevr
|
||||
|
||||
+@deftypefun int faccessat (int @var{filedes}, const char *@var{filename}, int @var{how}, int @var{flags})
|
||||
+@standards{POSIX.1-2008, unistd.h}
|
||||
+@comment Unaudited and therefore marked AC-Unsafe and AS-Unsafe by default
|
||||
+@safety{@mtsafe{}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}}
|
||||
+This function is a descriptor-relative version of the @code{access}
|
||||
+function above. @xref{Descriptor-Relative Access}. The @var{flags}
|
||||
+argument can contain a combination of the flags @code{AT_EACCESS} described
|
||||
+below, @code{AT_EMPTY_PATH}, and @code{AT_SYMLINK_NOFOLLOW}.
|
||||
+
|
||||
+@vtable @code
|
||||
+@item AT_EACCESS
|
||||
+This flag when passed to the @code{faccessat} function causes it to perform
|
||||
+access checks using effective user and group IDs instead of real IDs, which
|
||||
+is the default and matches the @code{access} function.
|
||||
+@end vtable
|
||||
+
|
||||
+Compared to @code{access}, some additional error conditions can occur.
|
||||
+@xref{Descriptor-Relative Access}.
|
||||
+
|
||||
+This function may not work correctly on older kernels missing the
|
||||
+@code{faccessat2} system call.
|
||||
+@end deftypefun
|
||||
+
|
||||
@node File Times
|
||||
@subsection File Times
|
||||
|
||||
@@ -3849,7 +3872,6 @@ creation always works like @code{open} with @code{O_EXCL}.
|
||||
The @code{mkdtemp} function comes from OpenBSD.
|
||||
|
||||
@c FIXME these are undocumented:
|
||||
-@c faccessat
|
||||
@c fchmodat
|
||||
@c fchownat
|
||||
@c futimesat
|
||||
38
SOURCES/glibc-RHEL-50546-3.patch
Normal file
38
SOURCES/glibc-RHEL-50546-3.patch
Normal file
@ -0,0 +1,38 @@
|
||||
commit 49766eb1a5b93d093bd0fada55ca7a42dfdb10d6
|
||||
Author: Arjun Shankar <arjun@redhat.com>
|
||||
Date: Wed Jun 4 13:08:55 2025 +0200
|
||||
|
||||
manual: Document mkdirat
|
||||
|
||||
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
diff --git a/manual/filesys.texi b/manual/filesys.texi
|
||||
index 17c15b54037e719d..d8f362f3beda9b28 100644
|
||||
--- a/manual/filesys.texi
|
||||
+++ b/manual/filesys.texi
|
||||
@@ -1964,6 +1964,17 @@ To use this function, your program should include the header file
|
||||
@pindex sys/stat.h
|
||||
@end deftypefun
|
||||
|
||||
+@deftypefun int mkdirat (int @var{filedes}, const char *@var{filename}, mode_t @var{mode})
|
||||
+@standards{POSIX.1-2008, sys/stat.h}
|
||||
+@comment Unaudited and therefore marked AC-Unsafe and AS-Unsafe by default
|
||||
+@safety{@mtsafe{}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}}
|
||||
+This function is a descriptor-relative version of the @code{mkdir}
|
||||
+function above. @xref{Descriptor-Relative Access}.
|
||||
+
|
||||
+Compared to @code{mkdir}, some additional error conditions can occur.
|
||||
+@xref{Descriptor-Relative Access}.
|
||||
+@end deftypefun
|
||||
+
|
||||
@node File Attributes
|
||||
@section File Attributes
|
||||
|
||||
@@ -3877,7 +3888,6 @@ The @code{mkdtemp} function comes from OpenBSD.
|
||||
@c futimesat
|
||||
@c fstatat (there's a commented-out safety assessment for this one)
|
||||
@c statx
|
||||
-@c mkdirat
|
||||
@c mkfifoat
|
||||
@c name_to_handle_at
|
||||
@c openat
|
||||
38
SOURCES/glibc-RHEL-50546-4.patch
Normal file
38
SOURCES/glibc-RHEL-50546-4.patch
Normal file
@ -0,0 +1,38 @@
|
||||
commit 60f86c9cd062882cbeb04b2944c3dfb7457ee5c5
|
||||
Author: Arjun Shankar <arjun@redhat.com>
|
||||
Date: Wed Jun 4 13:08:56 2025 +0200
|
||||
|
||||
manual: Document renameat
|
||||
|
||||
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
diff --git a/manual/filesys.texi b/manual/filesys.texi
|
||||
index d8f362f3beda9b28..8a173c562fa71f83 100644
|
||||
--- a/manual/filesys.texi
|
||||
+++ b/manual/filesys.texi
|
||||
@@ -1910,6 +1910,17 @@ file systems.
|
||||
@end table
|
||||
@end deftypefun
|
||||
|
||||
+@deftypefun int renameat (int @var{oldfiledes}, const char *@var{oldname}, int @var{newfiledes}, const char *@var{newname})
|
||||
+@standards{POSIX.1-2008, stdio.h}
|
||||
+@comment Unaudited and therefore marked AC-Unsafe and AS-Unsafe by default
|
||||
+@safety{@mtsafe{}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}}
|
||||
+This function is a descriptor-relative version of the @code{rename}
|
||||
+function above. @xref{Descriptor-Relative Access}.
|
||||
+
|
||||
+Compared to @code{rename}, some additional error conditions can occur.
|
||||
+@xref{Descriptor-Relative Access}.
|
||||
+@end deftypefun
|
||||
+
|
||||
@node Creating Directories
|
||||
@section Creating Directories
|
||||
@cindex creating a directory
|
||||
@@ -3893,7 +3904,6 @@ The @code{mkdtemp} function comes from OpenBSD.
|
||||
@c openat
|
||||
@c open_by_handle_at
|
||||
@c readlinkat
|
||||
-@c renameat
|
||||
@c renameat2
|
||||
@c scandirat
|
||||
@c symlinkat
|
||||
51
SOURCES/glibc-RHEL-50546-5.patch
Normal file
51
SOURCES/glibc-RHEL-50546-5.patch
Normal file
@ -0,0 +1,51 @@
|
||||
commit 75b725717ff23d0ae38fc7f4a0361cb1bdffbe2e
|
||||
Author: Arjun Shankar <arjun@redhat.com>
|
||||
Date: Wed Jun 4 13:08:57 2025 +0200
|
||||
|
||||
manual: Document unlinkat
|
||||
|
||||
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
diff --git a/manual/filesys.texi b/manual/filesys.texi
|
||||
index 8a173c562fa71f83..396d68c32925c501 100644
|
||||
--- a/manual/filesys.texi
|
||||
+++ b/manual/filesys.texi
|
||||
@@ -1779,6 +1779,31 @@ file system and can't be modified.
|
||||
@end table
|
||||
@end deftypefun
|
||||
|
||||
+@deftypefun int unlinkat (int @var{filedes}, const char *@var{filename}, int @var{flags})
|
||||
+@standards{POSIX.1-2008, unistd.h}
|
||||
+@comment Unaudited and therefore marked AC-Unsafe and AS-Unsafe by default
|
||||
+@safety{@mtsafe{}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}}
|
||||
+This function is a descriptor-relative version of the @code{unlink}
|
||||
+function above. @xref{Descriptor-Relative Access}. The @var{flags}
|
||||
+argument may either be @code{0} or contain the flag @code{AT_REMOVEDIR}:
|
||||
+
|
||||
+@table @code
|
||||
+@item AT_REMOVEDIR
|
||||
+This flag causes @code{unlinkat} to perform an @code{rmdir} operation on
|
||||
+@code{filename} instead of performing the equivalent of @code{unlink}.
|
||||
+@end table
|
||||
+
|
||||
+Compared to @code{unlink}, some additional error conditions can occur due to
|
||||
+descriptor-relative access. @xref{Descriptor-Relative Access}. In
|
||||
+addition to this, the following other errors can also occur:
|
||||
+
|
||||
+@table @code
|
||||
+@item EISDIR
|
||||
+The effective final path derived from @var{filename} and @var{filedes} is a
|
||||
+directory but @code{AT_REMOVEDIR} was not passed in @code{flags}.
|
||||
+@end table
|
||||
+@end deftypefun
|
||||
+
|
||||
@deftypefun int rmdir (const char *@var{filename})
|
||||
@standards{POSIX.1, unistd.h}
|
||||
@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
|
||||
@@ -3907,6 +3932,5 @@ The @code{mkdtemp} function comes from OpenBSD.
|
||||
@c renameat2
|
||||
@c scandirat
|
||||
@c symlinkat
|
||||
-@c unlinkat
|
||||
@c utimensat
|
||||
@c mknodat
|
||||
97
SOURCES/glibc-RHEL-50546-6.patch
Normal file
97
SOURCES/glibc-RHEL-50546-6.patch
Normal file
@ -0,0 +1,97 @@
|
||||
commit 25f1d945766a3a757d9b54eb48fe7c3c48c0f791
|
||||
Author: Arjun Shankar <arjun@redhat.com>
|
||||
Date: Wed Jun 4 13:08:58 2025 +0200
|
||||
|
||||
manual: Document futimens and utimensat
|
||||
|
||||
Document futimens and utimensat. Also document the EINVAL error
|
||||
condition for futimes. It is inherited by futimens and utimensat as
|
||||
well.
|
||||
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
diff --git a/manual/filesys.texi b/manual/filesys.texi
|
||||
index 396d68c32925c501..f21f21804251e480 100644
|
||||
--- a/manual/filesys.texi
|
||||
+++ b/manual/filesys.texi
|
||||
@@ -3307,6 +3307,10 @@ permission for the file, or be a privileged user.
|
||||
@item EBADF
|
||||
The @var{filedes} argument is not a valid file descriptor.
|
||||
|
||||
+@item EINVAL
|
||||
+At least one of the fields in the @code{tvp} array passed has an invalid
|
||||
+value.
|
||||
+
|
||||
@item EPERM
|
||||
If the @var{times} argument is not a null pointer, you must either be
|
||||
the owner of the file or be a privileged user.
|
||||
@@ -3316,6 +3320,64 @@ The file lives on a read-only file system.
|
||||
@end table
|
||||
@end deftypefun
|
||||
|
||||
+@deftypefun int futimens (int @var{filedes}, const struct timespec @var{tsp}@t{[2]})
|
||||
+@standards{POSIX.1-2008, sys/stat.h}
|
||||
+@comment Unaudited and therefore marked AC-Unsafe and AS-Unsafe by default
|
||||
+@safety{@mtsafe{}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}}
|
||||
+This function is like @code{futimes}, except that it sets the file access
|
||||
+and modification timestamps with nanosecond precision. The argument
|
||||
+@code{tsp} is used similarly to @code{futimes}' @code{tvp}, but has a
|
||||
+@code{const struct timespec} type that can express calendar time with
|
||||
+nanosecond precision. @xref{Time Types}.
|
||||
+@end deftypefun
|
||||
+
|
||||
+@deftypefun int utimensat (int @var{filedes}, const char *@var{filename}, const struct timespec @var{tsp}@t{[2]}, int @var{flags})
|
||||
+@standards{POSIX.1-2008, sys/stat.h}
|
||||
+@comment Unaudited and therefore marked AC-Unsafe and AS-Unsafe by default
|
||||
+@safety{@mtsafe{}@asunsafe{@asucorrupt{}}@acunsafe{@acucorrupt{}}}
|
||||
+This function is a descriptor-relative version of the @code{futimens}
|
||||
+function above. @xref{Descriptor-Relative Access}. The @var{flags}
|
||||
+argument can contain a combination of the flags @code{AT_EMPTY_PATH}, and
|
||||
+@code{AT_SYMLINK_NOFOLLOW}. The call:
|
||||
+
|
||||
+@smallexample
|
||||
+futimens (@var{filedes}, @var{tsp})
|
||||
+@end smallexample
|
||||
+
|
||||
+is equivalent to:
|
||||
+
|
||||
+@smallexample
|
||||
+utimensat (@var{filedes}, @code{NULL}, @var{tsp}, 0)
|
||||
+@end smallexample
|
||||
+
|
||||
+Compared to @code{futimens}, some additional error conditions can occur due
|
||||
+to descriptor-relative access. @xref{Descriptor-Relative Access}. In
|
||||
+addition to this, the following other errors can also occur:
|
||||
+
|
||||
+@table @code
|
||||
+@item EINVAL
|
||||
+The @var{filename} argument is NULL, @var{filedes} is not @code{AT_FDCWD},
|
||||
+and @var{flags} is not @code{0}.
|
||||
+
|
||||
+@item ELOOP
|
||||
+There are too many levels of indirection. This can be the result of
|
||||
+circular symbolic links to directories.
|
||||
+
|
||||
+@item ENAMETOOLONG
|
||||
+The resulting path is too long. This error only occurs on systems which
|
||||
+have a limit on the file name length.
|
||||
+
|
||||
+@item ENOENT
|
||||
+The @var{filename} argument is an empty string and @var{flags} does not
|
||||
+contain @code{AT_EMPTY_PATH}, or @var{filename} does not refer to an
|
||||
+existing file.
|
||||
+
|
||||
+@item ESRCH
|
||||
+Search permission was denied for one of the prefix components of the the
|
||||
+@var{filename} argument.
|
||||
+@end table
|
||||
+@end deftypefun
|
||||
+
|
||||
@node File Size
|
||||
@subsection File Size
|
||||
|
||||
@@ -3932,5 +3994,4 @@ The @code{mkdtemp} function comes from OpenBSD.
|
||||
@c renameat2
|
||||
@c scandirat
|
||||
@c symlinkat
|
||||
-@c utimensat
|
||||
@c mknodat
|
||||
148
SOURCES/glibc-RHEL-53909-1.patch
Normal file
148
SOURCES/glibc-RHEL-53909-1.patch
Normal file
@ -0,0 +1,148 @@
|
||||
commit 2eee835eca960c9d4119279804214b7a1ed5d156
|
||||
Author: DJ Delorie <dj@redhat.com>
|
||||
Date: Thu Aug 8 22:44:56 2024 -0400
|
||||
|
||||
inet: test if_nametoindex and if_indextoname
|
||||
|
||||
Tests for if_nameindex, if_name2index, and if_index2name
|
||||
|
||||
Tests that valid results are consistent.
|
||||
|
||||
Tests that invalid parameters fail correctly.
|
||||
|
||||
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
||||
|
||||
diff --git a/inet/Makefile b/inet/Makefile
|
||||
index ef6b94ed0b519d6d..dbdcea9e71fdd765 100644
|
||||
--- a/inet/Makefile
|
||||
+++ b/inet/Makefile
|
||||
@@ -136,6 +136,7 @@ tests := \
|
||||
tst-getni1 \
|
||||
tst-getni2 \
|
||||
tst-if_index-long \
|
||||
+ tst-if_nameindex \
|
||||
tst-inet6_rth \
|
||||
tst-network \
|
||||
tst-ntoa \
|
||||
diff --git a/inet/tst-if_nameindex.c b/inet/tst-if_nameindex.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..b025cdb3a7c6b68c
|
||||
--- /dev/null
|
||||
+++ b/inet/tst-if_nameindex.c
|
||||
@@ -0,0 +1,116 @@
|
||||
+/* Tests for if_nameindex et al.
|
||||
+ Copyright (C) 2024 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdlib.h>
|
||||
+#include <errno.h>
|
||||
+#include <net/if.h>
|
||||
+#include <netdb.h>
|
||||
+#include <string.h>
|
||||
+#include <support/check.h>
|
||||
+#include <support/descriptors.h>
|
||||
+#include <support/support.h>
|
||||
+
|
||||
+static char *buffer;
|
||||
+
|
||||
+static const char *test_names[] = {
|
||||
+ "testing",
|
||||
+ "invalid",
|
||||
+ "dont-match",
|
||||
+ "",
|
||||
+ "\001\001\001\177",
|
||||
+ NULL
|
||||
+};
|
||||
+
|
||||
+static void
|
||||
+checki (int i)
|
||||
+{
|
||||
+ char *ifname;
|
||||
+
|
||||
+ /* Test that a known-invalid index returns NULL. */
|
||||
+ /* BUFFER should not be accessed. */
|
||||
+
|
||||
+ printf ("Testing if_indextoname (%d) == NULL\n", i);
|
||||
+ ifname = if_indextoname (i, NULL);
|
||||
+ TEST_VERIFY (ifname == NULL);
|
||||
+ TEST_VERIFY (errno == ENXIO);
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ struct if_nameindex *if_ni, *ifp;
|
||||
+ int min_idx, max_idx, buflen = 0;
|
||||
+ int i;
|
||||
+
|
||||
+ if_ni = if_nameindex ();
|
||||
+ TEST_VERIFY (if_ni != NULL);
|
||||
+
|
||||
+ min_idx = max_idx = if_ni->if_index;
|
||||
+
|
||||
+ for (ifp = if_ni; !(ifp->if_index == 0 && ifp->if_name == NULL); ifp++)
|
||||
+ {
|
||||
+ printf ("%u: %s\n", ifp->if_index, ifp->if_name);
|
||||
+ if (ifp->if_index < min_idx)
|
||||
+ min_idx = ifp->if_index;
|
||||
+ if (ifp->if_index > max_idx)
|
||||
+ max_idx = ifp->if_index;
|
||||
+ if (strlen (ifp->if_name) + 1 > buflen)
|
||||
+ buflen = strlen (ifp->if_name) + 1;
|
||||
+ }
|
||||
+ buffer = (char *) xmalloc (buflen);
|
||||
+
|
||||
+ /* Check normal operation. */
|
||||
+ for (ifp = if_ni; !(ifp->if_index == 0 && ifp->if_name == NULL); ifp++)
|
||||
+ {
|
||||
+ unsigned int idx = if_nametoindex (ifp->if_name);
|
||||
+ TEST_VERIFY (idx == ifp->if_index);
|
||||
+
|
||||
+ char *fn = if_indextoname (ifp->if_index, buffer);
|
||||
+ TEST_VERIFY (strcmp (fn, ifp->if_name) == 0);
|
||||
+ }
|
||||
+
|
||||
+ for (i=-2; i<min_idx; i++)
|
||||
+ checki (i);
|
||||
+ for (i=max_idx+1; i<max_idx+3; i++)
|
||||
+ checki (i);
|
||||
+
|
||||
+ /* Check that a known-invalid name returns 0. */
|
||||
+
|
||||
+ for (i=0; test_names[i] != NULL; i++)
|
||||
+ {
|
||||
+ /* Make sure our "invalid" name is really invalid. */
|
||||
+ for (ifp = if_ni; !(ifp->if_index == 0 && ifp->if_name == NULL); ifp++)
|
||||
+ if (strcmp (test_names[i], ifp->if_name) == 0)
|
||||
+ goto not_this_one;
|
||||
+
|
||||
+ printf ("Testing if_nametoindex (%s) == 0\n", test_names[i]);
|
||||
+
|
||||
+ unsigned int idx = if_nametoindex (test_names[i]);
|
||||
+ TEST_VERIFY (idx == 0);
|
||||
+ TEST_VERIFY (errno == ENODEV);
|
||||
+
|
||||
+ not_this_one:
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ if_freenameindex (if_ni);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
20
SOURCES/glibc-RHEL-53909-2.patch
Normal file
20
SOURCES/glibc-RHEL-53909-2.patch
Normal file
@ -0,0 +1,20 @@
|
||||
commit 79f44e1a47e87907fb8e97bbd098e01c4adc26a5
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Mon Aug 26 16:45:31 2024 +0200
|
||||
|
||||
inet: Avoid label at end of compound statement in tst-if_nameindex
|
||||
|
||||
This fails to compile with GCC 8.
|
||||
|
||||
diff --git a/inet/tst-if_nameindex.c b/inet/tst-if_nameindex.c
|
||||
index b025cdb3a7c6b68c..5b905601245bef34 100644
|
||||
--- a/inet/tst-if_nameindex.c
|
||||
+++ b/inet/tst-if_nameindex.c
|
||||
@@ -105,6 +105,7 @@ do_test (void)
|
||||
TEST_VERIFY (errno == ENODEV);
|
||||
|
||||
not_this_one:
|
||||
+ ;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user