/* 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 . */ #include #include #include #include #include #include #include #include #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); }