Remove default value for LD_PROFILE_OUTPUT

Resolves: RHEL-142193
This commit is contained in:
Patsy Griffin 2026-02-10 08:38:45 -05:00 committed by Florian Weimer
parent 67904a03a3
commit 6748bccde4
7 changed files with 1235 additions and 0 deletions

267
glibc-RHEL-142193-1.patch Normal file
View File

@ -0,0 +1,267 @@
commit 1e1ad714ee9a663eda0e2bffad1d9f258b00a4e9
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Mon May 6 13:18:47 2024 -0300
support: Add envp argument to support_capture_subprogram
So tests can specify a list of environment variables.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Conflicts:
elf/tst-tunables-enable_secure.c
(file does not exist downstream)
diff --git a/elf/tst-audit18.c b/elf/tst-audit18.c
index 841251dd7003aa7d..cec93e269ca0b4ef 100644
--- a/elf/tst-audit18.c
+++ b/elf/tst-audit18.c
@@ -79,7 +79,7 @@ do_test (int argc, char *argv[])
setenv ("LD_AUDIT", "tst-auditmod18.so", 0);
struct support_capture_subprocess result
- = support_capture_subprogram (spargv[0], spargv);
+ = support_capture_subprogram (spargv[0], spargv, NULL);
support_capture_subprocess_check (&result, "tst-audit18", 0, sc_allow_stderr);
struct
diff --git a/elf/tst-audit19b.c b/elf/tst-audit19b.c
index 70bfe4eadf5ee845..88d99a416bbe93dd 100644
--- a/elf/tst-audit19b.c
+++ b/elf/tst-audit19b.c
@@ -69,7 +69,7 @@ do_test (int argc, char *argv[])
setenv ("LD_AUDIT", "tst-auditmod18b.so", 0);
struct support_capture_subprocess result
- = support_capture_subprogram (spargv[0], spargv);
+ = support_capture_subprogram (spargv[0], spargv, NULL);
support_capture_subprocess_check (&result, "tst-audit18b", 0, sc_allow_stderr);
bool find_symbind = false;
diff --git a/elf/tst-audit22.c b/elf/tst-audit22.c
index 4e97be3be0c6a2e3..6aa18af948afa9c5 100644
--- a/elf/tst-audit22.c
+++ b/elf/tst-audit22.c
@@ -83,7 +83,7 @@ do_test (int argc, char *argv[])
setenv ("LD_AUDIT", "tst-auditmod22.so", 0);
struct support_capture_subprocess result
- = support_capture_subprogram (spargv[0], spargv);
+ = support_capture_subprogram (spargv[0], spargv, NULL);
support_capture_subprocess_check (&result, "tst-audit22", 0, sc_allow_stderr);
/* The respawned process should always print the vDSO address (otherwise it
diff --git a/elf/tst-audit23.c b/elf/tst-audit23.c
index 1b76336595fcd301..7786a74e648aeb6f 100644
--- a/elf/tst-audit23.c
+++ b/elf/tst-audit23.c
@@ -87,7 +87,7 @@ do_one_test (int argc, char *argv[], bool pass_dlclose_flag)
setenv ("LD_AUDIT", "tst-auditmod23.so", 0);
struct support_capture_subprocess result
- = support_capture_subprogram (spargv[0], spargv);
+ = support_capture_subprogram (spargv[0], spargv, NULL);
support_capture_subprocess_check (&result, "tst-audit22", 0, sc_allow_stderr);
{
diff --git a/elf/tst-audit25a.c b/elf/tst-audit25a.c
index b209ee820f2f2a02..cdd4f2ce2b54d622 100644
--- a/elf/tst-audit25a.c
+++ b/elf/tst-audit25a.c
@@ -77,7 +77,7 @@ do_test (int argc, char *argv[])
{
struct support_capture_subprocess result
- = support_capture_subprogram (spargv[0], spargv);
+ = support_capture_subprogram (spargv[0], spargv, NULL);
support_capture_subprocess_check (&result, "tst-audit25a", 0,
sc_allow_stderr);
@@ -102,7 +102,7 @@ do_test (int argc, char *argv[])
{
setenv ("LD_BIND_NOW", "1", 0);
struct support_capture_subprocess result
- = support_capture_subprogram (spargv[0], spargv);
+ = support_capture_subprogram (spargv[0], spargv, NULL);
support_capture_subprocess_check (&result, "tst-audit25a", 0,
sc_allow_stderr);
diff --git a/elf/tst-audit25b.c b/elf/tst-audit25b.c
index 9b8665d5171b7c6b..939f4d6188368540 100644
--- a/elf/tst-audit25b.c
+++ b/elf/tst-audit25b.c
@@ -76,7 +76,7 @@ do_test (int argc, char *argv[])
{
struct support_capture_subprocess result
- = support_capture_subprogram (spargv[0], spargv);
+ = support_capture_subprogram (spargv[0], spargv, NULL);
support_capture_subprocess_check (&result, "tst-audit25a", 0,
sc_allow_stderr);
@@ -102,7 +102,7 @@ do_test (int argc, char *argv[])
{
setenv ("LD_BIND_NOW", "1", 0);
struct support_capture_subprocess result
- = support_capture_subprogram (spargv[0], spargv);
+ = support_capture_subprogram (spargv[0], spargv, NULL);
support_capture_subprocess_check (&result, "tst-audit25a", 0,
sc_allow_stderr);
diff --git a/elf/tst-glibc-hwcaps-2-cache.c b/elf/tst-glibc-hwcaps-2-cache.c
index 81ab44ff78ddbb57..af91476ccafeecff 100644
--- a/elf/tst-glibc-hwcaps-2-cache.c
+++ b/elf/tst-glibc-hwcaps-2-cache.c
@@ -32,7 +32,7 @@ main (int argc, char **argv)
/* Run ldconfig to populate the cache. */
char *command = xasprintf ("%s/ldconfig", support_install_rootsbindir);
struct support_capture_subprocess result =
- support_capture_subprogram (command, &((char *) { NULL }));
+ support_capture_subprogram (command, &((char *) { NULL }), NULL);
support_capture_subprocess_check (&result, "ldconfig", 0, sc_allow_none);
free (command);
diff --git a/elf/tst-rtld-run-static.c b/elf/tst-rtld-run-static.c
index b2650e85ffbcdc20..f05c00eb7b76958b 100644
--- a/elf/tst-rtld-run-static.c
+++ b/elf/tst-rtld-run-static.c
@@ -30,7 +30,7 @@ do_test (void)
{
char *argv[] = { (char *) "ld.so", ldconfig_path, (char *) "--help", NULL };
struct support_capture_subprocess cap
- = support_capture_subprogram (support_objdir_elf_ldso, argv);
+ = support_capture_subprogram (support_objdir_elf_ldso, argv, NULL);
support_capture_subprocess_check (&cap, "no --argv0", 0, sc_allow_stdout);
puts ("info: output without --argv0:");
puts (cap.out.buffer);
@@ -46,7 +46,7 @@ do_test (void)
ldconfig_path, (char *) "--help", NULL
};
struct support_capture_subprocess cap
- = support_capture_subprogram (support_objdir_elf_ldso, argv);
+ = support_capture_subprogram (support_objdir_elf_ldso, argv, NULL);
support_capture_subprocess_check (&cap, "with --argv0", 0, sc_allow_stdout);
puts ("info: output with --argv0:");
puts (cap.out.buffer);
diff --git a/elf/tst-tunables.c b/elf/tst-tunables.c
index dff34ed748b4ae83..4884dd09f0ddc505 100644
--- a/elf/tst-tunables.c
+++ b/elf/tst-tunables.c
@@ -396,7 +396,7 @@ do_test (int argc, char *argv[])
tests[i].value);
setenv (tests[i].name, tests[i].value, 1);
struct support_capture_subprocess result
- = support_capture_subprogram (spargv[0], spargv);
+ = support_capture_subprogram (spargv[0], spargv, NULL);
support_capture_subprocess_check (&result, "tst-tunables", 0,
sc_allow_stderr);
support_capture_subprocess_free (&result);
diff --git a/support/capture_subprocess.h b/support/capture_subprocess.h
index 8cbdca3b9dfb41ba..57bb941e7d1e5c84 100644
--- a/support/capture_subprocess.h
+++ b/support/capture_subprocess.h
@@ -35,11 +35,12 @@ struct support_capture_subprocess
struct support_capture_subprocess support_capture_subprocess
(void (*callback) (void *), void *closure);
-/* Issue FILE with ARGV arguments by using posix_spawn and capture standard
- output, standard error, and the exit status. The out.buffer and err.buffer
- are handle as support_capture_subprocess. */
+/* Issue FILE with ARGV arguments and ENVP environments by using posix_spawn
+ and capture standard output, standard error, and the exit status. If
+ ENVP is NULL the current environment variable is used. The out.buffer and
+ err.buffer are handle by support_capture_subprocess. */
struct support_capture_subprocess support_capture_subprogram
- (const char *file, char *const argv[]);
+ (const char *file, char *const argv[], char *const envp[]);
/* Copy the running program into a setgid binary and run it with
CHILD_ID argument. If the program exits with a non-zero status,
diff --git a/support/subprocess.h b/support/subprocess.h
index 8fbb895353d61965..8274a2b22bb10296 100644
--- a/support/subprocess.h
+++ b/support/subprocess.h
@@ -33,10 +33,11 @@ struct support_subprocess
struct support_subprocess support_subprocess
(void (*callback) (void *), void *closure);
-/* Issue FILE with ARGV arguments by using posix_spawn and return is PID, a
- pipe redirected to STDOUT, and a pipe redirected to STDERR. */
+/* Issue FILE with ARGV arguments and ENVP environments by using posix_spawn
+ and return is PID, a pipe redirected to STDOUT, and a pipe redirected to
+ STDERR. If ENVP is NULL the current environment variable is used. */
struct support_subprocess support_subprogram
- (const char *file, char *const argv[]);
+ (const char *file, char *const argv[], char *const envp[]);
/* Invoke program FILE with ARGV arguments by using posix_spawn and wait for it
to complete. Return program exit status. */
diff --git a/support/support_capture_subprocess.c b/support/support_capture_subprocess.c
index 8dc95f8aa723b6bc..cbc695106483ab54 100644
--- a/support/support_capture_subprocess.c
+++ b/support/support_capture_subprocess.c
@@ -98,13 +98,14 @@ support_capture_subprocess (void (*callback) (void *), void *closure)
}
struct support_capture_subprocess
-support_capture_subprogram (const char *file, char *const argv[])
+support_capture_subprogram (const char *file, char *const argv[],
+ char *const envp[])
{
struct support_capture_subprocess result;
xopen_memstream (&result.out);
xopen_memstream (&result.err);
- struct support_subprocess proc = support_subprogram (file, argv);
+ struct support_subprocess proc = support_subprogram (file, argv, envp);
support_capture_poll (&result, &proc);
return result;
diff --git a/support/support_subprocess.c b/support/support_subprocess.c
index a2fef394d42ea4f9..b692a7f8b178502d 100644
--- a/support/support_subprocess.c
+++ b/support/support_subprocess.c
@@ -69,7 +69,7 @@ support_subprocess (void (*callback) (void *), void *closure)
}
struct support_subprocess
-support_subprogram (const char *file, char *const argv[])
+support_subprogram (const char *file, char *const argv[], char *const envp[])
{
struct support_subprocess result = support_subprocess_init ();
@@ -84,7 +84,8 @@ support_subprogram (const char *file, char *const argv[])
xposix_spawn_file_actions_addclose (&fa, result.stdout_pipe[1]);
xposix_spawn_file_actions_addclose (&fa, result.stderr_pipe[1]);
- result.pid = xposix_spawn (file, &fa, NULL, argv, environ);
+ result.pid = xposix_spawn (file, &fa, NULL, argv,
+ envp == NULL ? environ : envp);
xclose (result.stdout_pipe[1]);
xclose (result.stderr_pipe[1]);
diff --git a/support/tst-support_capture_subprocess.c b/support/tst-support_capture_subprocess.c
index 8145548982a935cb..756fb75d195cdf8a 100644
--- a/support/tst-support_capture_subprocess.c
+++ b/support/tst-support_capture_subprocess.c
@@ -238,7 +238,7 @@ do_subprogram (const struct test *test)
args[argc] = NULL;
TEST_VERIFY (argc < argv_size);
- return support_capture_subprogram (args[0], args);
+ return support_capture_subprogram (args[0], args, NULL);
}
enum test_type
diff --git a/sysdeps/x86/tst-hwcap-tunables.c b/sysdeps/x86/tst-hwcap-tunables.c
index bc573c7435130dee..94307283d7cdbdc7 100644
--- a/sysdeps/x86/tst-hwcap-tunables.c
+++ b/sysdeps/x86/tst-hwcap-tunables.c
@@ -133,7 +133,7 @@ do_test (int argc, char *argv[])
setenv ("GLIBC_TUNABLES", tunable, 1);
struct support_capture_subprocess result
- = support_capture_subprogram (spargv[0], spargv);
+ = support_capture_subprogram (spargv[0], spargv, NULL);
support_capture_subprocess_check (&result, "tst-tunables", 0,
sc_allow_stderr);
support_capture_subprocess_free (&result);

171
glibc-RHEL-142193-2.patch Normal file
View File

@ -0,0 +1,171 @@
commit 7b543dcdf97d07fd4346feb17916e08fe83ad0ae
Author: Florian Weimer <fweimer@redhat.com>
Date: Thu Jan 15 22:29:46 2026 +0100
elf: Ignore LD_PROFILE if LD_PROFILE_OUTPUT is not set (bug 33797)
The previous default for LD_PROFILE_OUTPUT, /var/tmp, is insecure
because it's typically a 1777 directory, and other systems could
place malicious files there which interfere with execution.
Requiring the user to specify a profiling directory mitigates
the impact of bug 33797. Clear LD_PROFILE_OUTPUT alongside
with LD_PROFILE.
Rework the test not to use predictable file names.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Conflicts:
elf/tst-env-setuid.c
(Downstream is missing commit 5d23dfb289174d73b8907b86d2bef7a3ca889840)
diff --git a/elf/rtld.c b/elf/rtld.c
index cd790e37f2a323a4..8b189a87f76e7e08 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -360,7 +360,6 @@ struct rtld_global_ro _rtld_global_ro attribute_relro =
._dl_fpu_control = _FPU_DEFAULT,
._dl_pagesize = EXEC_PAGESIZE,
._dl_inhibit_cache = 0,
- ._dl_profile_output = "/var/tmp",
/* Function pointers. */
._dl_debug_printf = _dl_debug_printf,
@@ -2735,6 +2734,15 @@ process_envvars_default (struct dl_main_state *state)
}
}
+ /* There is no fixed, safe directory to store profiling data, so
+ activate LD_PROFILE only if LD_PROFILE_OUTPUT is set as well. */
+ if (GLRO(dl_profile) != NULL && GLRO(dl_profile_output) == NULL)
+ {
+ _dl_error_printf ("\
+warning: LD_PROFILE ignored because LD_PROFILE_OUTPUT not specified\n");
+ GLRO(dl_profile) = NULL;
+ }
+
/* If we have to run the dynamic linker in debugging mode and the
LD_DEBUG_OUTPUT environment variable is given, we write the debug
messages to this file. */
diff --git a/elf/tst-env-setuid.c b/elf/tst-env-setuid.c
index c084aa4c1a382152..ff4acd07e5e8b986 100644
--- a/elf/tst-env-setuid.c
+++ b/elf/tst-env-setuid.c
@@ -40,6 +40,12 @@ static char SETGID_CHILD[] = "setgid-child";
# define PROFILE_LIB "tst-sonamemove-runmod2.so"
#endif
+/* Computed path for LD_DEBUG_OUTPUT. */
+static char *debugoutputpath;
+
+/* Expected file name for erroneous LD_PROFILE output. */
+static char *profilepath;
+
struct envvar_t
{
const char *env;
@@ -56,13 +62,14 @@ static const struct envvar_t filtered_envvars[] =
{ "LD_LIBRARY_PATH", FILTERED_VALUE },
{ "LD_PRELOAD", FILTERED_VALUE },
{ "LD_PROFILE", PROFILE_LIB },
+ { "LD_PROFILE_OUTPUT", "/var/tmp" }, /* Not actually used. */
{ "MALLOC_ARENA_MAX", FILTERED_VALUE },
{ "MALLOC_PERTURB_", FILTERED_VALUE },
{ "MALLOC_TRACE", FILTERED_VALUE },
{ "MALLOC_TRIM_THRESHOLD_", FILTERED_VALUE },
{ "RES_OPTIONS", FILTERED_VALUE },
{ "LD_DEBUG", "all" },
- { "LD_DEBUG_OUTPUT", "/tmp/some-file" },
+ { "LD_DEBUG_OUTPUT", "overwritten" }, /* Not actually used. */
{ "LD_WARN", FILTERED_VALUE },
{ "LD_VERBOSE", FILTERED_VALUE },
{ "LD_BIND_NOW", "0" },
@@ -112,18 +119,12 @@ test_child (void)
}
/* Also check if no profile file was created.
- The parent sets LD_DEBUG_OUTPUT="/tmp/some-file"
- which should be filtered. Then it falls back to "/var/tmp".
Note: LD_PROFILE is not supported for static binaries. */
- {
- char *profilepath = xasprintf ("/var/tmp/%s.profile", PROFILE_LIB);
- if (!access (profilepath, R_OK))
- {
- printf ("FAIL: LD_PROFILE file at %s was created!\n", profilepath);
- ret = 1;
- }
- free (profilepath);
- }
+ if (!access (profilepath, R_OK))
+ {
+ printf ("FAIL: LD_PROFILE file at %s was created!\n", profilepath);
+ ret = 1;
+ }
return ret;
}
@@ -136,6 +137,11 @@ do_test (int argc, char **argv)
if (argc >= 2 && strstr (argv[1], LD_SO) != 0)
FAIL_UNSUPPORTED ("dynamic test requires --enable-hardcoded-path-in-tests");
+ profilepath = xasprintf ("%s/%s.profile",
+ support_objdir_root, PROFILE_LIB);
+ debugoutputpath = xasprintf ("%s/tst-env-setuid-file",
+ support_objdir_root);
+
/* Setgid child process. */
if (argc == 2 && strcmp (argv[1], SETGID_CHILD) == 0)
{
@@ -148,7 +154,6 @@ do_test (int argc, char **argv)
if (ret != 0)
exit (1);
- return 0;
}
else
{
@@ -162,20 +167,25 @@ do_test (int argc, char **argv)
e++)
setenv (e->env, e->value, 1);
+ /* Dynamically computed values. */
+ setenv ("LD_DEBUG_OUTPUT", debugoutputpath, 1);
+ setenv ("LD_PROFILE_OUTPUT", support_objdir_root, 1);
+
/* Ensure that the profile output does not exist from a previous run
(e.g. if test_dir, which defaults to /tmp, is mounted nosuid.)
Note: support_capture_subprogram_self_sgid creates the SGID binary
in test_dir. */
- {
- char *profilepath = xasprintf ("/var/tmp/%s.profile", PROFILE_LIB);
- unlink (profilepath);
- free (profilepath);
- }
+ unlink (profilepath);
support_capture_subprogram_self_sgid (SETGID_CHILD);
- return 0;
+ /* And clean up afterwards if necessary. */
+ unlink (profilepath);
}
+
+ free (profilepath);
+ free (debugoutputpath);
+ return 0;
}
#define TEST_FUNCTION_ARGV do_test
diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h
index f1724efe0f1fea7b..5ce5f090053eb09a 100644
--- a/sysdeps/generic/unsecvars.h
+++ b/sysdeps/generic/unsecvars.h
@@ -17,6 +17,7 @@
"LD_ORIGIN_PATH\0" \
"LD_PRELOAD\0" \
"LD_PROFILE\0" \
+ "LD_PROFILE_OUTPUT\0" \
"LD_SHOW_AUXV\0" \
"LD_VERBOSE\0" \
"LD_WARN\0" \

