[libc] Add program_invocation(_short)_name and tweak err.h functions (#212448) These GNU extensions hold the name of the program as invoked (argv[0]) and its short name (the basename after the last slash). Both variables are initialized in the startup code. As with all of our other variables, they are only available in full build mode. The trickiest part of this patch are the error reporting functions from <err.h>, which access this variable, and they are currently enabled in overlay mode. To make them work, I add an #ifdef to select the right version. I considered doing something more elaborate, like we have with `errno`, but that seemed too heavy for a single occurrence. I also drop the linux check in this function. The documentation says the functions should print the "last component of the program name", which "llvmlibc" is not. If someone wants to enable these functions on non-linux, they can figure out what they want to print here and how. Assisted by Gemini. GitOrigin-RevId: 8f53e523b92ccdc5074acc02c1c103149c20530a
diff --git a/config/linux/aarch64/entrypoints.txt b/config/linux/aarch64/entrypoints.txt index b5b50f3..c2c037c 100644 --- a/config/linux/aarch64/entrypoints.txt +++ b/config/linux/aarch64/entrypoints.txt
@@ -1041,6 +1041,10 @@ libc.src.dirent.readdir libc.src.dirent.fdopendir + # errno.h entrypoints + libc.src.errno.program_invocation_name + libc.src.errno.program_invocation_short_name + # arpa/inet.h entrypoints libc.src.arpa.inet.htonl libc.src.arpa.inet.htons
diff --git a/config/linux/riscv/entrypoints.txt b/config/linux/riscv/entrypoints.txt index 2d67b0d..4e3a383 100644 --- a/config/linux/riscv/entrypoints.txt +++ b/config/linux/riscv/entrypoints.txt
@@ -1223,6 +1223,10 @@ libc.src.ctype.tolower_l libc.src.ctype.toupper_l + # errno.h entrypoints + libc.src.errno.program_invocation_name + libc.src.errno.program_invocation_short_name + # stdlib.h entrypoints libc.src.stdlib.strtod_l libc.src.stdlib.strtof_l
diff --git a/config/linux/x86_64/entrypoints.txt b/config/linux/x86_64/entrypoints.txt index 255197d..5e84814 100644 --- a/config/linux/x86_64/entrypoints.txt +++ b/config/linux/x86_64/entrypoints.txt
@@ -1228,6 +1228,10 @@ libc.src.ctype.tolower_l libc.src.ctype.toupper_l + # errno.h entrypoints + libc.src.errno.program_invocation_name + libc.src.errno.program_invocation_short_name + # stdlib.h entrypoints libc.src.stdlib.strtod_l libc.src.stdlib.strtof_l
diff --git a/include/errno.yaml b/include/errno.yaml index e29d15b..ead1fc4 100644 --- a/include/errno.yaml +++ b/include/errno.yaml
@@ -8,5 +8,9 @@ types: - type_name: errno_t enums: [] -objects: [] +objects: + - object_name: program_invocation_name + object_type: char * + - object_name: program_invocation_short_name + object_type: char * functions: []
diff --git a/src/err/CMakeLists.txt b/src/err/CMakeLists.txt index 8f72d21..a12f006 100644 --- a/src/err/CMakeLists.txt +++ b/src/err/CMakeLists.txt
@@ -2,6 +2,11 @@ return() endif() +set(extra_deps) +if(LLVM_LIBC_FULL_BUILD) + list(APPEND extra_deps libc.src.errno.program_invocation_short_name) +endif() + add_object_library( report SRCS @@ -18,6 +23,7 @@ libc.src.__support.printf_core.writer libc.src.__support.CPP.string_view libc.src.__support.macros.config + ${extra_deps} ) add_entrypoint_object(
diff --git a/src/err/report.cpp b/src/err/report.cpp index 1dd69dc..e3ca01a 100644 --- a/src/err/report.cpp +++ b/src/err/report.cpp
@@ -23,8 +23,13 @@ #include "src/__support/printf_core/printf_main.h" #include "src/__support/printf_core/writer.h" -#ifdef __linux__ +#ifdef LIBC_FULL_BUILD +#include "src/errno/program_invocation_short_name.h" +#define PROGRAM_INVOCATION_SHORT_NAME \ + LIBC_NAMESPACE::program_invocation_short_name +#else extern "C" char *program_invocation_short_name; +#define PROGRAM_INVOCATION_SHORT_NAME ::program_invocation_short_name #endif namespace LIBC_NAMESPACE_DECL { @@ -32,12 +37,9 @@ void report(bool show_err, int err_num, const char *fmt, internal::ArgList &args) { - const char *progname = "libllvmlibc"; - // TODO: Use a proper way to get progname if available. -#ifdef __linux__ - progname = program_invocation_short_name; -#endif - + const char *progname = PROGRAM_INVOCATION_SHORT_NAME; + if (!progname) + progname = ""; char buffer[1024]; printf_core::FlushingBuffer wb( buffer, sizeof(buffer),
diff --git a/src/errno/CMakeLists.txt b/src/errno/CMakeLists.txt index 2852044..95d91dd 100644 --- a/src/errno/CMakeLists.txt +++ b/src/errno/CMakeLists.txt
@@ -14,3 +14,25 @@ libc.src.__support.macros.attributes libc.src.__support.macros.config ) + +add_entrypoint_object( + program_invocation_name + SRCS + program_invocation_name.cpp + HDRS + program_invocation_name.h + DEPENDS + libc.src.__support.common + libc.src.__support.macros.config +) + +add_entrypoint_object( + program_invocation_short_name + SRCS + program_invocation_short_name.cpp + HDRS + program_invocation_short_name.h + DEPENDS + libc.src.__support.common + libc.src.__support.macros.config +)
diff --git a/src/errno/program_invocation_name.cpp b/src/errno/program_invocation_name.cpp new file mode 100644 index 0000000..456c14c --- /dev/null +++ b/src/errno/program_invocation_name.cpp
@@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implementation of program_invocation_name. +/// +//===----------------------------------------------------------------------===// + +#include "src/errno/program_invocation_name.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +// Initialized by startup code. +LLVM_LIBC_VARIABLE(char *, program_invocation_name) = nullptr; + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/errno/program_invocation_name.h b/src/errno/program_invocation_name.h new file mode 100644 index 0000000..9d5f6be --- /dev/null +++ b/src/errno/program_invocation_name.h
@@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implementation header for program_invocation_name. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_ERRNO_PROGRAM_INVOCATION_NAME_H +#define LLVM_LIBC_SRC_ERRNO_PROGRAM_INVOCATION_NAME_H + +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +extern char *program_invocation_name; + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_ERRNO_PROGRAM_INVOCATION_NAME_H
diff --git a/src/errno/program_invocation_short_name.cpp b/src/errno/program_invocation_short_name.cpp new file mode 100644 index 0000000..e09a515 --- /dev/null +++ b/src/errno/program_invocation_short_name.cpp
@@ -0,0 +1,23 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implementation of program_invocation_short_name. +/// +//===----------------------------------------------------------------------===// + +#include "src/errno/program_invocation_short_name.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +// Initialized by startup code. +LLVM_LIBC_VARIABLE(char *, program_invocation_short_name) = nullptr; + +} // namespace LIBC_NAMESPACE_DECL
diff --git a/src/errno/program_invocation_short_name.h b/src/errno/program_invocation_short_name.h new file mode 100644 index 0000000..60bfd9a --- /dev/null +++ b/src/errno/program_invocation_short_name.h
@@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Implementation header for program_invocation_short_name. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_ERRNO_PROGRAM_INVOCATION_SHORT_NAME_H +#define LLVM_LIBC_SRC_ERRNO_PROGRAM_INVOCATION_SHORT_NAME_H + +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { + +extern char *program_invocation_short_name; + +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_ERRNO_PROGRAM_INVOCATION_SHORT_NAME_H
diff --git a/startup/linux/CMakeLists.txt b/startup/linux/CMakeLists.txt index e2c0b5c..e08f30a 100644 --- a/startup/linux/CMakeLists.txt +++ b/startup/linux/CMakeLists.txt
@@ -123,6 +123,8 @@ libc.src.stdlib.exit libc.src.stdlib.atexit libc.src.unistd.environ + libc.src.errno.program_invocation_name + libc.src.errno.program_invocation_short_name COMPILE_OPTIONS -ffreestanding # To avoid compiler warnings about calling the main function. -fno-builtin # avoid emit unexpected calls
diff --git a/startup/linux/do_start.cpp b/startup/linux/do_start.cpp index a545136..f2e8da5 100644 --- a/startup/linux/do_start.cpp +++ b/startup/linux/do_start.cpp
@@ -15,6 +15,8 @@ #include "src/__support/OSUtil/syscall.h" #include "src/__support/macros/config.h" #include "src/__support/threads/thread.h" +#include "src/errno/program_invocation_name.h" +#include "src/errno/program_invocation_short_name.h" #include "src/stdlib/atexit.h" #include "src/stdlib/exit.h" #include "src/unistd/environ.h" @@ -83,6 +85,15 @@ // Initialize the POSIX global declared in unistd.h environ = reinterpret_cast<char **>(env_ptr); + if (app.args->argc > 0 && app.args->argv[0] != 0) { + program_invocation_name = reinterpret_cast<char *>(app.args->argv[0]); + program_invocation_short_name = program_invocation_name; + for (char *p = program_invocation_name; *p != '\0'; ++p) { + if (*p == '/') + program_invocation_short_name = p + 1; + } + } + // After the env array, is the aux-vector. The end of the aux-vector is // denoted by an AT_NULL entry. ElfW(Phdr) *program_hdr_table = nullptr;
diff --git a/test/integration/startup/linux/CMakeLists.txt b/test/integration/startup/linux/CMakeLists.txt index 26ad9aa..ef98af3 100644 --- a/test/integration/startup/linux/CMakeLists.txt +++ b/test/integration/startup/linux/CMakeLists.txt
@@ -19,6 +19,18 @@ ) add_integration_test( + invocation_name_test + SUITE libc-startup-tests + SRCS + invocation_name_test.cpp + DEPENDS + libc.src.__support.CPP.string_view + libc.src.errno.program_invocation_name + libc.src.errno.program_invocation_short_name + libc.src.unistd.execve +) + +add_integration_test( startup_no_envp_test SUITE libc-startup-tests SRCS
diff --git a/test/integration/startup/linux/invocation_name_test.cpp b/test/integration/startup/linux/invocation_name_test.cpp new file mode 100644 index 0000000..d9fbff9 --- /dev/null +++ b/test/integration/startup/linux/invocation_name_test.cpp
@@ -0,0 +1,140 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Integration test for program_invocation_name. +/// +//===----------------------------------------------------------------------===// + +#include "src/__support/CPP/string_view.h" +#include "src/errno/program_invocation_name.h" +#include "src/errno/program_invocation_short_name.h" +#include "src/unistd/execve.h" +#include "test/IntegrationTest/test.h" + +TEST_MAIN(int argc, char **argv, char **envp) { + ASSERT_TRUE(argc >= 1); + ASSERT_TRUE(LIBC_NAMESPACE::program_invocation_name != nullptr); + ASSERT_TRUE(LIBC_NAMESPACE::program_invocation_short_name != nullptr); + ASSERT_TRUE(LIBC_NAMESPACE::program_invocation_name == argv[0]); + + LIBC_NAMESPACE::cpp::string_view arg1 = argc > 1 ? argv[1] : ""; + + if (arg1 == "reexec1") { + // Step 1: Executed with an absolute path containing slashes. + ASSERT_STREQ(LIBC_NAMESPACE::program_invocation_name, + "/known/path/to/invocation_name"); + ASSERT_STREQ(LIBC_NAMESPACE::program_invocation_short_name, + "invocation_name"); + + ASSERT_EQ(argc, 3); + const char *self_path = argv[2]; + char *const child_argv[] = { + const_cast<char *>("./relative/path/to/invocation_name"), + const_cast<char *>("reexec2"), + const_cast<char *>(self_path), + nullptr, + }; + LIBC_NAMESPACE::execve(self_path, child_argv, envp); + ASSERT_TRUE(false); + } + + if (arg1 == "reexec2") { + // Step 2: Executed with a relative path containing slashes. + ASSERT_STREQ(LIBC_NAMESPACE::program_invocation_name, + "./relative/path/to/invocation_name"); + ASSERT_STREQ(LIBC_NAMESPACE::program_invocation_short_name, + "invocation_name"); + + ASSERT_EQ(argc, 3); + const char *self_path = argv[2]; + char *const child_argv[] = { + const_cast<char *>(""), + const_cast<char *>("reexec3"), + const_cast<char *>(self_path), + nullptr, + }; + LIBC_NAMESPACE::execve(self_path, child_argv, envp); + ASSERT_TRUE(false); + } + + if (arg1 == "reexec3") { + // Step 3: Executed with an empty string. + ASSERT_TRUE(LIBC_NAMESPACE::program_invocation_name == + LIBC_NAMESPACE::program_invocation_short_name); + ASSERT_STREQ(LIBC_NAMESPACE::program_invocation_name, ""); + ASSERT_STREQ(LIBC_NAMESPACE::program_invocation_short_name, ""); + + ASSERT_EQ(argc, 3); + const char *self_path = argv[2]; + char *const child_argv[] = { + const_cast<char *>("/known/path/to/dir/"), + const_cast<char *>("reexec4"), + const_cast<char *>(self_path), + nullptr, + }; + LIBC_NAMESPACE::execve(self_path, child_argv, envp); + ASSERT_TRUE(false); + } + + if (arg1 == "reexec4") { + // Step 4: Executed with a path ending in a slash. + ASSERT_STREQ(LIBC_NAMESPACE::program_invocation_name, + "/known/path/to/dir/"); + ASSERT_STREQ(LIBC_NAMESPACE::program_invocation_short_name, ""); + + ASSERT_EQ(argc, 3); + const char *self_path = argv[2]; + char *const child_argv[] = { + const_cast<char *>("invocation_name_no_slash"), + const_cast<char *>("reexec5"), + const_cast<char *>(self_path), + nullptr, + }; + LIBC_NAMESPACE::execve(self_path, child_argv, envp); + ASSERT_TRUE(false); + } + + if (arg1 == "reexec5") { + // Step 5: Executed with a path without slashes. + ASSERT_TRUE(LIBC_NAMESPACE::program_invocation_name == + LIBC_NAMESPACE::program_invocation_short_name); + ASSERT_STREQ(LIBC_NAMESPACE::program_invocation_name, + "invocation_name_no_slash"); + ASSERT_STREQ(LIBC_NAMESPACE::program_invocation_short_name, + "invocation_name_no_slash"); + + ASSERT_EQ(argc, 3); + const char *self_path = argv[2]; + char *const child_argv[] = { + nullptr, + }; + LIBC_NAMESPACE::execve(self_path, child_argv, envp); + ASSERT_TRUE(false); + } + + if (argc == 1 && LIBC_NAMESPACE::cpp::string_view(argv[0]) == "") { + // Step 6: Executed with an empty (zero-length) argv. The kernel adds an + // empty string for argv[0]. + ASSERT_STREQ(LIBC_NAMESPACE::program_invocation_name, ""); + ASSERT_STREQ(LIBC_NAMESPACE::program_invocation_short_name, ""); + return 0; + } + + // Step 0: Initial run. Re-exec self with known argv[0] values. + const char *self_path = argv[0]; + char *const child_argv[] = { + const_cast<char *>("/known/path/to/invocation_name"), + const_cast<char *>("reexec1"), + const_cast<char *>(self_path), + nullptr, + }; + LIBC_NAMESPACE::execve(self_path, child_argv, envp); + ASSERT_TRUE(false); + return 1; +}
diff --git a/test/src/err/CMakeLists.txt b/test/src/err/CMakeLists.txt index f1e1fad..322e538 100644 --- a/test/src/err/CMakeLists.txt +++ b/test/src/err/CMakeLists.txt
@@ -6,7 +6,6 @@ add_libc_test( err_test - UNIT_TEST_ONLY SUITE libc_err_unittests SRCS @@ -18,7 +17,6 @@ add_libc_test( errx_test - UNIT_TEST_ONLY SUITE libc_err_unittests SRCS @@ -29,7 +27,6 @@ add_libc_test( verr_test - UNIT_TEST_ONLY SUITE libc_err_unittests SRCS @@ -41,7 +38,6 @@ add_libc_test( verrx_test - UNIT_TEST_ONLY SUITE libc_err_unittests SRCS @@ -52,7 +48,6 @@ add_libc_test( warn_test - UNIT_TEST_ONLY SUITE libc_err_unittests SRCS @@ -64,7 +59,6 @@ add_libc_test( warnx_test - UNIT_TEST_ONLY SUITE libc_err_unittests SRCS @@ -76,7 +70,6 @@ add_libc_test( vwarn_test - UNIT_TEST_ONLY SUITE libc_err_unittests SRCS @@ -88,7 +81,6 @@ add_libc_test( vwarnx_test - UNIT_TEST_ONLY SUITE libc_err_unittests SRCS