* [PATCH] libbpf: fix helper macro C++ compatibility
@ 2026-10-24 3:59 Eduard Zateev
2026-10-24 4:36 ` bot+bpf-ci
2026-10-24 19:16 ` Alexei Starovoitov
0 siblings, 2 replies; 3+ messages in thread
From: Eduard Zateev @ 2026-10-24 3:59 UTC (permalink / raw)
To: bpf
Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, linux-kernel,
Eduard Zateev
C++26 warns about the GNU named variadic parameter syntax in
bpf_helpers.h, which prevents consumers from using the header with
-Werror=variadic-macros. Replace the named parameters with __VA_ARGS__.
C++ also rejects implicit conversions used by helper argument packing,
bpf_for_each, and generated helper function pointers. Use explicit
integer and typed pointer casts so these helpers can be parsed and used
from C++.
Verified with GCC and Clang C++26 syntax checks using helper macros,
an ARM64-targeted BPF C smoke build, and generated helper definitions
from scripts/bpf_doc.py. checkpatch.pl reports no errors or warnings.
AI assistance: OpenAI Codex helped reproduce the C++26 failures and
draft changes to tools/lib/bpf/bpf_helpers.h and scripts/bpf_doc.py.
The human submitter must review the changes and test results before
submission.
Fixes: d6a6a55518c1 ("libbpf: Move BPF_SEQ_PRINTF and BPF_SNPRINTF to bpf_helpers.h")
Closes: https://github.com/libbpf/libbpf/issues/974
Signed-off-by: Eduard Zateev <hackerowskiy@gmail.com>
---
scripts/bpf_doc.py | 10 +++-
tools/lib/bpf/bpf_helpers.h | 107 ++++++++++++++++++++++--------------
2 files changed, 72 insertions(+), 45 deletions(-)
diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py
index 15d113a1b..02e31ad50 100755
--- a/scripts/bpf_doc.py
+++ b/scripts/bpf_doc.py
@@ -829,6 +829,11 @@ class PrinterHelpersHeader(Printer):
/* Forward declarations of BPF structs */'''
print(header)
+ print("#ifdef __cplusplus")
+ print("#define ___bpf_helper_stub(id, name) reinterpret_cast<decltype(+name)>(id)")
+ print("#else")
+ print("#define ___bpf_helper_stub(id, name) ((void *)(id))")
+ print("#endif")
for fwd in self.type_fwds:
print('%s;' % fwd)
print('')
@@ -849,8 +854,7 @@ class PrinterHelpersHeader(Printer):
print('')
def print_footer(self):
- footer = ''
- print(footer)
+ print("#undef ___bpf_helper_stub")
def map_type(self, t):
if t in self.known_types:
@@ -908,7 +912,7 @@ class PrinterHelpersHeader(Printer):
comma = ', '
print(one_arg, end='')
- print(') = (void *) %d;' % helper.enum_val)
+ print(') = ___bpf_helper_stub(%d, %s);' % (helper.enum_val, proto['name']))
print('')
diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h
index b37b727ef..6c9fdb696 100644
--- a/tools/lib/bpf/bpf_helpers.h
+++ b/tools/lib/bpf/bpf_helpers.h
@@ -233,20 +233,53 @@ enum libbpf_tristate {
#endif
#define ___bpf_fill0(arr, p, x) do {} while (0)
-#define ___bpf_fill1(arr, p, x) arr[p] = x
-#define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args)
-#define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args)
-#define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args)
-#define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args)
-#define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args)
-#define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args)
-#define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args)
-#define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args)
-#define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args)
-#define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args)
-#define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args)
-#define ___bpf_fill(arr, args...) \
- ___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args)
+#define ___bpf_fill1(arr, p, x) (arr[p] = (unsigned long long)(x))
+#define ___bpf_fill2(arr, p, x, ...) do { \
+ arr[p] = (unsigned long long)(x); \
+ ___bpf_fill1(arr, p + 1, __VA_ARGS__); \
+} while (0)
+#define ___bpf_fill3(arr, p, x, ...) do { \
+ arr[p] = (unsigned long long)(x); \
+ ___bpf_fill2(arr, p + 1, __VA_ARGS__); \
+} while (0)
+#define ___bpf_fill4(arr, p, x, ...) do { \
+ arr[p] = (unsigned long long)(x); \
+ ___bpf_fill3(arr, p + 1, __VA_ARGS__); \
+} while (0)
+#define ___bpf_fill5(arr, p, x, ...) do { \
+ arr[p] = (unsigned long long)(x); \
+ ___bpf_fill4(arr, p + 1, __VA_ARGS__); \
+} while (0)
+#define ___bpf_fill6(arr, p, x, ...) do { \
+ arr[p] = (unsigned long long)(x); \
+ ___bpf_fill5(arr, p + 1, __VA_ARGS__); \
+} while (0)
+#define ___bpf_fill7(arr, p, x, ...) do { \
+ arr[p] = (unsigned long long)(x); \
+ ___bpf_fill6(arr, p + 1, __VA_ARGS__); \
+} while (0)
+#define ___bpf_fill8(arr, p, x, ...) do { \
+ arr[p] = (unsigned long long)(x); \
+ ___bpf_fill7(arr, p + 1, __VA_ARGS__); \
+} while (0)
+#define ___bpf_fill9(arr, p, x, ...) do { \
+ arr[p] = (unsigned long long)(x); \
+ ___bpf_fill8(arr, p + 1, __VA_ARGS__); \
+} while (0)
+#define ___bpf_fill10(arr, p, x, ...) do { \
+ arr[p] = (unsigned long long)(x); \
+ ___bpf_fill9(arr, p + 1, __VA_ARGS__); \
+} while (0)
+#define ___bpf_fill11(arr, p, x, ...) do { \
+ arr[p] = (unsigned long long)(x); \
+ ___bpf_fill10(arr, p + 1, __VA_ARGS__); \
+} while (0)
+#define ___bpf_fill12(arr, p, x, ...) do { \
+ arr[p] = (unsigned long long)(x); \
+ ___bpf_fill11(arr, p + 1, __VA_ARGS__); \
+} while (0)
+#define ___bpf_fill(arr, ...) \
+ ___bpf_apply(___bpf_fill, ___bpf_narg(__VA_ARGS__))(arr, 0, __VA_ARGS__)
/*
* Format strings are in a section of their own. When global data of the object
@@ -260,15 +293,12 @@ enum libbpf_tristate {
* BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values
* in a structure.
*/
-#define BPF_SEQ_PRINTF(seq, fmt, args...) \
+#define BPF_SEQ_PRINTF(seq, fmt, ...) \
({ \
static const char ___fmt[] ___bpf_fmt_sec = fmt; \
- unsigned long long ___param[___bpf_narg(args)]; \
+ unsigned long long ___param[___bpf_narg(__VA_ARGS__)]; \
\
- _Pragma("GCC diagnostic push") \
- _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
- ___bpf_fill(___param, args); \
- _Pragma("GCC diagnostic pop") \
+ ___bpf_fill(___param, __VA_ARGS__); \
\
bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \
___param, sizeof(___param)); \
@@ -278,15 +308,12 @@ enum libbpf_tristate {
* BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of
* an array of u64.
*/
-#define BPF_SNPRINTF(out, out_size, fmt, args...) \
+#define BPF_SNPRINTF(out, out_size, fmt, ...) \
({ \
static const char ___fmt[] ___bpf_fmt_sec = fmt; \
- unsigned long long ___param[___bpf_narg(args)]; \
+ unsigned long long ___param[___bpf_narg(__VA_ARGS__)]; \
\
- _Pragma("GCC diagnostic push") \
- _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
- ___bpf_fill(___param, args); \
- _Pragma("GCC diagnostic pop") \
+ ___bpf_fill(___param, __VA_ARGS__); \
\
bpf_snprintf(out, out_size, ___fmt, \
___param, sizeof(___param)); \
@@ -309,29 +336,23 @@ enum libbpf_tristate {
* __bpf_vprintk wraps the bpf_trace_vprintk helper with variadic arguments
* instead of an array of u64.
*/
-#define __bpf_vprintk(fmt, args...) \
+#define __bpf_vprintk(fmt, ...) \
({ \
static const char ___fmt[] ___bpf_fmt_sec = fmt; \
- unsigned long long ___param[___bpf_narg(args)]; \
+ unsigned long long ___param[___bpf_narg(__VA_ARGS__)]; \
\
- _Pragma("GCC diagnostic push") \
- _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
- ___bpf_fill(___param, args); \
- _Pragma("GCC diagnostic pop") \
+ ___bpf_fill(___param, __VA_ARGS__); \
\
bpf_trace_vprintk(___fmt, sizeof(___fmt), \
___param, sizeof(___param)); \
})
-#define bpf_stream_printk(stream_id, fmt, args...) \
+#define bpf_stream_printk(stream_id, fmt, ...) \
({ \
static const char ___fmt[] ___bpf_fmt_sec = fmt; \
- unsigned long long ___param[___bpf_narg(args)]; \
+ unsigned long long ___param[___bpf_narg(__VA_ARGS__)]; \
\
- _Pragma("GCC diagnostic push") \
- _Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
- ___bpf_fill(___param, args); \
- _Pragma("GCC diagnostic pop") \
+ ___bpf_fill(___param, __VA_ARGS__); \
\
bpf_stream_vprintk(stream_id, ___fmt, ___param, sizeof(___param)); \
})
@@ -346,7 +367,7 @@ enum libbpf_tristate {
__bpf_printk /*1*/, __bpf_printk /*0*/)
/* Helper macro to print out debug messages */
-#define bpf_printk(fmt, args...) ___bpf_pick_printk(args)(fmt, ##args)
+#define bpf_printk(fmt, ...) ___bpf_pick_printk(__VA_ARGS__)(fmt, ##__VA_ARGS__)
struct bpf_iter_num;
@@ -382,16 +403,18 @@ extern void bpf_iter_num_destroy(struct bpf_iter_num *it) __weak __ksym;
* extension: __attribute__((cleanup(<func>))), supported by both GCC and
* Clang.
*/
-#define bpf_for_each(type, cur, args...) for ( \
+#define bpf_for_each(type, cur, ...) for ( \
/* initialize and define destructor */ \
struct bpf_iter_##type ___it __attribute__((aligned(8), /* enforce, just in case */, \
cleanup(bpf_iter_##type##_destroy))), \
/* ___p pointer is just to call bpf_iter_##type##_new() *once* to init ___it */ \
*___p __attribute__((unused)) = ( \
- bpf_iter_##type##_new(&___it, ##args), \
+ bpf_iter_##type##_new(&___it, \
+ ##__VA_ARGS__), \
/* this is a workaround for Clang bug: it currently doesn't emit BTF */ \
/* for bpf_iter_##type##_destroy() when used from cleanup() attribute */ \
- (void)bpf_iter_##type##_destroy, (void *)0); \
+ (void)bpf_iter_##type##_destroy, \
+ (struct bpf_iter_##type *)0); \
/* iteration and termination check */ \
(((cur) = bpf_iter_##type##_next(&___it))); \
)
--
2.55.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] libbpf: fix helper macro C++ compatibility
2026-10-24 3:59 [PATCH] libbpf: fix helper macro C++ compatibility Eduard Zateev
@ 2026-10-24 4:36 ` bot+bpf-ci
2026-10-24 19:16 ` Alexei Starovoitov
1 sibling, 0 replies; 3+ messages in thread
From: bot+bpf-ci @ 2026-10-24 4:36 UTC (permalink / raw)
To: hackerowskiy, bpf
Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, linux-kernel,
hackerowskiy, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 6125 bytes --]
> libbpf: fix helper macro C++ compatibility
TL;DR:
- bpf_for() and bpf_repeat() may still fail to compile in C++ (void *
initializer left unchanged)
- Fixes: tag looks inaccurate and would pull this into stable trees
- Nit: generated-header comment misplaced
> libbpf: fix helper macro C++ compatibility
>
> C++26 warns about the GNU named variadic parameter syntax in
> bpf_helpers.h, which prevents consumers from using the header with
> -Werror=variadic-macros. Replace the named parameters with __VA_ARGS__.
>
> C++ also rejects implicit conversions used by helper argument packing,
> bpf_for_each, and generated helper function pointers. Use explicit
> integer and typed pointer casts so these helpers can be parsed and used
> from C++.
>
> Verified with GCC and Clang C++26 syntax checks using helper macros,
> an ARM64-targeted BPF C smoke build, and generated helper definitions
> from scripts/bpf_doc.py. checkpatch.pl reports no errors or warnings.
>
> AI assistance: OpenAI Codex helped reproduce the C++26 failures and
> draft changes to tools/lib/bpf/bpf_helpers.h and scripts/bpf_doc.py.
> The human submitter must review the changes and test results before
> submission.
>
> Fixes: d6a6a55518c1 ("libbpf: Move BPF_SEQ_PRINTF and BPF_SNPRINTF to bpf_helpers.h")
> Closes: https://github.com/libbpf/libbpf/issues/974
> Signed-off-by: Eduard Zateev <hackerowskiy@gmail.com>
Is this Fixes: tag accurate?
d6a6a55518c1 only moved BPF_SEQ_PRINTF and BPF_SNPRINTF from bpf_tracing.h
to bpf_helpers.h. Their args... syntax came from c09add2fbc5a
("tools/libbpf: Add bpf_iter support").
The other constructs changed here came from different commits:
- bpf_printk()'s args... and ##args from c2758baa9798 ("libbpf: Modify
bpf_printk to choose helper based on arg count")
- bpf_for_each()'s (void *)0 initializer from the open-coded iterator
work, moved into bpf_helpers.h by c5e647416708
- the "(void *) %d" helper initializer in the generated header from
7a387bed47f7 ("scripts/bpf: teach bpf_helpers_doc.py to dump BPF
helper definitions")
None of those commits broke C++ support for the header. It never had C++
support, so this patch looks like it adds it rather than fixing a
regression.
The tag would also mark this change for stable trees back to v5.14, many of
which do not have bpf_for_each(), bpf_stream_printk() or __bpf_vprintk().
Should the Fixes: tag be dropped, given that Closes: already gives the
motivation?
> diff --git a/scripts/bpf_doc.py b/scripts/bpf_doc.py
> index 15d113a1bc1d..02e31ad509af 100755
> --- a/scripts/bpf_doc.py
> +++ b/scripts/bpf_doc.py
> @@ -829,6 +829,11 @@ class PrinterHelpersHeader(Printer):
> /* Forward declarations of BPF structs */'''
>
> print(header)
> + print("#ifdef __cplusplus")
> + print("#define ___bpf_helper_stub(id, name) reinterpret_cast<decltype(+name)>(id)")
> + print("#else")
> + print("#define ___bpf_helper_stub(id, name) ((void *)(id))")
> + print("#endif")
> for fwd in self.type_fwds:
> print('%s;' % fwd)
> print('')
This isn't a bug, but the header string ends with the comment
"/* Forward declarations of BPF structs */", and this new block is printed
right after it, before the struct declarations. The generated
bpf_helper_defs.h now reads:
/* Forward declarations of BPF structs */
#ifdef __cplusplus
#define ___bpf_helper_stub(id, name) reinterpret_cast<decltype(+name)>(id)
#else
#define ___bpf_helper_stub(id, name) ((void *)(id))
#endif
struct bpf_fib_lookup;
So the comment now sits on top of the macro block instead of the forward
declarations. Could the #ifdef block be printed before the comment, or after
the forward declarations and the blank line, so the comment stays next to the
declarations it describes?
[ ... ]
> @@ -382,16 +403,18 @@ extern void bpf_iter_num_destroy(struct bpf_iter_num *it) __weak __ksym;
> * extension: __attribute__((cleanup(<func>))), supported by both GCC and
> * Clang.
> */
> -#define bpf_for_each(type, cur, args...) for ( \
> +#define bpf_for_each(type, cur, ...) for ( \
> /* initialize and define destructor */ \
> struct bpf_iter_##type ___it __attribute__((aligned(8), /* enforce, just in case */, \
> cleanup(bpf_iter_##type##_destroy))), \
> /* ___p pointer is just to call bpf_iter_##type##_new() *once* to init ___it */ \
> *___p __attribute__((unused)) = ( \
> - bpf_iter_##type##_new(&___it, ##args), \
> + bpf_iter_##type##_new(&___it, \
> + ##__VA_ARGS__), \
> /* this is a workaround for Clang bug: it currently doesn't emit BTF */ \
> /* for bpf_iter_##type##_destroy() when used from cleanup() attribute */ \
> - (void)bpf_iter_##type##_destroy, (void *)0); \
> + (void)bpf_iter_##type##_destroy, \
> + (struct bpf_iter_##type *)0); \
> /* iteration and termination check */ \
> (((cur) = bpf_iter_##type##_next(&___it))); \
> )
The commit message says C++ rejects the implicit conversions used by
bpf_for_each(), and this changes its ___p initializer from (void *)0 to
(struct bpf_iter_##type *)0. The sibling macros bpf_for() and bpf_repeat()
still end their ___p initializer with:
(void)bpf_iter_num_destroy, (void *)0);
___p is declared as struct bpf_iter_num * there (in the same declaration as
___it), and C++ does not allow an implicit conversion from void * to
struct bpf_iter_num *.
Won't a C++ BPF program that uses bpf_for(i, 0, N) or bpf_repeat(N) still
fail to compile with the same error this patch fixes for bpf_for_each()?
Since the stated goal is that "these helpers can be parsed and used from
C++", should bpf_for() and bpf_repeat() get the same
(struct bpf_iter_num *)0 change?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/40061559445
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] libbpf: fix helper macro C++ compatibility
2026-10-24 3:59 [PATCH] libbpf: fix helper macro C++ compatibility Eduard Zateev
2026-10-24 4:36 ` bot+bpf-ci
@ 2026-10-24 19:16 ` Alexei Starovoitov
1 sibling, 0 replies; 3+ messages in thread
From: Alexei Starovoitov @ 2026-10-24 19:16 UTC (permalink / raw)
To: Eduard Zateev, bpf
Cc: daniel, andrii, eddyz87, memxor, martin.lau, song, yonghong.song,
jolsa, emil, ihor.solodrai, linux-kernel
On Fri, Oct 23, 2026 at 08:59 PM Eduard Zateev <hackerowskiy@gmail.com> wrote:
> The human submitter must review the changes and test results before
> submission.
ouch. did you read what AI generated for you?
The reason for rejection is the same as before.
You're stratching the surface without thinking.
Pls stop sending such patches.
pw-bot: cr
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-24 19:16 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-24 3:59 [PATCH] libbpf: fix helper macro C++ compatibility Eduard Zateev
2026-10-24 4:36 ` bot+bpf-ci
2026-10-24 19:16 ` Alexei Starovoitov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®