28
glibc-RHEL-142193-3.patch Normal file
View File

@ -0,0 +1,28 @@
commit 34d98aea6c1eaf7750a992bae55b2bca24898eab
Author: Florian Weimer <fweimer@redhat.com>
Date: Sat Jan 24 10:29:22 2026 +0100
support: Fix memory leaks in support_subprogram, support_subprogram_wait
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/support/support_subprocess.c b/support/support_subprocess.c
index b692a7f8b178502d..ec0c069470a586ac 100644
--- a/support/support_subprocess.c
+++ b/support/support_subprocess.c
@@ -86,6 +86,7 @@ support_subprogram (const char *file, char *const argv[], char *const envp[])
result.pid = xposix_spawn (file, &fa, NULL, argv,
envp == NULL ? environ : envp);
+ posix_spawn_file_actions_destroy (&fa);
xclose (result.stdout_pipe[1]);
xclose (result.stderr_pipe[1]);
@@ -102,6 +103,7 @@ support_subprogram_wait (const char *file, char *const argv[])
struct support_subprocess res = support_subprocess_init ();
res.pid = xposix_spawn (file, &fa, NULL, argv, environ);
+ posix_spawn_file_actions_destroy (&fa);
return support_process_wait (&res);
}

52
glibc-RHEL-142193-4.patch Normal file
View File

