* [PATCH v2 0/2] tools/nolibc: add err.h
@ 2026-03-13 20:26 Thomas Weißschuh
2026-03-13 20:26 ` [PATCH v2 1/2] tools/nolibc: add support for program_invocation_{,short_}name Thomas Weißschuh
2026-03-13 20:26 ` [PATCH v2 2/2] tools/nolibc: add err.h Thomas Weißschuh
0 siblings, 2 replies; 4+ messages in thread
From: Thomas Weißschuh @ 2026-03-13 20:26 UTC (permalink / raw)
To: Willy Tarreau, Shuah Khan
Cc: linux-kselftest, linux-kernel, Thomas Weißschuh
Add a few convenient helpers to print error and warning messages.
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
Changes in v2:
- Fix program_invocation_name test to handle test execution as /init
- Drop already applied patches
- Link to v1: https://patch.msgid.link/20260311-nolibc-err-h-v1-0-735a9de7f15d@weissschuh.net
---
Thomas Weißschuh (2):
tools/nolibc: add support for program_invocation_{,short_}name
tools/nolibc: add err.h
tools/include/nolibc/Makefile | 1 +
tools/include/nolibc/crt.h | 26 ++++++++
tools/include/nolibc/err.h | 91 ++++++++++++++++++++++++++++
tools/include/nolibc/errno.h | 2 +
tools/include/nolibc/nolibc.h | 1 +
tools/testing/selftests/nolibc/nolibc-test.c | 33 ++++++++++
6 files changed, 154 insertions(+)
---
base-commit: 745dadd6d3d984feafb96f27b60d74b29ade4957
change-id: 20260311-nolibc-err-h-df36a75dddf8
Best regards,
--
Thomas Weißschuh <linux@weissschuh.net>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v2 1/2] tools/nolibc: add support for program_invocation_{,short_}name
2026-03-13 20:26 [PATCH v2 0/2] tools/nolibc: add err.h Thomas Weißschuh
@ 2026-03-13 20:26 ` Thomas Weißschuh
2026-03-14 4:59 ` Willy Tarreau
2026-03-13 20:26 ` [PATCH v2 2/2] tools/nolibc: add err.h Thomas Weißschuh
1 sibling, 1 reply; 4+ messages in thread
From: Thomas Weißschuh @ 2026-03-13 20:26 UTC (permalink / raw)
To: Willy Tarreau, Shuah Khan
Cc: linux-kselftest, linux-kernel, Thomas Weißschuh
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>
---
tools/include/nolibc/crt.h | 26 ++++++++++++++++++++++
tools/include/nolibc/errno.h | 2 ++
tools/testing/selftests/nolibc/nolibc-test.c | 33 ++++++++++++++++++++++++++++
3 files changed, 61 insertions(+)
diff --git a/tools/include/nolibc/crt.h b/tools/include/nolibc/crt.h
index d9262998dae9..842f86e41f2f 100644
--- a/tools/include/nolibc/crt.h
+++ b/tools/include/nolibc/crt.h
@@ -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));
+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;
+
+ if (!long_name)
+ return NULL;
+
+ short_name = strrchr(long_name, '/');
+ if (!short_name || !short_name[0])
+ return NULL;
+
+ return short_name + 1;
+}
+
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) {
+ 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++)
diff --git a/tools/include/nolibc/errno.h b/tools/include/nolibc/errno.h
index 08a33c40ec0c..64d9f9bc6539 100644
--- a/tools/include/nolibc/errno.h
+++ b/tools/include/nolibc/errno.h
@@ -15,6 +15,8 @@
#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)
#endif
diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c
index 180611aabbfb..07af23833879 100644
--- a/tools/testing/selftests/nolibc/nolibc-test.c
+++ b/tools/testing/selftests/nolibc/nolibc-test.c
@@ -702,6 +702,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;
@@ -716,6 +747,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 */
@@ -741,6 +773,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 */
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v2 2/2] tools/nolibc: add err.h
2026-03-13 20:26 [PATCH v2 0/2] tools/nolibc: add err.h Thomas Weißschuh
2026-03-13 20:26 ` [PATCH v2 1/2] tools/nolibc: add support for program_invocation_{,short_}name Thomas Weißschuh
@ 2026-03-13 20:26 ` Thomas Weißschuh
1 sibling, 0 replies; 4+ messages in thread
From: Thomas Weißschuh @ 2026-03-13 20:26 UTC (permalink / raw)
To: Willy Tarreau, Shuah Khan
Cc: linux-kselftest, linux-kernel, Thomas Weißschuh
Add a few convenient helpers to print error and warning messages.
Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
Acked-by: Willy Tarreau <w@1wt.eu>
---
tools/include/nolibc/Makefile | 1 +
tools/include/nolibc/err.h | 91 +++++++++++++++++++++++++++++++++++++++++++
tools/include/nolibc/nolibc.h | 1 +
3 files changed, 93 insertions(+)
diff --git a/tools/include/nolibc/Makefile b/tools/include/nolibc/Makefile
index 1958dda98895..5d242312943f 100644
--- a/tools/include/nolibc/Makefile
+++ b/tools/include/nolibc/Makefile
@@ -30,6 +30,7 @@ all_files := \
ctype.h \
dirent.h \
elf.h \
+ err.h \
errno.h \
fcntl.h \
getopt.h \
diff --git a/tools/include/nolibc/err.h b/tools/include/nolibc/err.h
new file mode 100644
index 000000000000..2373c4999b71
--- /dev/null
+++ b/tools/include/nolibc/err.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: LGPL-2.1 OR MIT */
+/*
+ * formatted error message for NOLIBC
+ * Copyright (C) 2026 Thomas Weißschuh <linux@weissschuh.net>
+ */
+
+/* make sure to include all global symbols */
+#include "nolibc.h"
+
+#ifndef _NOLIBC_ERR_H
+#define _NOLIBC_ERR_H
+
+#include "errno.h"
+#include "stdarg.h"
+#include "sys.h"
+
+static __attribute__((unused))
+void vwarn(const char *fmt, va_list args)
+{
+#ifndef NOLIBC_IGNORE_ERRNO
+ fprintf(stderr, "%s: ", program_invocation_short_name);
+#endif
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, ": %m\n");
+}
+
+static __attribute__((unused))
+void vwarnx(const char *fmt, va_list args)
+{
+#ifndef NOLIBC_IGNORE_ERRNO
+ fprintf(stderr, "%s: ", program_invocation_short_name);
+#endif
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n");
+}
+
+static __attribute__((unused))
+void warn(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vwarn(fmt, args);
+ va_end(args);
+}
+
+static __attribute__((unused))
+void warnx(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vwarnx(fmt, args);
+ va_end(args);
+}
+
+static __attribute__((noreturn, unused))
+void verr(int eval, const char *fmt, va_list args)
+{
+ vwarn(fmt, args);
+ exit(eval);
+}
+
+static __attribute__((noreturn, unused))
+void verrx(int eval, const char *fmt, va_list args)
+{
+ warnx(fmt, args);
+ exit(eval);
+}
+
+static __attribute__((noreturn, unused))
+void err(int eval, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ verr(eval, fmt, args);
+ va_end(args);
+}
+
+static __attribute__((noreturn, unused))
+void errx(int eval, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ verrx(eval, fmt, args);
+ va_end(args);
+}
+
+#endif /* _NOLIBC_ERR_H */
diff --git a/tools/include/nolibc/nolibc.h b/tools/include/nolibc/nolibc.h
index d1d08b7f8599..fe8195483b6d 100644
--- a/tools/include/nolibc/nolibc.h
+++ b/tools/include/nolibc/nolibc.h
@@ -130,6 +130,7 @@
#include "getopt.h"
#include "poll.h"
#include "math.h"
+#include "err.h"
/* Used by programs to avoid std includes */
#define NOLIBC
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2 1/2] tools/nolibc: add support for program_invocation_{,short_}name
2026-03-13 20:26 ` [PATCH v2 1/2] tools/nolibc: add support for program_invocation_{,short_}name Thomas Weißschuh
@ 2026-03-14 4:59 ` Willy Tarreau
0 siblings, 0 replies; 4+ messages in thread
From: Willy Tarreau @ 2026-03-14 4:59 UTC (permalink / raw)
To: Thomas Weißschuh; +Cc: Shuah Khan, linux-kselftest, linux-kernel
Hi Thomas,
On Fri, Mar 13, 2026 at 09:26:28PM +0100, Thomas Weißschuh wrote:
> 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>
> ---
> tools/include/nolibc/crt.h | 26 ++++++++++++++++++++++
> tools/include/nolibc/errno.h | 2 ++
> tools/testing/selftests/nolibc/nolibc-test.c | 33 ++++++++++++++++++++++++++++
> 3 files changed, 61 insertions(+)
>
> diff --git a/tools/include/nolibc/crt.h b/tools/include/nolibc/crt.h
> index d9262998dae9..842f86e41f2f 100644
> --- a/tools/include/nolibc/crt.h
> +++ b/tools/include/nolibc/crt.h
> @@ -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));
>
> +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;
> +
> + if (!long_name)
> + return NULL;
> +
> + short_name = strrchr(long_name, '/');
> + if (!short_name || !short_name[0])
> + return NULL;
Here it should return long_name, not NULL, since you want a valid name
to use later. I'm seeing it passed to strcmp() for example.
Also because of this, I'm wondering for the first test about !long_name.
Either we consider that it's not possible to have a NULL long_name and
we don't need to test for it, or we consider it is valid, and we should
return a non-null string (e.g. "") so that the rest of the program
survives it. Both approaches are fine to me, but IMHO testing for NULL
to return a NULL that will crash the program later instead of just now
is not very useful. Maybe returning the empty string is still slightly
better because it allows well-designed programs that check their argc
before using argv[0] to survive.
Cheers,
Willy
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-03-14 4:59 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-13 20:26 [PATCH v2 0/2] tools/nolibc: add err.h Thomas Weißschuh
2026-03-13 20:26 ` [PATCH v2 1/2] tools/nolibc: add support for program_invocation_{,short_}name Thomas Weißschuh
2026-03-14 4:59 ` Willy Tarreau
2026-03-13 20:26 ` [PATCH v2 2/2] tools/nolibc: add err.h Thomas Weißschuh
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox