1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2026, Oracle and/or its affiliates. */
#include <test_progs.h>
#include <bpf/btf.h>
#include <bpf/libbpf.h>
#define BTF_SYSFS_DIR "/sys/kernel/btf"
#define BTF_INLINE_SUFFIX ".inline"
/*
* For a specific inline site, verify we have the right function,
* loc proto and loc param representation and that the offset is
* reasonable given the caller where it was inlined.
*/
void test_btf_inline(void)
{
struct btf *inline_btf = NULL, *btf = NULL, *vmlinux_btf = NULL;
const char *inline_caller = "bpf_testmod_test_write";
const char *inline_func = "testmod_inline_len";
unsigned long long caller_addr, base_addr;
bool skip = false, found_loc = false;
const struct btf_loc_param *lp;
int locsec_id, func_id, n, i;
const struct btf_type *t;
struct btf_loc *l;
const __u32 *p;
int err = 0;
if (!env.has_testmod) {
test__skip();
return;
}
/* LOCSEC offsets are relative to the ELF section base. */
base_addr = module_get_base_addr_sec("bpf_testmod", ".text");
if (!ASSERT_NEQ(base_addr, 0, "base_addr_nonzero"))
return;
if (!ASSERT_OK(kallsyms_find(inline_caller, &caller_addr),
"find_inline_caller"))
return;
if (!ASSERT_NEQ(caller_addr, 0, "caller_addr_nonzero"))
return;
if (!ASSERT_GE(caller_addr, base_addr, "caller_addr_ge_base_addr"))
return;
caller_addr -= base_addr;
vmlinux_btf = btf__load_vmlinux_btf();
if (!ASSERT_OK_PTR(vmlinux_btf, "vmlinux_btf"))
return;
btf = btf__parse_split(BTF_SYSFS_DIR "/bpf_testmod", vmlinux_btf);
if (!ASSERT_OK_PTR(btf, "bpf_testmod_btf"))
goto out;
inline_btf = btf__parse_split(BTF_SYSFS_DIR "/bpf_testmod" BTF_INLINE_SUFFIX,
btf);
err = libbpf_get_error(inline_btf);
/* pahole may not have inline BTF feature support. */
if (err == -ENOENT) {
skip = true;
goto out;
}
if (!ASSERT_OK(err, "parse_inline_btf"))
goto out;
locsec_id = btf__find_by_name_kind(inline_btf, "inline.text", BTF_KIND_LOCSEC);
if (locsec_id < 0) {
skip = true;
goto out;
}
func_id = btf__find_by_name_kind(inline_btf, inline_func, BTF_KIND_FUNC);
if (!ASSERT_GT(func_id, 0, "inline_caller_func"))
goto out;
t = btf__type_by_id(inline_btf, locsec_id);
n = btf_vlen(t);
for (i = 0, l = btf_locsec_locs(t); i < n; i++, l++) {
if (l->func == func_id) {
found_loc = true;
break;
}
}
if (!ASSERT_TRUE(found_loc, "found_loc"))
goto out;
if (!ASSERT_GT(l->loc_proto, 0, "loc_proto_id"))
goto out;
if (!ASSERT_GT(l->offset, 0, "loc_offset"))
goto out;
t = btf__type_by_id(inline_btf, l->loc_proto);
if (!ASSERT_OK_PTR(t, "loc_proto_ptr"))
goto out;
if (!ASSERT_EQ(btf_vlen(t), 1, "loc_proto_one_param"))
goto out;
p = btf_loc_proto_params(t);
if (!ASSERT_GT(*p, 0, "loc_proto_has_loc_param"))
goto out;
t = btf__type_by_id(inline_btf, *p);
if (!ASSERT_OK_PTR(t, "loc_param_ptr"))
goto out;
lp = btf_loc_param(t);
if (!ASSERT_TRUE(lp->flags & BTF_LOC_PARAM_REG, "param_has_reg"))
goto out;
if (!ASSERT_GT(l->offset, caller_addr, "inline_gt_caller"))
goto out;
/* simple sanity test to roughly ensure inline site still in function */
if (!ASSERT_LT(l->offset, caller_addr + 512, "inline_in_caller"))
goto out;
out:
btf__free(inline_btf);
btf__free(btf);
btf__free(vmlinux_btf);
if (skip)
test__skip();
}
|