@ -0,0 +1,52 @@
commit e8502182f09211663c1583960442eb6ff502a33e
Author: Florian Weimer <fweimer@redhat.com>
Date: Sat Jan 24 10:29:22 2026 +0100
support: Add support_hardcoded_paths_in_test
It indicates whether glibc was configured with
--enable-hardcoded-path-in-tests.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/support/Makefile b/support/Makefile
index aee37b9c82e94499..18051532e628b887 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -250,6 +250,12 @@ CFLAGS-support_paths.c = \
-DCOMPLOCALEDIR_PATH=\"$(complocaledir)\" \
-DSYSCONFDIR_PATH=\"$(sysconfdir)\"
+ifeq ($(build-hardcoded-path-in-tests),yes)
+CFLAGS-support_paths.c += -DHARDCODED_PATHS_IN_TEST=true
+else
+CFLAGS-support_paths.c += -DHARDCODED_PATHS_IN_TEST=false
+endif
+
# Build with exception handling and asynchronous unwind table support.
CFLAGS-.oS += -fexceptions -fasynchronous-unwind-tables
diff --git a/support/support.h b/support/support.h
index 2717e5583add690b..df80c81e4178bc9e 100644
--- a/support/support.h
+++ b/support/support.h
@@ -147,6 +147,9 @@ extern const char support_complocaledir_prefix[];
/* Corresponds to the install's etc/ directory. */
extern const char support_sysconfdir_prefix[];
+/* If true, glibc was configured with --enable-hardcoded-path-in-tests. */
+extern const bool support_hardcoded_paths_in_test;
+
/* Copies the file at the path FROM to TO. If TO does not exist, it
is created. If TO is a regular file, it is truncated before
copying. The file mode is copied, but the permissions are not. */
diff --git a/support/support_paths.c b/support/support_paths.c
index 3cad09abd2244a3d..1ad605b6e2273525 100644
--- a/support/support_paths.c
+++ b/support/support_paths.c
@@ -99,3 +99,5 @@ const char support_sysconfdir_prefix[] = SYSCONFDIR_PATH;
#else
# error please -DSYSCONFDIR_PATH=something in the Makefile
#endif
+
+const bool support_hardcoded_paths_in_test = HARDCODED_PATHS_IN_TEST;

40
glibc-RHEL-142193-5.patch Normal file
View File

