public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
From: Mahe Tardy <mahe.tardy@gmail.com>
To: netdev@vger.kernel.org
Cc: Yonghong Song <yhs@fb.com>, Alexei Starovoitov <ast@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>
Subject: bpf: LLVM BTF inner map struct type def missing
Date: Tue, 22 Jul 2025 20:44:42 +0200	[thread overview]
Message-ID: <aH_cGvgC20iD8qs9@gmail.com> (raw)

Hello,

While writing a BPF prog using map of maps I bumped into this compiler
bug that GitHub user thediveo and Isovalent colleague Timo Beckers
already discussed in the cilium/ebpf discussions [^1].

The issue is that a struct only used in a inner map is not included in
the program BTF, so it needs a dummy declaration elsewhere to work.

For example such program:

	#include "vmlinux.h"
	#include <bpf/bpf_helpers.h>

	struct missing_type {
		uint64_t foo;
	};

	// struct missing_type bar; // commented on purpose

	struct {
		__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
		__type(key, uint32_t);
		__type(value, uint32_t);
		__uint(max_entries, 16);
		__array(
			values, struct {
				__uint(type, BPF_MAP_TYPE_HASH);
				__type(key, uint64_t);
				__type(value, struct missing_type);
				__uint(max_entries, 32);
			});
	} outer_map SEC(".maps");

Then do:

	bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
	clang -target bpf -g -O2 -c prog.c -o prog.o
	bpftool btf dump file prog.o

Will result in:

	[1] PTR '(anon)' type_id=3
	[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
	[3] ARRAY '(anon)' type_id=2 index_type_id=4 nr_elems=12
	[4] INT '__ARRAY_SIZE_TYPE__' size=4 bits_offset=0 nr_bits=32 encoding=(none)
	[5] PTR '(anon)' type_id=6
	[6] TYPEDEF 'uint32_t' type_id=7
	[7] TYPEDEF 'u32' type_id=8
	[8] TYPEDEF '__u32' type_id=9
	[9] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)
	[10] PTR '(anon)' type_id=11
	[11] ARRAY '(anon)' type_id=2 index_type_id=4 nr_elems=16
	[12] PTR '(anon)' type_id=13
	[13] STRUCT '(anon)' size=32 vlen=4
		'type' type_id=14 bits_offset=0
		'key' type_id=16 bits_offset=64
		'value' type_id=21 bits_offset=128
		'max_entries' type_id=22 bits_offset=192
	[14] PTR '(anon)' type_id=15
	[15] ARRAY '(anon)' type_id=2 index_type_id=4 nr_elems=1
	[16] PTR '(anon)' type_id=17
	[17] TYPEDEF 'uint64_t' type_id=18
	[18] TYPEDEF 'u64' type_id=19
	[19] TYPEDEF '__u64' type_id=20
	[20] INT 'unsigned long long' size=8 bits_offset=0 nr_bits=64 encoding=(none)
	[21] PTR '(anon)' type_id=28
	[22] PTR '(anon)' type_id=23
	[23] ARRAY '(anon)' type_id=2 index_type_id=4 nr_elems=32
	[24] ARRAY '(anon)' type_id=12 index_type_id=4 nr_elems=0
	[25] STRUCT '(anon)' size=32 vlen=5
		'type' type_id=1 bits_offset=0
		'key' type_id=5 bits_offset=64
		'value' type_id=5 bits_offset=128
		'max_entries' type_id=10 bits_offset=192
		'values' type_id=24 bits_offset=256
	[26] VAR 'outer_map' type_id=25, linkage=global
	[27] DATASEC '.maps' size=0 vlen=1
		type_id=26 offset=0 size=32 (VAR 'outer_map')
	[28] FWD 'missing_type' fwd_kind=struct

You can see that the outer map is [25], with values [24] with type to
[12] thus [13] and then the value of [13] is [21] which points to type
[28]. And [28] is a forward declaration. Thus if we try to load this
program (there's no program but the libbpf error msg is explicit):

	bpftool prog load prog.o /sys/fs/bpf/prog

Output is

	libbpf: map 'outer_map.inner': can't determine value size for type [28]: -22.

Now if you uncomment the commented line in the example (or use this type
in a function as suggested by Timo), the BTF looks like this:

	[1] PTR '(anon)' type_id=3
	[2] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED
	[3] ARRAY '(anon)' type_id=2 index_type_id=4 nr_elems=12
	[4] INT '__ARRAY_SIZE_TYPE__' size=4 bits_offset=0 nr_bits=32 encoding=(none)
	[5] PTR '(anon)' type_id=6
	[6] TYPEDEF 'uint32_t' type_id=7
	[7] TYPEDEF 'u32' type_id=8
	[8] TYPEDEF '__u32' type_id=9
	[9] INT 'unsigned int' size=4 bits_offset=0 nr_bits=32 encoding=(none)
	[10] PTR '(anon)' type_id=11
	[11] ARRAY '(anon)' type_id=2 index_type_id=4 nr_elems=16
	[12] PTR '(anon)' type_id=13
	[13] STRUCT '(anon)' size=32 vlen=4
		'type' type_id=14 bits_offset=0
		'key' type_id=16 bits_offset=64
		'value' type_id=21 bits_offset=128
		'max_entries' type_id=22 bits_offset=192
	[14] PTR '(anon)' type_id=15
	[15] ARRAY '(anon)' type_id=2 index_type_id=4 nr_elems=1
	[16] PTR '(anon)' type_id=17
	[17] TYPEDEF 'uint64_t' type_id=18
	[18] TYPEDEF 'u64' type_id=19
	[19] TYPEDEF '__u64' type_id=20
	[20] INT 'unsigned long long' size=8 bits_offset=0 nr_bits=64 encoding=(none)
	[21] PTR '(anon)' type_id=27
	[22] PTR '(anon)' type_id=23
	[23] ARRAY '(anon)' type_id=2 index_type_id=4 nr_elems=32
	[24] ARRAY '(anon)' type_id=12 index_type_id=4 nr_elems=0
	[25] STRUCT '(anon)' size=32 vlen=5
		'type' type_id=1 bits_offset=0
		'key' type_id=5 bits_offset=64
		'value' type_id=5 bits_offset=128
		'max_entries' type_id=10 bits_offset=192
		'values' type_id=24 bits_offset=256
	[26] VAR 'outer_map' type_id=25, linkage=global
	[27] STRUCT 'missing_type' size=8 vlen=1
		'foo' type_id=17 bits_offset=0
	[28] VAR 'bar' type_id=27, linkage=global
	[29] DATASEC '.bss' size=0 vlen=1
		type_id=28 offset=0 size=8 (VAR 'bar')
	[30] DATASEC '.maps' size=0 vlen=1
		type_id=26 offset=0 size=32 (VAR 'outer_map')

And then the type [27] exists, loading can now proceed.

I tested it with latest LLVM-project head when writing this e789f8bdf369
("[libc][math] Add Generic Comparison Operations for floating point
types (#144983)").

If you think it's reasonable to fix, I would be interested looking into
this.

[^1]: https://github.com/cilium/ebpf/discussions/1658#discussioncomment-12491339

             reply	other threads:[~2025-07-22 18:44 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-07-22 18:44 Mahe Tardy [this message]
2025-07-22 23:02 ` bpf: LLVM BTF inner map struct type def missing Yonghong Song
2025-07-25 11:51   ` Mahe Tardy
2025-07-25 17:40     ` Yonghong Song

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=aH_cGvgC20iD8qs9@gmail.com \
    --to=mahe.tardy@gmail.com \
    --cc=ast@kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=netdev@vger.kernel.org \
    --cc=yhs@fb.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox