* [PATCH bpf-next v3 0/3] Normalize enum values in libbpf and bpftool
@ 2026-11-01 10:32 Hungyu Lin
2026-11-01 10:32 ` [PATCH bpf-next v3 1/3] libbpf: Normalize enum values according to signedness Hungyu Lin
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Hungyu Lin @ 2026-11-01 10:32 UTC (permalink / raw)
To: ast, bpf
Cc: andrii, qmo, daniel, yonghong.song, eddyz87, ihor.solodrai,
shuah, linux-kselftest, linux-kernel, Hungyu Lin
This series fixes enum signedness problems in libbpf and bpftool data
dumps using the shared btf_enum_value() helper suggested by Alexei.
It also uses the helper when parsing enum-backed map fields, where sign
extension can change the requested map_extra value.
Keep existing enum name matches before considering normalized values.
This matters for eight-byte BTF_KIND_ENUM values in both dumpers and
narrow negative enums in bpftool. libbpf already zero-extends narrow
data without kflag; that behavior is unchanged. bpftool preserves the
original name's priority in one scan, even if another enumerator matches
the normalized value.
For an eight-byte ordinary enum with kflag clear, a stored negative
enumerator can match both its sign- and zero-extended values. This is
a compatibility choice: identical metadata can describe either a legacy
signed enum or an unsigned enum.
Unknown bpftool values follow kflag. With kflag clear, numeric output is
unsigned, including BTF sanitized by libbpf for kernels without enum64
support. This changes unknown negative values whose original signedness
is no longer recorded. libbpf's numeric fallback is unchanged.
CO-RE is left unchanged. Clang 14 and 18 emit a zero-extended immediate
for 0x80000000U, which the current sign-extended orig_val rejects. But
using the helper directly would reject negative enum immediates from
Clang 14 objects without kflag, and would zero-extend negative values
from target BTF without kflag. Those compatibility issues need a
separate change.
This supersedes the pending libbpf patch [1] and bpftool data-dump v2 [2],
as requested in [3]. The already applied bpftool type-JSON fix,
eaae576c34d7, is in the base and is not resubmitted.
Changes in v3:
- Combine libbpf, bpftool and regression tests into one series.
- Add btf_enum_value() and use it for enum-backed map fields and data
name matching.
- Preserve existing legacy names, including eight-byte ordinary enums
and ambiguous narrow names, addressing the review of bpftool v2.
- Add libbpf data-dump tests and kernel-backed bpftool map-dump tests.
Validation on ARM64, GCC 13.3.0, Clang 18.1.3, Linux 6.12.68:
- Complete libbpf and bpftool builds pass, including the libbpf ABI
checks and bpftool built with -Werror.
- With the new tests on the baseline: 38 subtests pass, 8 fail.
- With the fixes: all 46 subtests pass, none fail or skip.
These are the btf_dump and bpftool_map_dump test groups. The bpftool
cases load BTF and maps into the kernel and check both CLI formats.
Run these groups with ./test_progs -t btf_dump,bpftool_map_dump.
- map_extra parsing was checked with a separate local object-open
harness; this series does not include a selftest for that path.
Arena allocation and mmap were not tested.
[1] https://lore.kernel.org/bpf/20261031233219.9362-1-dennylin0707@gmail.com/
[2] https://lore.kernel.org/bpf/20261101001024.9994-1-dennylin0707@gmail.com/
[3] https://lore.kernel.org/bpf/DMJG1R2YIJRK.16CZLV9S5U7Z7@gmail.com/
Thanks,
Hungyu
Hungyu Lin (3):
libbpf: Normalize enum values according to signedness
bpftool: Preserve unsigned enum values in data dumps
selftests/bpf: Cover enum signedness and legacy name matching
tools/bpf/bpftool/btf_dumper.c | 42 ++--
tools/lib/bpf/btf.h | 6 +
tools/lib/bpf/btf_dump.c | 3 +-
tools/lib/bpf/libbpf.c | 2 +-
.../bpf/prog_tests/bpftool_map_dump.c | 180 ++++++++++++++++++
.../selftests/bpf/prog_tests/btf_dump.c | 84 ++++++++
6 files changed, 302 insertions(+), 15 deletions(-)
base-commit: a30c772627b2ef4bc715332b1b04938a914556cd
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH bpf-next v3 1/3] libbpf: Normalize enum values according to signedness 2026-11-01 10:32 [PATCH bpf-next v3 0/3] Normalize enum values in libbpf and bpftool Hungyu Lin @ 2026-11-01 10:32 ` Hungyu Lin 2026-11-01 10:32 ` [PATCH bpf-next v3 2/3] bpftool: Preserve unsigned enum values in data dumps Hungyu Lin 2026-11-01 10:32 ` [PATCH bpf-next v3 3/3] selftests/bpf: Cover enum signedness and legacy name matching Hungyu Lin 2 siblings, 0 replies; 4+ messages in thread From: Hungyu Lin @ 2026-11-01 10:32 UTC (permalink / raw) To: ast, bpf Cc: andrii, qmo, daniel, yonghong.song, eddyz87, ihor.solodrai, shuah, linux-kselftest, linux-kernel, Hungyu Lin BTF_KIND_ENUM stores enumerators in a signed 32-bit field even for unsigned enums. btf_dump_enum_data() compares unsigned data against that signed field, so values with bit 31 set fail to match their names. get_map_field_long() also sign-extends the field: for example, __ulong(map_extra, 0x80000000) becomes 0xffffffff80000000 instead of the requested arena mapping address. Add btf_enum_value() to extend an enumerator according to kflag and use it in both places. For data dumps, keep the original comparison as well. Older BTF producers did not record enum signedness; an eight-byte BTF_KIND_ENUM can contain signed data such as -1 with kflag clear. Using only the helper would lose an existing name match. This compatibility comparison does not truncate the data value or change the numeric fallback. For an eight-byte BTF_KIND_ENUM with kflag clear, a negative stored enumerator can therefore match both its sign- and zero-extended values. The record cannot distinguish legacy signed metadata from unsigned metadata with the same bits. Fixes: d90ec262b35b ("libbpf: Add enum64 support for btf_dump") Fixes: d147357e2e59 ("libbpf: Allow specifying 64-bit integers in map BTF.") Suggested-by: Alexei Starovoitov <ast@kernel.org> Link: https://lore.kernel.org/bpf/DMJG1R2YIJRK.16CZLV9S5U7Z7@gmail.com/ Signed-off-by: Hungyu Lin <dennylin0707@gmail.com> --- tools/lib/bpf/btf.h | 6 ++++++ tools/lib/bpf/btf_dump.c | 3 ++- tools/lib/bpf/libbpf.c | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index 57f12630c5d1..eeb4984d6c57 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -636,6 +636,12 @@ static inline struct btf_enum *btf_enum(const struct btf_type *t) return (struct btf_enum *)(t + 1); } +/* btf_enum->val is signed even for unsigned enums; kflag records the sign. */ +static inline __s64 btf_enum_value(const struct btf_type *t, const struct btf_enum *e) +{ + return btf_kflag(t) ? (__s64)e->val : (__u32)e->val; +} + struct btf_enum64; static inline struct btf_enum64 *btf_enum64(const struct btf_type *t) diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index ea9a34db4c00..b636dc4185a0 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -2373,7 +2373,8 @@ static int btf_dump_enum_data(struct btf_dump *d, const struct btf_enum *e; for (i = 0, e = btf_enum(t); i < btf_vlen(t); i++, e++) { - if (value != e->val) + /* Keep names from legacy signed enums without kflag. */ + if (value != e->val && value != btf_enum_value(t, e)) continue; btf_dump_type_values(d, "%s", btf_name_of(d, e->name_off)); return 0; diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 02e75b183ed0..cecd6b5f27de 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -2627,7 +2627,7 @@ static bool get_map_field_long(const char *map_name, const struct btf *btf, if (btf_is_enum(t)) { const struct btf_enum *e = btf_enum(t); - *res = e->val; + *res = btf_enum_value(t, e); } else { const struct btf_enum64 *e = btf_enum64(t); -- 2.43.0 ^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH bpf-next v3 2/3] bpftool: Preserve unsigned enum values in data dumps 2026-11-01 10:32 [PATCH bpf-next v3 0/3] Normalize enum values in libbpf and bpftool Hungyu Lin 2026-11-01 10:32 ` [PATCH bpf-next v3 1/3] libbpf: Normalize enum values according to signedness Hungyu Lin @ 2026-11-01 10:32 ` Hungyu Lin 2026-11-01 10:32 ` [PATCH bpf-next v3 3/3] selftests/bpf: Cover enum signedness and legacy name matching Hungyu Lin 2 siblings, 0 replies; 4+ messages in thread From: Hungyu Lin @ 2026-11-01 10:32 UTC (permalink / raw) To: ast, bpf Cc: andrii, qmo, daniel, yonghong.song, eddyz87, ihor.solodrai, shuah, linux-kselftest, linux-kernel, Hungyu Lin btf_dumper_enum() loads ordinary enum data as signed and uses signed numeric output. Unsigned values with the high bit set can therefore be printed as negative numbers. Narrow unsigned values also fail to match their enumerators: an eight-bit 0xff is loaded as -1, while its named value is stored as 255. btf_dumper_enum64() likewise uses signed numeric output for unmatched values. For example, 0x8000000000000001 is printed as -9223372036854775807 instead of 9223372036854775809. Load the data according to the enum's signedness, use btf_enum_value() for name matching, and select the numeric writer according to kflag. Retain the original signed-width comparison and give it priority over new matches. This preserves names from older BTF without signedness, including eight-byte ordinary enums, and keeps the selected name when multiple enumerators could match. Remember the first normalized match while looking for an original match so only one scan is needed. For an eight-byte BTF_KIND_ENUM with kflag clear, a negative stored enumerator can therefore match both its sign- and zero-extended values. The record cannot distinguish legacy signed metadata from unsigned metadata with the same bits. Unmatched values are printed as unsigned when kflag is clear. This includes BTF read back from kernels without enum64 support, where libbpf clears kflag before loading. The original signedness cannot be recovered from that flag. Fixes: ca7e6e45d108 ("bpftool: Print the enum's name instead of value") Fixes: 58a53978fdf6 ("bpftool: Add btf enum64 support") Suggested-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Hungyu Lin <dennylin0707@gmail.com> --- tools/bpf/bpftool/btf_dumper.c | 42 +++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c index 3267163119dc..792e210cb503 100644 --- a/tools/bpf/bpftool/btf_dumper.c +++ b/tools/bpf/bpftool/btf_dumper.c @@ -148,37 +148,50 @@ static int btf_dumper_enum(const struct btf_dumper *d, const struct btf_type *t, const void *data) { - const struct btf_enum *enums = btf_enum(t); - __s64 value; + const struct btf_enum *enums = btf_enum(t), *match = NULL; + bool is_signed = btf_kflag(t); + __s64 value, legacy_value; __u32 i; switch (t->size) { case 8: - value = *(__s64 *)data; + legacy_value = *(__s64 *)data; + value = legacy_value; break; case 4: - value = *(__s32 *)data; + legacy_value = *(__s32 *)data; + value = is_signed ? legacy_value : *(__u32 *)data; break; case 2: - value = *(__s16 *)data; + legacy_value = *(__s16 *)data; + value = is_signed ? legacy_value : *(__u16 *)data; break; case 1: - value = *(__s8 *)data; + legacy_value = *(__s8 *)data; + value = is_signed ? legacy_value : *(__u8 *)data; break; default: return -EINVAL; } + /* Prefer existing name matches, including signed enums without kflag. */ for (i = 0; i < btf_vlen(t); i++) { - if (value == enums[i].val) { - jsonw_string(d->jw, - btf__name_by_offset(d->btf, - enums[i].name_off)); - return 0; + if (legacy_value == enums[i].val) { + match = &enums[i]; + break; } + if (!match && value == btf_enum_value(t, &enums[i])) + match = &enums[i]; + } + if (match) { + jsonw_string(d->jw, btf__name_by_offset(d->btf, match->name_off)); + return 0; } - jsonw_int(d->jw, value); + if (is_signed) + jsonw_int(d->jw, value); + else + jsonw_uint(d->jw, value); return 0; } @@ -204,7 +217,10 @@ static int btf_dumper_enum64(const struct btf_dumper *d, } } - jsonw_int(d->jw, value); + if (btf_kflag(t)) + jsonw_int(d->jw, value); + else + jsonw_uint(d->jw, value); return 0; } -- 2.43.0 ^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH bpf-next v3 3/3] selftests/bpf: Cover enum signedness and legacy name matching 2026-11-01 10:32 [PATCH bpf-next v3 0/3] Normalize enum values in libbpf and bpftool Hungyu Lin 2026-11-01 10:32 ` [PATCH bpf-next v3 1/3] libbpf: Normalize enum values according to signedness Hungyu Lin 2026-11-01 10:32 ` [PATCH bpf-next v3 2/3] bpftool: Preserve unsigned enum values in data dumps Hungyu Lin @ 2026-11-01 10:32 ` Hungyu Lin 2 siblings, 0 replies; 4+ messages in thread From: Hungyu Lin @ 2026-11-01 10:32 UTC (permalink / raw) To: ast, bpf Cc: andrii, qmo, daniel, yonghong.song, eddyz87, ihor.solodrai, shuah, linux-kselftest, linux-kernel, Hungyu Lin Exercise unsigned enum values with their high bit set and signed controls through libbpf's data dumper. Include ordinary enums stored in eight bytes and legacy BTF with kflag clear so normalization does not lose existing names or match only the low 32 bits of wider data. Extend bpftool_map_dump with BTF-backed array maps and check both JSON and plain output. Cover narrow unsigned names, signed and unsigned numeric fallbacks, legacy enum names, both enumerator orders for ambiguous narrow names, and unmatched ENUM64 values. Build legacy enum records through the public BTF API and load them into the kernel before invoking bpftool. Initialize map data at its actual width so the tests also work on big-endian hosts. Suggested-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Hungyu Lin <dennylin0707@gmail.com> --- .../bpf/prog_tests/bpftool_map_dump.c | 180 ++++++++++++++++++ .../selftests/bpf/prog_tests/btf_dump.c | 84 ++++++++ 2 files changed, 264 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/bpftool_map_dump.c b/tools/testing/selftests/bpf/prog_tests/bpftool_map_dump.c index 187b3f784307..926d0f324a5f 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpftool_map_dump.c +++ b/tools/testing/selftests/bpf/prog_tests/bpftool_map_dump.c @@ -7,6 +7,7 @@ #define PIN_PATH "/sys/fs/bpf/test_bpftool_map_dump_sk_storage" #define QUEUE_PIN_PATH "/sys/fs/bpf/test_bpftool_map_dump_queue" +#define ENUM_PIN_PATH "/sys/fs/bpf/test_bpftool_map_dump_enum" #define JSON_ERROR "{\"error\":\"can't dump sk_storage map" /* An empty socket storage map with an int value. */ @@ -124,10 +125,189 @@ static void test_queue(void) close(map_fd); } +enum enum_dump_kind { + DUMP_ENUM_UNSIGNED, + DUMP_ENUM_SIGNED, + DUMP_ENUM_LEGACY, + DUMP_ENUM_ALIAS_FIRST, + DUMP_ENUM_ALIAS_LAST, + DUMP_ENUM64_UNSIGNED, + DUMP_ENUM64_SIGNED, +}; + +struct enum_dump_test { + const char *name; + enum enum_dump_kind kind; + __u32 size; + __u64 value; + const char *expected; +}; + +static const struct enum_dump_test enum_dump_tests[] = { + { "enum_u8_name", DUMP_ENUM_UNSIGNED, 1, 0xff, "\"MAX\"" }, + { "enum_u8_unknown", DUMP_ENUM_UNSIGNED, 1, (__u64)-22, "234" }, + { "enum_u16_name", DUMP_ENUM_UNSIGNED, 2, 0xffff, "\"MAX\"" }, + { "enum_u16_unknown", DUMP_ENUM_UNSIGNED, 2, (__u64)-22, "65514" }, + { "enum_u32_high", DUMP_ENUM_UNSIGNED, 4, 0x80000000, "\"HIGH\"" }, + { "enum_u32_max", DUMP_ENUM_UNSIGNED, 4, 0xffffffff, "\"MAX\"" }, + { "enum_u32_unknown", DUMP_ENUM_UNSIGNED, 4, (__u64)-22, "4294967274" }, + { "enum_s8_name", DUMP_ENUM_SIGNED, 1, (__u64)-1, "\"NEG\"" }, + { "enum_s8_unknown", DUMP_ENUM_SIGNED, 1, (__u64)-22, "-22" }, + { "enum_s16_name", DUMP_ENUM_SIGNED, 2, (__u64)-1, "\"NEG\"" }, + { "enum_s32_name", DUMP_ENUM_SIGNED, 4, (__u64)-1, "\"NEG\"" }, + { "enum_s32_unknown", DUMP_ENUM_SIGNED, 4, (__u64)-22, "-22" }, + { "enum_legacy8", DUMP_ENUM_LEGACY, 1, (__u64)-1, "\"NEG\"" }, + { "enum_legacy16", DUMP_ENUM_LEGACY, 2, (__u64)-1, "\"NEG\"" }, + { "enum_legacy32", DUMP_ENUM_LEGACY, 4, (__u64)-1, "\"NEG\"" }, + { "enum_legacy_wide", DUMP_ENUM_LEGACY, 8, (__u64)-1, "\"NEG\"" }, + { "enum_alias_first", DUMP_ENUM_ALIAS_FIRST, 1, 0xff, "\"NEG\"" }, + { "enum_alias_last", DUMP_ENUM_ALIAS_LAST, 1, 0xff, "\"NEG\"" }, + { "enum_wide_name", DUMP_ENUM_UNSIGNED, 8, 0xffffffff, "\"MAX\"" }, + { "enum_wide_unknown", DUMP_ENUM_UNSIGNED, 8, 0x1ffffffffULL, "8589934591" }, + { "enum64_unsigned", DUMP_ENUM64_UNSIGNED, 8, (1ULL << 63) | 1, + "9223372036854775809" }, + { "enum64_signed", DUMP_ENUM64_SIGNED, 8, (__u64)-22, "-22" }, +}; + +static int add_enum_dump_values(struct btf *btf, const struct enum_dump_test *test) +{ + __u64 max = test->size == 1 ? 0xff : test->size == 2 ? 0xffff : 0xffffffff; + int err; + + switch (test->kind) { + case DUMP_ENUM_UNSIGNED: + err = btf__add_enum_value(btf, "HIGH", (max >> 1) + 1); + return err ?: btf__add_enum_value(btf, "MAX", max); + case DUMP_ENUM_SIGNED: + return btf__add_enum_value(btf, "NEG", -1); + case DUMP_ENUM_LEGACY: + /* Model old BTF: raw signed -1, but no signedness kflag. */ + return btf__add_enum_value(btf, "NEG", (__u32)-1); + case DUMP_ENUM_ALIAS_FIRST: + err = btf__add_enum_value(btf, "U255", 0xff); + return err ?: btf__add_enum_value(btf, "NEG", (__u32)-1); + case DUMP_ENUM_ALIAS_LAST: + err = btf__add_enum_value(btf, "NEG", (__u32)-1); + return err ?: btf__add_enum_value(btf, "U255", 0xff); + case DUMP_ENUM64_UNSIGNED: + err = btf__add_enum64_value(btf, "HIGH", 1ULL << 63); + return err ?: btf__add_enum64_value(btf, "LOW", 1); + case DUMP_ENUM64_SIGNED: + return btf__add_enum64_value(btf, "NEG", (__u64)-1); + default: + return -EINVAL; + } +} + +static int create_enum_dump_map(const struct enum_dump_test *test) +{ + LIBBPF_OPTS(bpf_map_create_opts, opts); + bool enum64 = test->kind == DUMP_ENUM64_UNSIGNED || + test->kind == DUMP_ENUM64_SIGNED; + struct btf *btf; + int key_id, value_id, fd = -1; + + btf = btf__new_empty(); + if (!ASSERT_OK_PTR(btf, "btf__new_empty")) + return -1; + + key_id = btf__add_int(btf, "int", 4, BTF_INT_SIGNED); + if (!ASSERT_GT(key_id, 0, "btf__add_int")) + goto out; + if (enum64) + value_id = btf__add_enum64(btf, "enum_value", test->size, + test->kind == DUMP_ENUM64_SIGNED); + else + value_id = btf__add_enum(btf, "enum_value", test->size); + if (!ASSERT_GT(value_id, 0, "add enum")) + goto out; + if (!ASSERT_OK(add_enum_dump_values(btf, test), "add enum values")) + goto out; + if (!ASSERT_OK(btf__load_into_kernel(btf), "btf__load_into_kernel")) + goto out; + + opts.btf_fd = btf__fd(btf); + opts.btf_key_type_id = key_id; + opts.btf_value_type_id = value_id; + fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "enum_dump", sizeof(__u32), + test->size, 1, &opts); + ASSERT_OK_FD(fd, "bpf_map_create"); +out: + btf__free(btf); + return fd; +} + +static void test_enum_dump(const struct enum_dump_test *test) +{ + union { + __u8 val8; + __u16 val16; + __u32 val32; + __u64 val64; + } value; + char output[1024] = {}, expected[128]; + __u32 key = 0; + int map_fd, ret; + + /* Store the value at its native width, including on big-endian hosts. */ + switch (test->size) { + case 1: + value.val8 = (__u8)test->value; + break; + case 2: + value.val16 = (__u16)test->value; + break; + case 4: + value.val32 = (__u32)test->value; + break; + case 8: + value.val64 = test->value; + break; + default: + PRINT_FAIL("unexpected enum size %u (expected 1, 2, 4 or 8)\n", test->size); + return; + } + + map_fd = create_enum_dump_map(test); + if (map_fd < 0) + return; + if (!ASSERT_OK(bpf_map_update_elem(map_fd, &key, &value, BPF_ANY), + "update enum value")) + goto out; + if (!ASSERT_OK(bpf_obj_pin(map_fd, ENUM_PIN_PATH), "pin enum map")) + goto out; + + ret = get_bpftool_command_output("-j map dump pinned " ENUM_PIN_PATH, + output, sizeof(output)); + if (!ASSERT_OK(ret, "json enum dump")) + goto out; + snprintf(expected, sizeof(expected), "\"formatted\":{\"key\":0,\"value\":%s}", + test->expected); + ASSERT_HAS_SUBSTR(output, expected, "json enum value"); + + memset(output, 0, sizeof(output)); + ret = get_bpftool_command_output("map dump pinned " ENUM_PIN_PATH, + output, sizeof(output)); + if (!ASSERT_OK(ret, "plain enum dump")) + goto out; + snprintf(expected, sizeof(expected), "\"value\": %s\n", test->expected); + ASSERT_HAS_SUBSTR(output, expected, "plain enum value"); +out: + unlink(ENUM_PIN_PATH); + close(map_fd); +} + void test_bpftool_map_dump(void) { + size_t i; + if (test__start_subtest("sk_storage")) test_sk_storage(); if (test__start_subtest("queue")) test_queue(); + + for (i = 0; i < ARRAY_SIZE(enum_dump_tests); i++) { + if (test__start_subtest(enum_dump_tests[i].name)) + test_enum_dump(&enum_dump_tests[i]); + } } diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c index dbef014f75ac..20a169095e9c 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c +++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c @@ -952,6 +952,87 @@ static void test_btf_dump_enum_data(struct btf *btf, struct btf_dump *d, sizeof(enum bpf_cmd) - 1, "", BPF_MAP_CREATE); } +static void test_btf_dump_enum_values(void) +{ + LIBBPF_OPTS(btf_dump_type_data_opts, opts, + .compact = true, + .skip_names = true); + struct { + const char *name; + __u32 size; + __s64 enum_value; + union { + __u8 u8; + __u16 u16; + __u32 u32; + __u64 u64; + } data; + const char *expected; + } cases[] = { + { "unsigned_byte", 1, 0xff, { .u8 = 0xff }, "VALUE" }, + { "unsigned_short", 2, 0xffff, { .u16 = 0xffff }, "VALUE" }, + { "unsigned_high", 4, 0x80000000LL, + { .u32 = 0x80000000U }, "VALUE" }, + { "unsigned_max", 4, 0xffffffffLL, + { .u32 = 0xffffffffU }, "VALUE" }, + { "signed_value", 4, -1, { .u32 = (__u32)-1 }, "VALUE" }, + { "signed_unknown", 4, -1, { .u32 = (__u32)-22 }, "-22" }, + /* Old BTF stores negative enum values without setting kflag. */ + { "legacy_wide", 8, (__u32)-1, { .u64 = (__u64)-1 }, "VALUE" }, + { "unsigned_wide", 8, 0xffffffffLL, + { .u64 = 0xffffffffULL }, "VALUE" }, + /* + * An unrelated 64-bit value must not match an ENUM32 by its + * low 32 bits. Do not constrain the numeric fallback's width. + */ + { "wide_unknown", 8, (__u32)-1, + { .u64 = 0x1ffffffffULL }, NULL }, + { "unknown_byte", 1, (__u32)-1, { .u8 = (__u8)-22 }, "234" }, + { "unknown_short", 2, (__u32)-1, { .u16 = (__u16)-22 }, "65514" }, + { "unknown_word", 4, (__u32)-1, + { .u32 = (__u32)-22 }, "4294967274" }, + }; + struct btf_dump *d; + struct btf *btf; + int ids[ARRAY_SIZE(cases)]; + char str[STRSIZE]; + int err, i; + + btf = btf__new_empty(); + if (!ASSERT_OK_PTR(btf, "new_empty")) + return; + for (i = 0; i < ARRAY_SIZE(cases); i++) { + ids[i] = btf__add_enum(btf, cases[i].name, cases[i].size); + if (!ASSERT_GT(ids[i], 0, "add_enum")) + goto free_btf; + err = btf__add_enum_value(btf, "VALUE", cases[i].enum_value); + if (!ASSERT_OK(err, "add_enum_value")) + goto free_btf; + } + + d = btf_dump__new(btf, btf_dump_snprintf, str, NULL); + if (!ASSERT_OK_PTR(d, "btf_dump__new")) + goto free_btf; + + for (i = 0; i < ARRAY_SIZE(cases); i++) { + str[0] = '\0'; + err = btf_dump__dump_type_data(d, ids[i], &cases[i].data, + cases[i].size, &opts); + ASSERT_EQ(err, cases[i].size, cases[i].name); + if (cases[i].expected) { + ASSERT_STREQ(str, cases[i].expected, cases[i].name); + } else { + ASSERT_NEQ(str[0], '\0', cases[i].name); + ASSERT_EQ(strspn(str, "0123456789"), strlen(str), + cases[i].name); + } + } + + btf_dump__free(d); +free_btf: + btf__free(btf); +} + static void test_btf_dump_struct_data(struct btf *btf, struct btf_dump *d, char *str) { @@ -1396,6 +1477,9 @@ void test_btf_dump() { if (test__start_subtest("btf_dump: type_tags")) test_btf_dump_type_tags(); + if (test__start_subtest("btf_dump: enum_values")) + test_btf_dump_enum_values(); + btf = libbpf_find_kernel_btf(); if (!ASSERT_OK_PTR(btf, "no kernel BTF found")) return; -- 2.43.0 ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-01 10:32 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-11-01 10:32 [PATCH bpf-next v3 0/3] Normalize enum values in libbpf and bpftool Hungyu Lin 2026-11-01 10:32 ` [PATCH bpf-next v3 1/3] libbpf: Normalize enum values according to signedness Hungyu Lin 2026-11-01 10:32 ` [PATCH bpf-next v3 2/3] bpftool: Preserve unsigned enum values in data dumps Hungyu Lin 2026-11-01 10:32 ` [PATCH bpf-next v3 3/3] selftests/bpf: Cover enum signedness and legacy name matching Hungyu Lin
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®