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
next 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