@ -0,0 +1,40 @@
commit 458a6a2b935f60a25a136846fe8b7a4723296dda
Author: Florian Weimer <fweimer@redhat.com>
Date: Sat Jan 24 10:29:39 2026 +0100
support: Reinitialize containers if /etc is present
This prevents test failures because configuration file leftovers
unexpectedly change glibc for future tests. Whether this
triggers depends on test execution order.
Adding postclean.req files manually (before this change) appears
too error-prone.
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/support/test-container.c b/support/test-container.c
index a9c9926c21f3a21e..f2853f6a71475420 100644
--- a/support/test-container.c
+++ b/support/test-container.c
@@ -134,7 +134,9 @@ int verbose = 0;
- 'mkdirp': A minimal "mkdir -p FILE" command.
* mytest.root/postclean.req causes fresh rsync (with delete) after
- test if present
+ test if present. If /etc is present, the testroot is cleaned,
+ too. This prevents further tests from using special
+ configurations in /etc from previous tests.
* mytest.root/ldconfig.run causes ldconfig to be issued prior
test execution (to setup the initial ld.so.cache).
@@ -868,7 +870,8 @@ main (int argc, char **argv)
if (strrchr (so_base, '/') != NULL)
strrchr (so_base, '/')[1] = 0;
- if (file_exists (concat (command_root, "/postclean.req", NULL)))
+ if (file_exists (concat (command_root, "/postclean.req", NULL))
+ || file_exists (concat (command_root, "/etc", NULL)))
do_postclean = 1;
if (file_exists (concat (command_root, "/ldconfig.run", NULL)))

506
glibc-RHEL-142193-6.patch Normal file
View File

