tools/nolibc: add support for program_invocation_{,short_}name

Add support for the GNU extensions 'program_invocation_name' and
'program_invocation_short_name'. These are useful to print error
messages, which by convention include the program name.

As these are global variables which take up memory even if not used,
similar to 'errno', gate them behind NOLIBC_IGNORE_ERRNO.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
Acked-by: Willy Tarreau <w@1wt.eu>
Link: https://patch.msgid.link/20260318-nolibc-err-h-v4-1-08247a694bd9@weissschuh.net
This commit is contained in:
Thomas Weißschuh 2026-03-18 16:50:16 +01:00
parent 5662ec000d
commit b74be92274
3 changed files with 64 additions and 0 deletions

View File

@ -17,6 +17,7 @@ const unsigned long *_auxv __attribute__((weak));
void _start(void);
static void __stack_chk_init(void);
static void exit(int);
static char *strrchr(const char *s, int c);
extern void (*const __preinit_array_start[])(int, char **, char**) __attribute__((weak));
extern void (*const __preinit_array_end[])(int, char **, char**) __attribute__((weak));
@ -27,6 +28,24 @@ extern void (*const __init_array_end[])(int, char **, char**) __attribute__((wea
extern void (*const __fini_array_start[])(void) __attribute__((weak));
extern void (*const __fini_array_end[])(void) __attribute__((weak));
#ifndef NOLIBC_IGNORE_ERRNO
extern char *program_invocation_name __attribute__((weak));
extern char *program_invocation_short_name __attribute__((weak));
static __inline__
char *__nolibc_program_invocation_short_name(char *long_name)
{
char *short_name;
short_name = strrchr(long_name, '/');
if (!short_name || !short_name[0])
return long_name;
return short_name + 1;
}
#endif /* NOLIBC_IGNORE_ERRNO */
void _start_c(long *sp);
__attribute__((weak,used))
#if __nolibc_has_feature(undefined_behavior_sanitizer)
@ -76,6 +95,13 @@ void _start_c(long *sp)
;
_auxv = auxv;
#ifndef NOLIBC_IGNORE_ERRNO
if (argc > 0 && argv[0]) {
program_invocation_name = argv[0];
program_invocation_short_name = __nolibc_program_invocation_short_name(argv[0]);
}
#endif /* NOLIBC_IGNORE_ERRNO */
for (ctor_func = __preinit_array_start; ctor_func < __preinit_array_end; ctor_func++)
(*ctor_func)(argc, argv, envp);
for (ctor_func = __init_array_start; ctor_func < __init_array_end; ctor_func++)

View File

@ -15,8 +15,12 @@
#ifndef NOLIBC_IGNORE_ERRNO
#define SET_ERRNO(v) do { errno = (v); } while (0)
int errno __attribute__((weak));
char *program_invocation_name __attribute__((weak)) = "";
char *program_invocation_short_name __attribute__((weak)) = "";
#else
#define SET_ERRNO(v) do { } while (0)
#define program_invocation_name ""
#define program_invocation_short_name ""
#endif

View File

@ -42,6 +42,7 @@
#include <unistd.h>
#include <limits.h>
#include <ctype.h>
#include <stdbool.h>
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
@ -710,6 +711,37 @@ static void constructor2(int argc, char **argv, char **envp)
constructor_test_value |= 1 << 1;
}
int test_program_invocation_name(void)
{
char buf[100];
char *dirsep;
ssize_t r;
int fd;
fd = open("/proc/self/cmdline", O_RDONLY);
if (fd == -1)
return 1;
r = read(fd, buf, sizeof(buf));
close(fd);
if (r < 1 || r == sizeof(buf))
return 1;
buf[r - 1] = '\0';
if (strcmp(program_invocation_name, buf) != 0)
return 1;
dirsep = strrchr(buf, '/');
if (!dirsep || dirsep[1] == '\0')
return 1;
if (strcmp(program_invocation_short_name, dirsep + 1) != 0)
return 1;
return 0;
}
int run_startup(int min, int max)
{
int test;
@ -724,6 +756,7 @@ int run_startup(int min, int max)
#ifdef NOLIBC
test_auxv = _auxv;
#endif
bool proc = access("/proc", R_OK) == 0;
for (test = min; test >= 0 && test <= max; test++) {
int llen = 0; /* line length */
@ -749,6 +782,7 @@ int run_startup(int min, int max)
CASE_TEST(constructor); EXPECT_EQ(is_nolibc, constructor_test_value, 0x3); break;
CASE_TEST(linkage_errno); EXPECT_PTREQ(1, linkage_test_errno_addr(), &errno); break;
CASE_TEST(linkage_constr); EXPECT_EQ(1, linkage_test_constructor_test_value, 0x3); break;
CASE_TEST(prog_name); EXPECT_ZR(proc, test_program_invocation_name()); break;
case __LINE__:
return ret; /* must be last */
/* note: do not set any defaults so as to permit holes above */