mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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®