@ -0,0 +1,506 @@
commit 229f65f5f322609283c7104c80c8af6434dff628
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Feb 2 21:15:48 2026 +0100
support: Add support_spawn_wrap and related functionality
It allows us to write test cases in C that run tests with
dynamic linker wrapping.
The iconv test case was auto-generated. The posix_spawn usage
is mechanical, and the interface it tests is newly added in this
commit, so this should be acceptable.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
diff --git a/support/Makefile b/support/Makefile
index 18051532e628b887..f32652685e3fdc94 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -90,6 +90,7 @@ libsupport-routines = \
support_shared_allocate \
support_small_stack_thread_attribute \
support_socket_so_timestamp_time64 \
+ support_spawn_wrap \
support_stack_alloc \
support_stat_nanoseconds \
support_subprocess \
@@ -256,6 +257,11 @@ else
CFLAGS-support_paths.c += -DHARDCODED_PATHS_IN_TEST=false
endif
+CFLAGS-support_spawn_wrap.c += \
+ '-DRUN_PROGRAM_ENV=$(patsubst %, ELEMENT ("%"), $(run-program-env))' \
+ '-DRTLD_PREFIX=$(patsubst %, ELEMENT ("%"), $(rtld-prefix))' \
+ # CFLAGS-support_spawn_wrap.c
+
# Build with exception handling and asynchronous unwind table support.
CFLAGS-.oS += -fexceptions -fasynchronous-unwind-tables
@@ -344,6 +350,7 @@ tests = \
tst-support_quote_string \
tst-support_readdir \
tst-support_record_failure \
+ tst-support_spawn_wrap \
tst-test_compare \
tst-test_compare_blob \
tst-test_compare_string \
diff --git a/support/subprocess.h b/support/subprocess.h
index 8274a2b22bb10296..3438df28c23fac9e 100644
--- a/support/subprocess.h
+++ b/support/subprocess.h
@@ -52,4 +52,36 @@ int support_process_wait (struct support_subprocess *proc);
then with a SIGKILL. Return the status as for waitpid call. */
int support_process_terminate (struct support_subprocess *proc);
+/* Arguments to pass to posix_spawn and related functions to run a
+ process under the built glibc. This overrides the dynamic linker,
+ its search path, and other search paths, such as for locales. */
+struct support_spawn_wrapped
+{
+ const char *path;
+ char *const *argv;
+ char *const *envp;
+};
+
+enum support_spawn_wrap_flags
+ {
+ /* Always wrap the invocation, even if test binaries are linked
+ with overridden the default paths to point into the build tree
+ (--enable-hardcoded-path-in-tests). Can be used to run
+ non-test binaries. */
+ support_spawn_wrap_force = 1 << 0,
+ };
+
+/* Wrap the invocation for invoking testing. PATH is the program
+ path. If ARGV is null, no arguments are passed. If ENVP is null,
+ environ is used instead. The result must not be modified. It is a
+ deep copy of the inputs. */
+struct support_spawn_wrapped *support_spawn_wrap (const char *path,
+ char *const argv[],
+ char *const envp[],
+ enum
+ support_spawn_wrap_flags);
+
+/* Deallocate the result of support_spawn_wrap. */
+void support_spawn_wrapped_free (struct support_spawn_wrapped *);
+
#endif
diff --git a/support/support_spawn_wrap.c b/support/support_spawn_wrap.c
new file mode 100644
index 0000000000000000..e61e12f486ca6a28
--- /dev/null
+++ b/support/support_spawn_wrap.c
@@ -0,0 +1,171 @@
+/* Wrap a subprocess invocation with an ld.so invocation.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/subprocess.h>
+#include <support/support.h>
+#include <unistd.h>
+
+#define ELEMENT(s) s,
+static const char *const rtld_prefix[] = { RTLD_PREFIX "--argv0" };
+static const char *const run_program_env[] = { RUN_PROGRAM_ENV };
+#undef ELEMENT
+
+/* Return a newly allocated argument vector, with ld.so wrapping per
+ rtld_prefix applied if WRAP is true. */
+static char *const *
+rewrite_argv (const char *path, char *const argv[], bool wrap)
+{
+ char *const substitute[] = { (char *) path, NULL};
+ if (argv == NULL)
+ argv = substitute;
+ TEST_VERIFY (argv[0] != NULL);
+
+ size_t length;
+ for (length = 0; argv[length] != 0; ++length)
+ ;
+ /* Potential wrapping, injected path, and null terminator. */
+ length += array_length (rtld_prefix) + 1 + 1;
+
+ char **result = xcalloc (length, sizeof (result));
+
+ size_t inpos = 0;
+ size_t outpos = 0;
+ if (wrap)
+ {
+ for (size_t i = 0; i < array_length (rtld_prefix); ++i)
+ {
+ TEST_VERIFY (outpos < length);
+ result[outpos++] = xstrdup (rtld_prefix[i]);
+ }
+
+ /* --argv0 argument. */
+ TEST_VERIFY (outpos < length);
+ result[outpos++] = xstrdup (argv[0]);
+ inpos = 1;
+
+ /* Path to program as used by ld.so. */
+ TEST_VERIFY (outpos < length);
+ result[outpos++] = xstrdup (path);
+ }
+
+ for (; argv[inpos] != NULL; ++inpos)
+ {
+ TEST_VERIFY (outpos < length);
+ result[outpos++] = xstrdup (argv[inpos]);
+ }
+
+ TEST_VERIFY (outpos < length);
+ return result;
+
+}
+
+/* Return a newly allocated, rewritten environment, with the settings
+ from run_program_env. */
+static char *const *
+rewrite_env (char *const envp[])
+{
+ if (envp == NULL)
+ envp = environ;
+
+ size_t length;
+ for (length = 0; envp[length] != 0; ++length)
+ ;
+ length += array_length (run_program_env) + 1;
+
+ /* Set to true if an element of run_program_env is copied. This is
+ used to avoid adding it again. */
+ bool copied[array_length (run_program_env)] = { false, };
+
+ char **result = xcalloc (length, sizeof (result));
+ size_t outpos = 0;
+ for (size_t inpos = 0; envp[inpos] != NULL; ++inpos)
+ {
+ const char *to_copy = envp[inpos];
+ /* If there is no assignment operator, this environment string
+ cannot be overridden. */
+ const char *envp_assign = strchr (to_copy, '=');
+ if (envp_assign != NULL)
+ {
+ size_t length_with_assign = envp_assign - to_copy + 1;
+ for (size_t i = 0; i < array_length (run_program_env); ++i)
+ {
+ if (strncmp (to_copy, run_program_env[i], length_with_assign)
+ == 0 && !copied[i])
+ {
+ to_copy = run_program_env[i];
+ copied[i] = true;
+ break;
+ }
+ }
+ }
+ TEST_VERIFY (outpos < length);
+ result[outpos++] = xstrdup (to_copy);
+ }
+
+ for (size_t i = 0; i < array_length (run_program_env); ++i)
+ {
+ TEST_VERIFY (strchr (run_program_env[i], '=') != 0);
+ if (!copied[i])
+ {
+ TEST_VERIFY (outpos < length);
+ result[outpos++] = xstrdup (run_program_env[i]);
+ }
+ }
+
+ TEST_VERIFY (outpos < length);
+ return result;
+}
+
+struct support_spawn_wrapped *
+support_spawn_wrap (const char *path,
+ char *const argv[],
+ char *const envp[],
+ enum support_spawn_wrap_flags flags)
+{
+ if (flags != 0)
+ TEST_COMPARE (flags, support_spawn_wrap_force);
+ bool force = flags & support_spawn_wrap_force;
+ bool wrap = force || !support_hardcoded_paths_in_test;
+
+ struct support_spawn_wrapped *result = xmalloc (sizeof (*result));
+ if (wrap)
+ result->path = xstrdup (support_objdir_elf_ldso);
+ else
+ result->path = xstrdup (path);
+ result->argv = rewrite_argv (path, argv, wrap);
+ result->envp = rewrite_env (envp);
+ return result;
+}
+
+void
+support_spawn_wrapped_free (struct support_spawn_wrapped *wrapped)
+{
+ free ((char *) wrapped->path);
+ for (size_t i = 0; wrapped->argv[i] != NULL; ++i)
+ free (wrapped->argv[i]);
+ free ((char **) wrapped->argv);
+ for (size_t i = 0; wrapped->envp[i] != NULL; ++i)
+ free (wrapped->envp[i]);
+ free ((char **) wrapped->envp);
+ free (wrapped);
+}
diff --git a/support/tst-support_spawn_wrap.c b/support/tst-support_spawn_wrap.c
new file mode 100644
index 0000000000000000..d0bb8a419412c48d
--- /dev/null
+++ b/support/tst-support_spawn_wrap.c
@@ -0,0 +1,235 @@
+/* Tests for support_spawn_wrap.
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <getopt.h>
+#include <spawn.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/subprocess.h>
+#include <support/support.h>
+#include <support/xspawn.h>
+#include <support/xunistd.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+/* Return true if running via an explicit ld.so invocation. */
+static bool
+running_via_ldso (void)
+{
+ char *self = realpath ("/proc/self/exe", NULL);
+ if (self == NULL)
+ FAIL_UNSUPPORTED ("/proc/self/exe not available");
+ char *ldso = realpath (support_objdir_elf_ldso, NULL);
+ TEST_VERIFY_EXIT (ldso != NULL);
+ bool result = strcmp (self, ldso) == 0;
+ free (ldso);
+ free (self);
+ return result;
+}
+
+static void
+test_subprocess (int argc, char **argv)
+{
+ {
+ char *argc_env = getenv ("argc");
+ if (argc_env != NULL)
+ {
+ char *argc_arg = xasprintf ("%d", argc);
+ TEST_COMPARE_STRING (argc_arg, argc_env);
+ free (argc_arg);
+ }
+ }
+
+ if (argc >= 2 && strcmp (argv[1], "alpha") == 0)
+ {
+ TEST_COMPARE (argc, 4);
+ TEST_COMPARE_STRING (argv[0], "program");
+ TEST_COMPARE_STRING (argv[2], "beta");
+ TEST_COMPARE_STRING (argv[3], "gamma");
+ TEST_COMPARE_STRING (argv[4], NULL);
+ }
+ else if (argc >= 2 && strcmp (argv[1], "check-env") == 0)
+ printf ("%d %s\n", argc, getenv ("extra"));
+ else if (argc >= 2 && strcmp (argv[1], "check-ld.so") == 0)
+ TEST_VERIFY (running_via_ldso ());
+}
+
+/* The "recurse" environment variable and the --recurse option
+ indicate recursive invocation. */
+static int flag_recurse;
+#define CMDLINE_OPTIONS \
+ { "recurse", no_argument, &flag_recurse, 1 }, \
+ /* CMDLINE_OPTION */
+
+static void
+prepare (int argc, char **argv)
+{
+ if (getenv ("recurse") != NULL || flag_recurse)
+ {
+ test_subprocess (argc, argv);
+ support_record_failure_barrier ();
+ exit (0);
+ }
+}
+
+#define PREPARE prepare
+
+/* Test wrapping of a non-test program (iconv). This uses posix_spawn
+ directly, mainly for illustrative purposes. */
+void
+test_iconv (void)
+{
+ char *iconv_prog = xasprintf ("%s/iconv/iconv_prog", support_objdir_root);
+ char *argv[] = { (char *) "iconv", (char *) "-f", (char *) "UTF-8",
+ (char *) "-t", (char *) "ISO-8859-1", NULL };
+ struct support_spawn_wrapped *w
+ = support_spawn_wrap (iconv_prog, argv, NULL, support_spawn_wrap_force);
+
+ /* Set up pipes for stdin, stdout, and stderr. */
+ int stdin_pipe[2];
+ xpipe (stdin_pipe);
+ int stdout_pipe[2];
+ xpipe (stdout_pipe);
+ int stderr_pipe[2];
+ xpipe (stderr_pipe);
+
+ posix_spawn_file_actions_t fa;
+ posix_spawn_file_actions_init (&fa);
+ xposix_spawn_file_actions_adddup2 (&fa, stdin_pipe[0], STDIN_FILENO);
+ xposix_spawn_file_actions_addclose (&fa, stdin_pipe[0]);
+ xposix_spawn_file_actions_addclose (&fa, stdin_pipe[1]);
+ xposix_spawn_file_actions_adddup2 (&fa, stdout_pipe[1], STDOUT_FILENO);
+ xposix_spawn_file_actions_addclose (&fa, stdout_pipe[0]);
+ xposix_spawn_file_actions_addclose (&fa, stdout_pipe[1]);
+ xposix_spawn_file_actions_adddup2 (&fa, stderr_pipe[1], STDERR_FILENO);
+ xposix_spawn_file_actions_addclose (&fa, stderr_pipe[0]);
+ xposix_spawn_file_actions_addclose (&fa, stderr_pipe[1]);
+
+ pid_t pid = xposix_spawn (w->path, &fa, NULL, w->argv, w->envp);
+ posix_spawn_file_actions_destroy (&fa);
+
+ xclose (stdin_pipe[0]);
+ xclose (stdout_pipe[1]);
+ xclose (stderr_pipe[1]);
+
+ /* Write UTF-8 encoding of "äöü\n" to stdin. */
+ xwrite (stdin_pipe[1], "\xc3\xa4\xc3\xb6\xc3\xbc\n", 7);
+ xclose (stdin_pipe[1]);
+
+ /* Read the converted output from the pipe. */
+ char buf[16];
+ ssize_t ret = read (stdout_pipe[0], buf, sizeof (buf));
+ xclose (stdout_pipe[0]);
+
+ /* ISO-8859-1 encoding of "äöü\n". */
+ TEST_COMPARE_BLOB (buf, ret, "\xe4\xf6\xfc\n", 4);
+
+ int status;
+ xwaitpid (pid, &status, 0);
+ TEST_COMPARE (status, 0);
+
+ /* Check that nothing has been written to stderr. */
+ ret = read (stderr_pipe[0], buf, sizeof (buf));
+ TEST_COMPARE (ret, 0);
+ xclose (stderr_pipe[0]);
+
+ support_spawn_wrapped_free (w);
+ free (iconv_prog);
+}
+
+static int
+do_test (void)
+{
+ char *program = xasprintf ("%s/support/tst-support_spawn_wrap",
+ support_objdir_root);
+
+ {
+ char *env[] = { (char *) "recurse=", (char *) "argc=1", NULL };
+ struct support_spawn_wrapped *w
+ = support_spawn_wrap (program, NULL, env, 0);
+ struct support_capture_subprocess proc
+ = support_capture_subprogram (w->path, w->argv, w->envp);
+ support_capture_subprocess_check (&proc, "no arguments", 0, sc_allow_none);
+ support_capture_subprocess_free (&proc);
+ support_spawn_wrapped_free (w);
+ }
+
+ {
+ char *argv[] = { (char *) "program", (char *) "--recurse", NULL };
+ struct support_spawn_wrapped *w
+ = support_spawn_wrap (program, argv, NULL, 0);
+ struct support_capture_subprocess proc
+ = support_capture_subprogram (w->path, w->argv, w->envp);
+ support_capture_subprocess_check (&proc, "default envvironment", 0,
+ sc_allow_none);
+ support_capture_subprocess_free (&proc);
+ support_spawn_wrapped_free (w);
+ }
+
+ {
+ char *argv[] = { (char *) "program", (char *) "alpha", (char *) "beta",
+ (char *) "gamma", NULL };
+ char *env[] = { (char *) "recurse=", (char *) "argc=4", NULL };
+ struct support_spawn_wrapped *w
+ = support_spawn_wrap (program, argv, env, 0);
+ struct support_capture_subprocess proc
+ = support_capture_subprogram (w->path, w->argv, w->envp);
+ support_capture_subprocess_check (&proc, "3 arguments", 0, sc_allow_none);
+ support_capture_subprocess_free (&proc);
+ support_spawn_wrapped_free (w);
+ }
+
+ {
+ char *argv[] = { (char *) "program", (char *) "check-env", NULL };
+ char *env[] = { (char *) "recurse=", (char *) "argc=2",
+ (char *) "extra=17", NULL };
+ struct support_spawn_wrapped *w
+ = support_spawn_wrap (program, argv, env, 0);
+ struct support_capture_subprocess proc
+ = support_capture_subprogram (w->path, w->argv, w->envp);
+ TEST_COMPARE_STRING (proc.out.buffer, "2 17\n");
+ support_capture_subprocess_check (&proc, "check-env", 0, sc_allow_stdout);
+ support_capture_subprocess_free (&proc);
+ support_spawn_wrapped_free (w);
+ }
+
+ test_iconv ();
+
+ /* This may trigger EXIT_UNSUPPORTED, so run this before the tests
+ that rely on running_via_ldso. */
+ TEST_COMPARE (!running_via_ldso (), support_hardcoded_paths_in_test);
+
+ {
+ char *argv[] = { (char *) "program", (char *) "check-ld.so", NULL };
+ char *env[] = { (char *) "recurse=", NULL };
+ struct support_spawn_wrapped *w
+ = support_spawn_wrap (program, argv, env, support_spawn_wrap_force);
+ struct support_capture_subprocess proc
+ = support_capture_subprogram (w->path, w->argv, w->envp);
+ support_capture_subprocess_check (&proc, "check-ld.so", 0, sc_allow_none);
+ support_capture_subprocess_free (&proc);
+ support_spawn_wrapped_free (w);
+ }
+
+ free (program);
+ return 0;
+}
+
+#include <support/test-driver.c>

171
glibc-RHEL-142193-7.patch Normal file
View File

@ -0,0 +1,171 @@
commit 364426a59ee30ee3e528e5b5cae36b5dee045320
Author: Florian Weimer <fweimer@redhat.com>
Date: Mon Feb 2 21:15:48 2026 +0100
elf: Add test case for LD_PROFILE/LD_PROFILE_OUTPUT interaction
This verifies that LD_PROFILE is correctly ignored if LD_PROFILE_OUTPUT
is not set.
The test was initially auto-generated, then heavily edited and re-edited
for brevity and clarity. The test uses glibc-specific interfaces
(including one that did not exist at all a couple of hours ago), so
this should be unproblematic.
diff --git a/elf/Makefile b/elf/Makefile
index cf54ea084489b9cf..73c426ae9c1da05c 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -448,6 +448,7 @@ tests += \
tst-initorder \
tst-initorder2 \
tst-latepthread \
+ tst-ld_profile \
tst-main1 \
tst-next-ver \
tst-nodelete-dlclose \
diff --git a/elf/tst-ld_profile.c b/elf/tst-ld_profile.c
new file mode 100644
index 0000000000000000..6c392f682b950b00
--- /dev/null
+++ b/elf/tst-ld_profile.c
@@ -0,0 +1,139 @@
+/* Test LD_PROFILE/LD_PROFILE_OUTPUT interaction (bug 33797).
+ Copyright (C) 2026 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <gnu/lib-names.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/capture_subprocess.h>
+#include <support/check.h>
+#include <support/subprocess.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <unistd.h>
+
+/* Expected profile file path (based on LD_PROFILE_OUTPUT and LIBC_SO). */
+static char *profile_file_path;
+
+/* Path to this test program for recursive invocation. */
+static char *program;
+
+/* LD_PROFILE_OUTPUT environment variable setting. */
+static char *ld_profile_output_env;
+
+/* Run the test program with the specified environment array. */
+static struct support_capture_subprocess
+run_test_program (char *env[])
+{
+ /* Make sure the the potential output file does not exist. */
+ unlink (profile_file_path);
+
+/* Command line arguments for recursive invocation. This turns the
+ test program in a no-op (with LD_PROFILE output the only side effect). */
+ static char *recurse_argv[] =
+ {
+ (char *) "tst-ld_profile", (char *) "recurse", NULL
+ };
+ struct support_spawn_wrapped *w
+ = support_spawn_wrap (program, recurse_argv, env, 0);
+ struct support_capture_subprocess proc
+ = support_capture_subprogram (w->path, w->argv, w->envp);
+ support_spawn_wrapped_free (w);
+
+ return proc;
+}
+
+static void
+test_profile_without_output (void)
+{
+ char *env[] = { (char *) "LD_PROFILE=" LIBC_SO, NULL };
+ struct support_capture_subprocess proc = run_test_program (env);
+ const char *expected_warning =
+ "warning: LD_PROFILE ignored because LD_PROFILE_OUTPUT not specified\n";
+ TEST_COMPARE_STRING (proc.err.buffer, expected_warning);
+ support_capture_subprocess_check (&proc,
+ "LD_PROFILE without LD_PROFILE_OUTPUT",
+ 0, sc_allow_stderr);
+ support_capture_subprocess_free (&proc);
+
+ TEST_VERIFY (access (profile_file_path, F_OK) != 0);
+ /* Also check the old /var/tmp path. */
+ TEST_VERIFY (access ("/var/tmp/" LIBC_SO ".profile", F_OK) != 0);
+}
+
+static void
+test_profile_with_output (void)
+{
+ char *env[] = { (char *) "LD_PROFILE=" LIBC_SO, ld_profile_output_env, NULL };
+ struct support_capture_subprocess proc = run_test_program (env);
+ support_capture_subprocess_check (&proc, "LD_PROFILE with LD_PROFILE_OUTPUT",
+ 0, sc_allow_none);
+ support_capture_subprocess_free (&proc);
+
+ /* This asserts that the file was created. */
+ TEST_COMPARE (unlink (profile_file_path), 0);
+}
+
+static void
+test_output_without_profile (void)
+{
+ char *env[] = { ld_profile_output_env, NULL };
+ struct support_capture_subprocess proc = run_test_program (env);
+ support_capture_subprocess_check (&proc,
+ "LD_PROFILE_OUTPUT without LD_PROFILE",
+ 0, sc_allow_none);
+ support_capture_subprocess_free (&proc);
+
+ TEST_VERIFY (access (profile_file_path, F_OK) != 0);
+}
+
+static void
+prepare (int argc, char **argv)
+{
+ /* Do nothing on recursive invocation. */
+ if (argc >= 2 && strcmp (argv[1], "recurse") == 0)
+ exit (0);
+}
+
+#define PREPARE prepare
+
+static int
+do_test (void)
+{
+ if (access ("/var/tmp/" LIBC_SO ".profile", F_OK) == 0)
+ FAIL_UNSUPPORTED ("/var/tmp/" LIBC_SO ".profile exists");
+
+ /* Temporary directory for the profile output. */
+ char *profile_output_dir = support_create_temp_directory ("tst-ld_profile");
+ profile_file_path = xasprintf ("%s/%s.profile", profile_output_dir, LIBC_SO);
+ ld_profile_output_env = xasprintf ("LD_PROFILE_OUTPUT=%s",
+ profile_output_dir);
+ program = xasprintf ("%s/elf/tst-ld_profile", support_objdir_root);
+
+ test_profile_without_output ();
+ test_profile_with_output ();
+ test_output_without_profile ();
+
+ free (program);
+ free (ld_profile_output_env);
+ free (profile_file_path);
+ free (profile_output_dir);
+
+ return 0;
+}
+
+#include <support/test-driver.c>