Merge branch 'compile-once-run-everywhere'

Andrii Nakryiko says:

====================
This patch set implements central part of CO-RE (Compile Once - Run
Everywhere, see [0] and [1] for slides and video): relocating fields offsets.
Most of the details are written down as comments to corresponding parts of the
code.

Patch #1 adds a bunch of commonly useful btf_xxx helpers to simplify working
with BTF types.
Patch #2 converts existing libbpf code to these new helpers and removes some
of pre-existing ones.
Patch #3 adds loading of .BTF.ext offset relocations section and macros to
work with its contents.
Patch #4 implements CO-RE relocations algorithm in libbpf.
Patch #5 introduced BPF_CORE_READ macro, hiding usage of Clang's
__builtin_preserve_access_index intrinsic that records offset relocation.
Patches #6-#14 adds selftests validating various parts of relocation handling,
type compatibility, etc.

For all tests to work, you'll need latest Clang/LLVM supporting
__builtin_preserve_access_index intrinsic, used for recording offset
relocations. Kernel on which selftests run should have BTF information built
in (CONFIG_DEBUG_INFO_BTF=y).

  [0] http://vger.kernel.org/bpfconf2019.html#session-2
  [1] http://vger.kernel.org/lpc-bpf2018.html#session-2

v5->v6:
- fix bad comment formatting for real (Alexei);

v4->v5:
- drop constness for btf_xxx() helpers, allowing to avoid type casts (Alexei);
- rebase on latest bpf-next, change test__printf back to printf;

v3->v4:
- added btf_xxx helpers (Alexei);
- switched libbpf code to new helpers;
- reduced amount of logging and simplified format in few places (Alexei);
- made flavor name parsing logic more strict (exactly three underscores);
- no uname() error checking (Alexei);
- updated misc tests to reflect latest Clang fixes (Yonghong);

v2->v3:
- enclose BPF_CORE_READ args in parens (Song);

v1->v2:
- add offsetofend(), fix btf_ext optional fields checks (Song);
- add bpf_core_dump_spec() for logging spec representation;
- move special first element processing out of the loop (Song);
- typo fixes (Song);
- drop BPF_ST | BPF_MEM insn relocation (Alexei);
- extracted BPF_CORE_READ into bpf_helpers (Alexei);
- added extra tests validating Clang capturing relocs correctly (Yonghong);
- switch core_relocs.c to use sub-tests;
- updated mods tests after Clang bug was fixed (Yonghong);
- fix bug enumerating candidate types;
====================

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Alexei Starovoitov 2019-08-07 14:43:50 -07:00
commit 726e333fd2
62 changed files with 2981 additions and 281 deletions

View File

@ -19,13 +19,6 @@
#define BTF_MAX_NR_TYPES 0x7fffffff
#define BTF_MAX_STR_OFFSET 0x7fffffff
#define IS_MODIFIER(k) (((k) == BTF_KIND_TYPEDEF) || \
((k) == BTF_KIND_VOLATILE) || \
((k) == BTF_KIND_CONST) || \
((k) == BTF_KIND_RESTRICT))
#define IS_VAR(k) ((k) == BTF_KIND_VAR)
static struct btf_type btf_void;
struct btf {
@ -42,47 +35,6 @@ struct btf {
int fd;
};
struct btf_ext_info {
/*
* info points to the individual info section (e.g. func_info and
* line_info) from the .BTF.ext. It does not include the __u32 rec_size.
*/
void *info;
__u32 rec_size;
__u32 len;
};
struct btf_ext {
union {
struct btf_ext_header *hdr;
void *data;
};
struct btf_ext_info func_info;
struct btf_ext_info line_info;
__u32 data_size;
};
struct btf_ext_info_sec {
__u32 sec_name_off;
__u32 num_info;
/* Followed by num_info * record_size number of bytes */
__u8 data[0];
};
/* The minimum bpf_func_info checked by the loader */
struct bpf_func_info_min {
__u32 insn_off;
__u32 type_id;
};
/* The minimum bpf_line_info checked by the loader */
struct bpf_line_info_min {
__u32 insn_off;
__u32 file_name_off;
__u32 line_off;
__u32 line_col;
};
static inline __u64 ptr_to_u64(const void *ptr)
{
return (__u64) (unsigned long) ptr;
@ -192,9 +144,9 @@ static int btf_parse_str_sec(struct btf *btf)
static int btf_type_size(struct btf_type *t)
{
int base_size = sizeof(struct btf_type);
__u16 vlen = BTF_INFO_VLEN(t->info);
__u16 vlen = btf_vlen(t);
switch (BTF_INFO_KIND(t->info)) {
switch (btf_kind(t)) {
case BTF_KIND_FWD:
case BTF_KIND_CONST:
case BTF_KIND_VOLATILE:
@ -219,7 +171,7 @@ static int btf_type_size(struct btf_type *t)
case BTF_KIND_DATASEC:
return base_size + vlen * sizeof(struct btf_var_secinfo);
default:
pr_debug("Unsupported BTF_KIND:%u\n", BTF_INFO_KIND(t->info));
pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t));
return -EINVAL;
}
}
@ -263,7 +215,7 @@ const struct btf_type *btf__type_by_id(const struct btf *btf, __u32 type_id)
static bool btf_type_is_void(const struct btf_type *t)
{
return t == &btf_void || BTF_INFO_KIND(t->info) == BTF_KIND_FWD;
return t == &btf_void || btf_is_fwd(t);
}
static bool btf_type_is_void_or_null(const struct btf_type *t)
@ -284,7 +236,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
t = btf__type_by_id(btf, type_id);
for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t);
i++) {
switch (BTF_INFO_KIND(t->info)) {
switch (btf_kind(t)) {
case BTF_KIND_INT:
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
@ -303,7 +255,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
type_id = t->type;
break;
case BTF_KIND_ARRAY:
array = (const struct btf_array *)(t + 1);
array = btf_array(t);
if (nelems && array->nelems > UINT32_MAX / nelems)
return -E2BIG;
nelems *= array->nelems;
@ -334,8 +286,7 @@ int btf__resolve_type(const struct btf *btf, __u32 type_id)
t = btf__type_by_id(btf, type_id);
while (depth < MAX_RESOLVE_DEPTH &&
!btf_type_is_void_or_null(t) &&
(IS_MODIFIER(BTF_INFO_KIND(t->info)) ||
IS_VAR(BTF_INFO_KIND(t->info)))) {
(btf_is_mod(t) || btf_is_typedef(t) || btf_is_var(t))) {
type_id = t->type;
t = btf__type_by_id(btf, type_id);
depth++;
@ -554,11 +505,11 @@ static int compare_vsi_off(const void *_a, const void *_b)
static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf,
struct btf_type *t)
{
__u32 size = 0, off = 0, i, vars = BTF_INFO_VLEN(t->info);
__u32 size = 0, off = 0, i, vars = btf_vlen(t);
const char *name = btf__name_by_offset(btf, t->name_off);
const struct btf_type *t_var;
struct btf_var_secinfo *vsi;
struct btf_var *var;
const struct btf_var *var;
int ret;
if (!name) {
@ -574,12 +525,11 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf,
t->size = size;
for (i = 0, vsi = (struct btf_var_secinfo *)(t + 1);
i < vars; i++, vsi++) {
for (i = 0, vsi = btf_var_secinfos(t); i < vars; i++, vsi++) {
t_var = btf__type_by_id(btf, vsi->type);
var = (struct btf_var *)(t_var + 1);
var = btf_var(t_var);
if (BTF_INFO_KIND(t_var->info) != BTF_KIND_VAR) {
if (!btf_is_var(t_var)) {
pr_debug("Non-VAR type seen in section %s\n", name);
return -EINVAL;
}
@ -595,7 +545,8 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf,
ret = bpf_object__variable_offset(obj, name, &off);
if (ret) {
pr_debug("No offset found in symbol table for VAR %s\n", name);
pr_debug("No offset found in symbol table for VAR %s\n",
name);
return -ENOENT;
}
@ -619,7 +570,7 @@ int btf__finalize_data(struct bpf_object *obj, struct btf *btf)
* is section size and global variable offset. We use
* the info from the ELF itself for this purpose.
*/
if (BTF_INFO_KIND(t->info) == BTF_KIND_DATASEC) {
if (btf_is_datasec(t)) {
err = btf_fixup_datasec(obj, btf, t);
if (err)
break;
@ -774,14 +725,13 @@ int btf__get_map_kv_tids(const struct btf *btf, const char *map_name,
return -EINVAL;
}
if (BTF_INFO_KIND(container_type->info) != BTF_KIND_STRUCT ||
BTF_INFO_VLEN(container_type->info) < 2) {
if (!btf_is_struct(container_type) || btf_vlen(container_type) < 2) {
pr_warning("map:%s container_name:%s is an invalid container struct\n",
map_name, container_name);
return -EINVAL;
}
key = (struct btf_member *)(container_type + 1);
key = btf_members(container_type);
value = key + 1;
key_size = btf__resolve_size(btf, key->type);
@ -831,6 +781,9 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext,
/* The start of the info sec (including the __u32 record_size). */
void *info;
if (ext_sec->len == 0)
return 0;
if (ext_sec->off & 0x03) {
pr_debug(".BTF.ext %s section is not aligned to 4 bytes\n",
ext_sec->desc);
@ -934,11 +887,24 @@ static int btf_ext_setup_line_info(struct btf_ext *btf_ext)
return btf_ext_setup_info(btf_ext, &param);
}
static int btf_ext_setup_offset_reloc(struct btf_ext *btf_ext)
{
struct btf_ext_sec_setup_param param = {
.off = btf_ext->hdr->offset_reloc_off,
.len = btf_ext->hdr->offset_reloc_len,
.min_rec_size = sizeof(struct bpf_offset_reloc),
.ext_info = &btf_ext->offset_reloc_info,
.desc = "offset_reloc",
};
return btf_ext_setup_info(btf_ext, &param);
}
static int btf_ext_parse_hdr(__u8 *data, __u32 data_size)
{
const struct btf_ext_header *hdr = (struct btf_ext_header *)data;
if (data_size < offsetof(struct btf_ext_header, func_info_off) ||
if (data_size < offsetofend(struct btf_ext_header, hdr_len) ||
data_size < hdr->hdr_len) {
pr_debug("BTF.ext header not found");
return -EINVAL;
@ -996,6 +962,9 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size)
}
memcpy(btf_ext->data, data, size);
if (btf_ext->hdr->hdr_len <
offsetofend(struct btf_ext_header, line_info_len))
goto done;
err = btf_ext_setup_func_info(btf_ext);
if (err)
goto done;
@ -1004,6 +973,13 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size)
if (err)
goto done;
if (btf_ext->hdr->hdr_len <
offsetofend(struct btf_ext_header, offset_reloc_len))
goto done;
err = btf_ext_setup_offset_reloc(btf_ext);
if (err)
goto done;
done:
if (err) {
btf_ext__free(btf_ext);
@ -1440,10 +1416,9 @@ static struct btf_dedup *btf_dedup_new(struct btf *btf, struct btf_ext *btf_ext,
d->map[0] = 0;
for (i = 1; i <= btf->nr_types; i++) {
struct btf_type *t = d->btf->types[i];
__u16 kind = BTF_INFO_KIND(t->info);
/* VAR and DATASEC are never deduped and are self-canonical */
if (kind == BTF_KIND_VAR || kind == BTF_KIND_DATASEC)
if (btf_is_var(t) || btf_is_datasec(t))
d->map[i] = i;
else
d->map[i] = BTF_UNPROCESSED_ID;
@ -1484,11 +1459,11 @@ static int btf_for_each_str_off(struct btf_dedup *d, str_off_fn_t fn, void *ctx)
if (r)
return r;
switch (BTF_INFO_KIND(t->info)) {
switch (btf_kind(t)) {
case BTF_KIND_STRUCT:
case BTF_KIND_UNION: {
struct btf_member *m = (struct btf_member *)(t + 1);
__u16 vlen = BTF_INFO_VLEN(t->info);
struct btf_member *m = btf_members(t);
__u16 vlen = btf_vlen(t);
for (j = 0; j < vlen; j++) {
r = fn(&m->name_off, ctx);
@ -1499,8 +1474,8 @@ static int btf_for_each_str_off(struct btf_dedup *d, str_off_fn_t fn, void *ctx)
break;
}
case BTF_KIND_ENUM: {
struct btf_enum *m = (struct btf_enum *)(t + 1);
__u16 vlen = BTF_INFO_VLEN(t->info);
struct btf_enum *m = btf_enum(t);
__u16 vlen = btf_vlen(t);
for (j = 0; j < vlen; j++) {
r = fn(&m->name_off, ctx);
@ -1511,8 +1486,8 @@ static int btf_for_each_str_off(struct btf_dedup *d, str_off_fn_t fn, void *ctx)
break;
}
case BTF_KIND_FUNC_PROTO: {
struct btf_param *m = (struct btf_param *)(t + 1);
__u16 vlen = BTF_INFO_VLEN(t->info);
struct btf_param *m = btf_params(t);
__u16 vlen = btf_vlen(t);
for (j = 0; j < vlen; j++) {
r = fn(&m->name_off, ctx);
@ -1801,16 +1776,16 @@ static long btf_hash_enum(struct btf_type *t)
/* Check structural equality of two ENUMs. */
static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)
{
struct btf_enum *m1, *m2;
const struct btf_enum *m1, *m2;
__u16 vlen;
int i;
if (!btf_equal_common(t1, t2))
return false;
vlen = BTF_INFO_VLEN(t1->info);
m1 = (struct btf_enum *)(t1 + 1);
m2 = (struct btf_enum *)(t2 + 1);
vlen = btf_vlen(t1);
m1 = btf_enum(t1);
m2 = btf_enum(t2);
for (i = 0; i < vlen; i++) {
if (m1->name_off != m2->name_off || m1->val != m2->val)
return false;
@ -1822,8 +1797,7 @@ static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)
static inline bool btf_is_enum_fwd(struct btf_type *t)
{
return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM &&
BTF_INFO_VLEN(t->info) == 0;
return btf_is_enum(t) && btf_vlen(t) == 0;
}
static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)
@ -1843,8 +1817,8 @@ static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)
*/
static long btf_hash_struct(struct btf_type *t)
{
struct btf_member *member = (struct btf_member *)(t + 1);
__u32 vlen = BTF_INFO_VLEN(t->info);
const struct btf_member *member = btf_members(t);
__u32 vlen = btf_vlen(t);
long h = btf_hash_common(t);
int i;
@ -1864,16 +1838,16 @@ static long btf_hash_struct(struct btf_type *t)
*/
static bool btf_shallow_equal_struct(struct btf_type *t1, struct btf_type *t2)
{
struct btf_member *m1, *m2;
const struct btf_member *m1, *m2;
__u16 vlen;
int i;
if (!btf_equal_common(t1, t2))
return false;
vlen = BTF_INFO_VLEN(t1->info);
m1 = (struct btf_member *)(t1 + 1);
m2 = (struct btf_member *)(t2 + 1);
vlen = btf_vlen(t1);
m1 = btf_members(t1);
m2 = btf_members(t2);
for (i = 0; i < vlen; i++) {
if (m1->name_off != m2->name_off || m1->offset != m2->offset)
return false;
@ -1890,7 +1864,7 @@ static bool btf_shallow_equal_struct(struct btf_type *t1, struct btf_type *t2)
*/
static long btf_hash_array(struct btf_type *t)
{
struct btf_array *info = (struct btf_array *)(t + 1);
const struct btf_array *info = btf_array(t);
long h = btf_hash_common(t);
h = hash_combine(h, info->type);
@ -1908,13 +1882,13 @@ static long btf_hash_array(struct btf_type *t)
*/
static bool btf_equal_array(struct btf_type *t1, struct btf_type *t2)
{
struct btf_array *info1, *info2;
const struct btf_array *info1, *info2;
if (!btf_equal_common(t1, t2))
return false;
info1 = (struct btf_array *)(t1 + 1);
info2 = (struct btf_array *)(t2 + 1);
info1 = btf_array(t1);
info2 = btf_array(t2);
return info1->type == info2->type &&
info1->index_type == info2->index_type &&
info1->nelems == info2->nelems;
@ -1927,14 +1901,10 @@ static bool btf_equal_array(struct btf_type *t1, struct btf_type *t2)
*/
static bool btf_compat_array(struct btf_type *t1, struct btf_type *t2)
{
struct btf_array *info1, *info2;
if (!btf_equal_common(t1, t2))
return false;
info1 = (struct btf_array *)(t1 + 1);
info2 = (struct btf_array *)(t2 + 1);
return info1->nelems == info2->nelems;
return btf_array(t1)->nelems == btf_array(t2)->nelems;
}
/*
@ -1944,8 +1914,8 @@ static bool btf_compat_array(struct btf_type *t1, struct btf_type *t2)
*/
static long btf_hash_fnproto(struct btf_type *t)
{
struct btf_param *member = (struct btf_param *)(t + 1);
__u16 vlen = BTF_INFO_VLEN(t->info);
const struct btf_param *member = btf_params(t);
__u16 vlen = btf_vlen(t);
long h = btf_hash_common(t);
int i;
@ -1966,16 +1936,16 @@ static long btf_hash_fnproto(struct btf_type *t)
*/
static bool btf_equal_fnproto(struct btf_type *t1, struct btf_type *t2)
{
struct btf_param *m1, *m2;
const struct btf_param *m1, *m2;
__u16 vlen;
int i;
if (!btf_equal_common(t1, t2))
return false;
vlen = BTF_INFO_VLEN(t1->info);
m1 = (struct btf_param *)(t1 + 1);
m2 = (struct btf_param *)(t2 + 1);
vlen = btf_vlen(t1);
m1 = btf_params(t1);
m2 = btf_params(t2);
for (i = 0; i < vlen; i++) {
if (m1->name_off != m2->name_off || m1->type != m2->type)
return false;
@ -1992,7 +1962,7 @@ static bool btf_equal_fnproto(struct btf_type *t1, struct btf_type *t2)
*/
static bool btf_compat_fnproto(struct btf_type *t1, struct btf_type *t2)
{
struct btf_param *m1, *m2;
const struct btf_param *m1, *m2;
__u16 vlen;
int i;
@ -2000,9 +1970,9 @@ static bool btf_compat_fnproto(struct btf_type *t1, struct btf_type *t2)
if (t1->name_off != t2->name_off || t1->info != t2->info)
return false;
vlen = BTF_INFO_VLEN(t1->info);
m1 = (struct btf_param *)(t1 + 1);
m2 = (struct btf_param *)(t2 + 1);
vlen = btf_vlen(t1);
m1 = btf_params(t1);
m2 = btf_params(t2);
for (i = 0; i < vlen; i++) {
if (m1->name_off != m2->name_off)
return false;
@ -2028,7 +1998,7 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
__u32 cand_id;
long h;
switch (BTF_INFO_KIND(t->info)) {
switch (btf_kind(t)) {
case BTF_KIND_CONST:
case BTF_KIND_VOLATILE:
case BTF_KIND_RESTRICT:
@ -2141,13 +2111,13 @@ static uint32_t resolve_fwd_id(struct btf_dedup *d, uint32_t type_id)
{
__u32 orig_type_id = type_id;
if (BTF_INFO_KIND(d->btf->types[type_id]->info) != BTF_KIND_FWD)
if (!btf_is_fwd(d->btf->types[type_id]))
return type_id;
while (is_type_mapped(d, type_id) && d->map[type_id] != type_id)
type_id = d->map[type_id];
if (BTF_INFO_KIND(d->btf->types[type_id]->info) != BTF_KIND_FWD)
if (!btf_is_fwd(d->btf->types[type_id]))
return type_id;
return orig_type_id;
@ -2156,7 +2126,7 @@ static uint32_t resolve_fwd_id(struct btf_dedup *d, uint32_t type_id)
static inline __u16 btf_fwd_kind(struct btf_type *t)
{
return BTF_INFO_KFLAG(t->info) ? BTF_KIND_UNION : BTF_KIND_STRUCT;
return btf_kflag(t) ? BTF_KIND_UNION : BTF_KIND_STRUCT;
}
/*
@ -2277,8 +2247,8 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
cand_type = d->btf->types[cand_id];
canon_type = d->btf->types[canon_id];
cand_kind = BTF_INFO_KIND(cand_type->info);
canon_kind = BTF_INFO_KIND(canon_type->info);
cand_kind = btf_kind(cand_type);
canon_kind = btf_kind(canon_type);
if (cand_type->name_off != canon_type->name_off)
return 0;
@ -2327,12 +2297,12 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
return btf_dedup_is_equiv(d, cand_type->type, canon_type->type);
case BTF_KIND_ARRAY: {
struct btf_array *cand_arr, *canon_arr;
const struct btf_array *cand_arr, *canon_arr;
if (!btf_compat_array(cand_type, canon_type))
return 0;
cand_arr = (struct btf_array *)(cand_type + 1);
canon_arr = (struct btf_array *)(canon_type + 1);
cand_arr = btf_array(cand_type);
canon_arr = btf_array(canon_type);
eq = btf_dedup_is_equiv(d,
cand_arr->index_type, canon_arr->index_type);
if (eq <= 0)
@ -2342,14 +2312,14 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
case BTF_KIND_STRUCT:
case BTF_KIND_UNION: {
struct btf_member *cand_m, *canon_m;
const struct btf_member *cand_m, *canon_m;
__u16 vlen;
if (!btf_shallow_equal_struct(cand_type, canon_type))
return 0;
vlen = BTF_INFO_VLEN(cand_type->info);
cand_m = (struct btf_member *)(cand_type + 1);
canon_m = (struct btf_member *)(canon_type + 1);
vlen = btf_vlen(cand_type);
cand_m = btf_members(cand_type);
canon_m = btf_members(canon_type);
for (i = 0; i < vlen; i++) {
eq = btf_dedup_is_equiv(d, cand_m->type, canon_m->type);
if (eq <= 0)
@ -2362,7 +2332,7 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
}
case BTF_KIND_FUNC_PROTO: {
struct btf_param *cand_p, *canon_p;
const struct btf_param *cand_p, *canon_p;
__u16 vlen;
if (!btf_compat_fnproto(cand_type, canon_type))
@ -2370,9 +2340,9 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
eq = btf_dedup_is_equiv(d, cand_type->type, canon_type->type);
if (eq <= 0)
return eq;
vlen = BTF_INFO_VLEN(cand_type->info);
cand_p = (struct btf_param *)(cand_type + 1);
canon_p = (struct btf_param *)(canon_type + 1);
vlen = btf_vlen(cand_type);
cand_p = btf_params(cand_type);
canon_p = btf_params(canon_type);
for (i = 0; i < vlen; i++) {
eq = btf_dedup_is_equiv(d, cand_p->type, canon_p->type);
if (eq <= 0)
@ -2427,8 +2397,8 @@ static void btf_dedup_merge_hypot_map(struct btf_dedup *d)
targ_type_id = d->hypot_map[cand_type_id];
t_id = resolve_type_id(d, targ_type_id);
c_id = resolve_type_id(d, cand_type_id);
t_kind = BTF_INFO_KIND(d->btf->types[t_id]->info);
c_kind = BTF_INFO_KIND(d->btf->types[c_id]->info);
t_kind = btf_kind(d->btf->types[t_id]);
c_kind = btf_kind(d->btf->types[c_id]);
/*
* Resolve FWD into STRUCT/UNION.
* It's ok to resolve FWD into STRUCT/UNION that's not yet
@ -2497,7 +2467,7 @@ static int btf_dedup_struct_type(struct btf_dedup *d, __u32 type_id)
return 0;
t = d->btf->types[type_id];
kind = BTF_INFO_KIND(t->info);
kind = btf_kind(t);
if (kind != BTF_KIND_STRUCT && kind != BTF_KIND_UNION)
return 0;
@ -2592,7 +2562,7 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
t = d->btf->types[type_id];
d->map[type_id] = BTF_IN_PROGRESS_ID;
switch (BTF_INFO_KIND(t->info)) {
switch (btf_kind(t)) {
case BTF_KIND_CONST:
case BTF_KIND_VOLATILE:
case BTF_KIND_RESTRICT:
@ -2616,7 +2586,7 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
break;
case BTF_KIND_ARRAY: {
struct btf_array *info = (struct btf_array *)(t + 1);
struct btf_array *info = btf_array(t);
ref_type_id = btf_dedup_ref_type(d, info->type);
if (ref_type_id < 0)
@ -2650,8 +2620,8 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
return ref_type_id;
t->type = ref_type_id;
vlen = BTF_INFO_VLEN(t->info);
param = (struct btf_param *)(t + 1);
vlen = btf_vlen(t);
param = btf_params(t);
for (i = 0; i < vlen; i++) {
ref_type_id = btf_dedup_ref_type(d, param->type);
if (ref_type_id < 0)
@ -2791,7 +2761,7 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id)
struct btf_type *t = d->btf->types[type_id];
int i, r;
switch (BTF_INFO_KIND(t->info)) {
switch (btf_kind(t)) {
case BTF_KIND_INT:
case BTF_KIND_ENUM:
break;
@ -2811,7 +2781,7 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id)
break;
case BTF_KIND_ARRAY: {
struct btf_array *arr_info = (struct btf_array *)(t + 1);
struct btf_array *arr_info = btf_array(t);
r = btf_dedup_remap_type_id(d, arr_info->type);
if (r < 0)
@ -2826,8 +2796,8 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id)
case BTF_KIND_STRUCT:
case BTF_KIND_UNION: {
struct btf_member *member = (struct btf_member *)(t + 1);
__u16 vlen = BTF_INFO_VLEN(t->info);
struct btf_member *member = btf_members(t);
__u16 vlen = btf_vlen(t);
for (i = 0; i < vlen; i++) {
r = btf_dedup_remap_type_id(d, member->type);
@ -2840,8 +2810,8 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id)
}
case BTF_KIND_FUNC_PROTO: {
struct btf_param *param = (struct btf_param *)(t + 1);
__u16 vlen = BTF_INFO_VLEN(t->info);
struct btf_param *param = btf_params(t);
__u16 vlen = btf_vlen(t);
r = btf_dedup_remap_type_id(d, t->type);
if (r < 0)
@ -2859,8 +2829,8 @@ static int btf_dedup_remap_type(struct btf_dedup *d, __u32 type_id)
}
case BTF_KIND_DATASEC: {
struct btf_var_secinfo *var = (struct btf_var_secinfo *)(t + 1);
__u16 vlen = BTF_INFO_VLEN(t->info);
struct btf_var_secinfo *var = btf_var_secinfos(t);
__u16 vlen = btf_vlen(t);
for (i = 0; i < vlen; i++) {
r = btf_dedup_remap_type_id(d, var->type);

View File

@ -5,6 +5,7 @@
#define __LIBBPF_BTF_H
#include <stdarg.h>
#include <linux/btf.h>
#include <linux/types.h>
#ifdef __cplusplus
@ -57,6 +58,10 @@ struct btf_ext_header {
__u32 func_info_len;
__u32 line_info_off;
__u32 line_info_len;
/* optional part of .BTF.ext header */
__u32 offset_reloc_off;
__u32 offset_reloc_len;
};
LIBBPF_API void btf__free(struct btf *btf);
@ -120,6 +125,183 @@ LIBBPF_API void btf_dump__free(struct btf_dump *d);
LIBBPF_API int btf_dump__dump_type(struct btf_dump *d, __u32 id);
/*
* A set of helpers for easier BTF types handling
*/
static inline __u16 btf_kind(const struct btf_type *t)
{
return BTF_INFO_KIND(t->info);
}
static inline __u16 btf_vlen(const struct btf_type *t)
{
return BTF_INFO_VLEN(t->info);
}
static inline bool btf_kflag(const struct btf_type *t)
{
return BTF_INFO_KFLAG(t->info);
}
static inline bool btf_is_int(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_INT;
}
static inline bool btf_is_ptr(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_PTR;
}
static inline bool btf_is_array(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_ARRAY;
}
static inline bool btf_is_struct(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_STRUCT;
}
static inline bool btf_is_union(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_UNION;
}
static inline bool btf_is_composite(const struct btf_type *t)
{
__u16 kind = btf_kind(t);
return kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION;
}
static inline bool btf_is_enum(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_ENUM;
}
static inline bool btf_is_fwd(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_FWD;
}
static inline bool btf_is_typedef(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_TYPEDEF;
}
static inline bool btf_is_volatile(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_VOLATILE;
}
static inline bool btf_is_const(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_CONST;
}
static inline bool btf_is_restrict(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_RESTRICT;
}
static inline bool btf_is_mod(const struct btf_type *t)
{
__u16 kind = btf_kind(t);
return kind == BTF_KIND_VOLATILE ||
kind == BTF_KIND_CONST ||
kind == BTF_KIND_RESTRICT;
}
static inline bool btf_is_func(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_FUNC;
}
static inline bool btf_is_func_proto(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_FUNC_PROTO;
}
static inline bool btf_is_var(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_VAR;
}
static inline bool btf_is_datasec(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_DATASEC;
}
static inline __u8 btf_int_encoding(const struct btf_type *t)
{
return BTF_INT_ENCODING(*(__u32 *)(t + 1));
}
static inline __u8 btf_int_offset(const struct btf_type *t)
{
return BTF_INT_OFFSET(*(__u32 *)(t + 1));
}
static inline __u8 btf_int_bits(const struct btf_type *t)
{
return BTF_INT_BITS(*(__u32 *)(t + 1));
}
static inline struct btf_array *btf_array(const struct btf_type *t)
{
return (struct btf_array *)(t + 1);
}
static inline struct btf_enum *btf_enum(const struct btf_type *t)
{
return (struct btf_enum *)(t + 1);
}
static inline struct btf_member *btf_members(const struct btf_type *t)
{
return (struct btf_member *)(t + 1);
}
/* Get bit offset of a member with specified index. */
static inline __u32 btf_member_bit_offset(const struct btf_type *t,
__u32 member_idx)
{
const struct btf_member *m = btf_members(t) + member_idx;
bool kflag = btf_kflag(t);
return kflag ? BTF_MEMBER_BIT_OFFSET(m->offset) : m->offset;
}
/*
* Get bitfield size of a member, assuming t is BTF_KIND_STRUCT or
* BTF_KIND_UNION. If member is not a bitfield, zero is returned.
*/
static inline __u32 btf_member_bitfield_size(const struct btf_type *t,
__u32 member_idx)
{
const struct btf_member *m = btf_members(t) + member_idx;
bool kflag = btf_kflag(t);
return kflag ? BTF_MEMBER_BITFIELD_SIZE(m->offset) : 0;
}
static inline struct btf_param *btf_params(const struct btf_type *t)
{
return (struct btf_param *)(t + 1);
}
static inline struct btf_var *btf_var(const struct btf_type *t)
{
return (struct btf_var *)(t + 1);
}
static inline struct btf_var_secinfo *
btf_var_secinfos(const struct btf_type *t)
{
return (struct btf_var_secinfo *)(t + 1);
}
#ifdef __cplusplus
} /* extern "C" */
#endif

View File

@ -100,21 +100,6 @@ static bool str_equal_fn(const void *a, const void *b, void *ctx)
return strcmp(a, b) == 0;
}
static __u16 btf_kind_of(const struct btf_type *t)
{
return BTF_INFO_KIND(t->info);
}
static __u16 btf_vlen_of(const struct btf_type *t)
{
return BTF_INFO_VLEN(t->info);
}
static bool btf_kflag_of(const struct btf_type *t)
{
return BTF_INFO_KFLAG(t->info);
}
static const char *btf_name_of(const struct btf_dump *d, __u32 name_off)
{
return btf__name_by_offset(d->btf, name_off);
@ -349,7 +334,7 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
*/
struct btf_dump_type_aux_state *tstate = &d->type_states[id];
const struct btf_type *t;
__u16 kind, vlen;
__u16 vlen;
int err, i;
/* return true, letting typedefs know that it's ok to be emitted */
@ -357,18 +342,16 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
return 1;
t = btf__type_by_id(d->btf, id);
kind = btf_kind_of(t);
if (tstate->order_state == ORDERING) {
/* type loop, but resolvable through fwd declaration */
if ((kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION) &&
through_ptr && t->name_off != 0)
if (btf_is_composite(t) && through_ptr && t->name_off != 0)
return 0;
pr_warning("unsatisfiable type cycle, id:[%u]\n", id);
return -ELOOP;
}
switch (kind) {
switch (btf_kind(t)) {
case BTF_KIND_INT:
tstate->order_state = ORDERED;
return 0;
@ -378,14 +361,12 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
tstate->order_state = ORDERED;
return err;
case BTF_KIND_ARRAY: {
const struct btf_array *a = (void *)(t + 1);
case BTF_KIND_ARRAY:
return btf_dump_order_type(d, btf_array(t)->type, through_ptr);
return btf_dump_order_type(d, a->type, through_ptr);
}
case BTF_KIND_STRUCT:
case BTF_KIND_UNION: {
const struct btf_member *m = (void *)(t + 1);
const struct btf_member *m = btf_members(t);
/*
* struct/union is part of strong link, only if it's embedded
* (so no ptr in a path) or it's anonymous (so has to be
@ -396,7 +377,7 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
tstate->order_state = ORDERING;
vlen = btf_vlen_of(t);
vlen = btf_vlen(t);
for (i = 0; i < vlen; i++, m++) {
err = btf_dump_order_type(d, m->type, false);
if (err < 0)
@ -447,7 +428,7 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
return btf_dump_order_type(d, t->type, through_ptr);
case BTF_KIND_FUNC_PROTO: {
const struct btf_param *p = (void *)(t + 1);
const struct btf_param *p = btf_params(t);
bool is_strong;
err = btf_dump_order_type(d, t->type, through_ptr);
@ -455,7 +436,7 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
return err;
is_strong = err > 0;
vlen = btf_vlen_of(t);
vlen = btf_vlen(t);
for (i = 0; i < vlen; i++, p++) {
err = btf_dump_order_type(d, p->type, through_ptr);
if (err < 0)
@ -553,7 +534,7 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
return;
t = btf__type_by_id(d->btf, id);
kind = btf_kind_of(t);
kind = btf_kind(t);
if (top_level_def && t->name_off == 0) {
pr_warning("unexpected nameless definition, id:[%u]\n", id);
@ -618,12 +599,9 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
case BTF_KIND_RESTRICT:
btf_dump_emit_type(d, t->type, cont_id);
break;
case BTF_KIND_ARRAY: {
const struct btf_array *a = (void *)(t + 1);
btf_dump_emit_type(d, a->type, cont_id);
case BTF_KIND_ARRAY:
btf_dump_emit_type(d, btf_array(t)->type, cont_id);
break;
}
case BTF_KIND_FWD:
btf_dump_emit_fwd_def(d, id, t);
btf_dump_printf(d, ";\n\n");
@ -656,8 +634,8 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
* applicable
*/
if (top_level_def || t->name_off == 0) {
const struct btf_member *m = (void *)(t + 1);
__u16 vlen = btf_vlen_of(t);
const struct btf_member *m = btf_members(t);
__u16 vlen = btf_vlen(t);
int i, new_cont_id;
new_cont_id = t->name_off == 0 ? cont_id : id;
@ -678,8 +656,8 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
}
break;
case BTF_KIND_FUNC_PROTO: {
const struct btf_param *p = (void *)(t + 1);
__u16 vlen = btf_vlen_of(t);
const struct btf_param *p = btf_params(t);
__u16 vlen = btf_vlen(t);
int i;
btf_dump_emit_type(d, t->type, cont_id);
@ -696,7 +674,7 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
static int btf_align_of(const struct btf *btf, __u32 id)
{
const struct btf_type *t = btf__type_by_id(btf, id);
__u16 kind = btf_kind_of(t);
__u16 kind = btf_kind(t);
switch (kind) {
case BTF_KIND_INT:
@ -709,15 +687,12 @@ static int btf_align_of(const struct btf *btf, __u32 id)
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
return btf_align_of(btf, t->type);
case BTF_KIND_ARRAY: {
const struct btf_array *a = (void *)(t + 1);
return btf_align_of(btf, a->type);
}
case BTF_KIND_ARRAY:
return btf_align_of(btf, btf_array(t)->type);
case BTF_KIND_STRUCT:
case BTF_KIND_UNION: {
const struct btf_member *m = (void *)(t + 1);
__u16 vlen = btf_vlen_of(t);
const struct btf_member *m = btf_members(t);
__u16 vlen = btf_vlen(t);
int i, align = 1;
for (i = 0; i < vlen; i++, m++)
@ -726,7 +701,7 @@ static int btf_align_of(const struct btf *btf, __u32 id)
return align;
}
default:
pr_warning("unsupported BTF_KIND:%u\n", btf_kind_of(t));
pr_warning("unsupported BTF_KIND:%u\n", btf_kind(t));
return 1;
}
}
@ -737,20 +712,18 @@ static bool btf_is_struct_packed(const struct btf *btf, __u32 id,
const struct btf_member *m;
int align, i, bit_sz;
__u16 vlen;
bool kflag;
align = btf_align_of(btf, id);
/* size of a non-packed struct has to be a multiple of its alignment*/
if (t->size % align)
return true;
m = (void *)(t + 1);
kflag = btf_kflag_of(t);
vlen = btf_vlen_of(t);
m = btf_members(t);
vlen = btf_vlen(t);
/* all non-bitfield fields have to be naturally aligned */
for (i = 0; i < vlen; i++, m++) {
align = btf_align_of(btf, m->type);
bit_sz = kflag ? BTF_MEMBER_BITFIELD_SIZE(m->offset) : 0;
bit_sz = btf_member_bitfield_size(t, i);
if (bit_sz == 0 && m->offset % (8 * align) != 0)
return true;
}
@ -807,7 +780,7 @@ static void btf_dump_emit_struct_fwd(struct btf_dump *d, __u32 id,
const struct btf_type *t)
{
btf_dump_printf(d, "%s %s",
btf_kind_of(t) == BTF_KIND_STRUCT ? "struct" : "union",
btf_is_struct(t) ? "struct" : "union",
btf_dump_type_name(d, id));
}
@ -816,12 +789,11 @@ static void btf_dump_emit_struct_def(struct btf_dump *d,
const struct btf_type *t,
int lvl)
{
const struct btf_member *m = (void *)(t + 1);
bool kflag = btf_kflag_of(t), is_struct;
const struct btf_member *m = btf_members(t);
bool is_struct = btf_is_struct(t);
int align, i, packed, off = 0;
__u16 vlen = btf_vlen_of(t);
__u16 vlen = btf_vlen(t);
is_struct = btf_kind_of(t) == BTF_KIND_STRUCT;
packed = is_struct ? btf_is_struct_packed(d->btf, id, t) : 0;
align = packed ? 1 : btf_align_of(d->btf, id);
@ -835,8 +807,8 @@ static void btf_dump_emit_struct_def(struct btf_dump *d,
int m_off, m_sz;
fname = btf_name_of(d, m->name_off);
m_sz = kflag ? BTF_MEMBER_BITFIELD_SIZE(m->offset) : 0;
m_off = kflag ? BTF_MEMBER_BIT_OFFSET(m->offset) : m->offset;
m_sz = btf_member_bitfield_size(t, i);
m_off = btf_member_bit_offset(t, i);
align = packed ? 1 : btf_align_of(d->btf, m->type);
btf_dump_emit_bit_padding(d, off, m_off, m_sz, align, lvl + 1);
@ -870,8 +842,8 @@ static void btf_dump_emit_enum_def(struct btf_dump *d, __u32 id,
const struct btf_type *t,
int lvl)
{
const struct btf_enum *v = (void *)(t+1);
__u16 vlen = btf_vlen_of(t);
const struct btf_enum *v = btf_enum(t);
__u16 vlen = btf_vlen(t);
const char *name;
size_t dup_cnt;
int i;
@ -905,7 +877,7 @@ static void btf_dump_emit_fwd_def(struct btf_dump *d, __u32 id,
{
const char *name = btf_dump_type_name(d, id);
if (btf_kflag_of(t))
if (btf_kflag(t))
btf_dump_printf(d, "union %s", name);
else
btf_dump_printf(d, "struct %s", name);
@ -987,7 +959,6 @@ static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
struct id_stack decl_stack;
const struct btf_type *t;
int err, stack_start;
__u16 kind;
stack_start = d->decl_stack_cnt;
for (;;) {
@ -1008,8 +979,7 @@ static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
break;
t = btf__type_by_id(d->btf, id);
kind = btf_kind_of(t);
switch (kind) {
switch (btf_kind(t)) {
case BTF_KIND_PTR:
case BTF_KIND_VOLATILE:
case BTF_KIND_CONST:
@ -1017,12 +987,9 @@ static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
case BTF_KIND_FUNC_PROTO:
id = t->type;
break;
case BTF_KIND_ARRAY: {
const struct btf_array *a = (void *)(t + 1);
id = a->type;
case BTF_KIND_ARRAY:
id = btf_array(t)->type;
break;
}
case BTF_KIND_INT:
case BTF_KIND_ENUM:
case BTF_KIND_FWD:
@ -1032,7 +999,7 @@ static void btf_dump_emit_type_decl(struct btf_dump *d, __u32 id,
goto done;
default:
pr_warning("unexpected type in decl chain, kind:%u, id:[%u]\n",
kind, id);
btf_kind(t), id);
goto done;
}
}
@ -1070,7 +1037,7 @@ static void btf_dump_emit_mods(struct btf_dump *d, struct id_stack *decl_stack)
id = decl_stack->ids[decl_stack->cnt - 1];
t = btf__type_by_id(d->btf, id);
switch (btf_kind_of(t)) {
switch (btf_kind(t)) {
case BTF_KIND_VOLATILE:
btf_dump_printf(d, "volatile ");
break;
@ -1087,20 +1054,6 @@ static void btf_dump_emit_mods(struct btf_dump *d, struct id_stack *decl_stack)
}
}
static bool btf_is_mod_kind(const struct btf *btf, __u32 id)
{
const struct btf_type *t = btf__type_by_id(btf, id);
switch (btf_kind_of(t)) {
case BTF_KIND_VOLATILE:
case BTF_KIND_CONST:
case BTF_KIND_RESTRICT:
return true;
default:
return false;
}
}
static void btf_dump_emit_name(const struct btf_dump *d,
const char *name, bool last_was_ptr)
{
@ -1139,7 +1092,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
}
t = btf__type_by_id(d->btf, id);
kind = btf_kind_of(t);
kind = btf_kind(t);
switch (kind) {
case BTF_KIND_INT:
@ -1185,7 +1138,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
btf_dump_printf(d, " restrict");
break;
case BTF_KIND_ARRAY: {
const struct btf_array *a = (void *)(t + 1);
const struct btf_array *a = btf_array(t);
const struct btf_type *next_t;
__u32 next_id;
bool multidim;
@ -1201,7 +1154,8 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
*/
while (decls->cnt) {
next_id = decls->ids[decls->cnt - 1];
if (btf_is_mod_kind(d->btf, next_id))
next_t = btf__type_by_id(d->btf, next_id);
if (btf_is_mod(next_t))
decls->cnt--;
else
break;
@ -1214,7 +1168,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
}
next_t = btf__type_by_id(d->btf, next_id);
multidim = btf_kind_of(next_t) == BTF_KIND_ARRAY;
multidim = btf_is_array(next_t);
/* we need space if we have named non-pointer */
if (fname[0] && !last_was_ptr)
btf_dump_printf(d, " ");
@ -1228,8 +1182,8 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
return;
}
case BTF_KIND_FUNC_PROTO: {
const struct btf_param *p = (void *)(t + 1);
__u16 vlen = btf_vlen_of(t);
const struct btf_param *p = btf_params(t);
__u16 vlen = btf_vlen(t);
int i;
btf_dump_emit_mods(d, decls);

File diff suppressed because it is too large Load Diff

View File

@ -92,6 +92,7 @@ LIBBPF_API void bpf_object__close(struct bpf_object *object);
struct bpf_object_load_attr {
struct bpf_object *obj;
int log_level;
const char *target_btf_path;
};
/* Load/unload object into/from kernel */

View File

@ -29,6 +29,10 @@
#ifndef max
# define max(x, y) ((x) < (y) ? (y) : (x))
#endif
#ifndef offsetofend
# define offsetofend(TYPE, FIELD) \
(offsetof(TYPE, FIELD) + sizeof(((TYPE *)0)->FIELD))
#endif
extern void libbpf_print(enum libbpf_print_level level,
const char *format, ...)
@ -46,4 +50,105 @@ do { \
int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
const char *str_sec, size_t str_len);
struct btf_ext_info {
/*
* info points to the individual info section (e.g. func_info and
* line_info) from the .BTF.ext. It does not include the __u32 rec_size.
*/
void *info;
__u32 rec_size;
__u32 len;
};
#define for_each_btf_ext_sec(seg, sec) \
for (sec = (seg)->info; \
(void *)sec < (seg)->info + (seg)->len; \
sec = (void *)sec + sizeof(struct btf_ext_info_sec) + \
(seg)->rec_size * sec->num_info)
#define for_each_btf_ext_rec(seg, sec, i, rec) \
for (i = 0, rec = (void *)&(sec)->data; \
i < (sec)->num_info; \
i++, rec = (void *)rec + (seg)->rec_size)
struct btf_ext {
union {
struct btf_ext_header *hdr;
void *data;
};
struct btf_ext_info func_info;
struct btf_ext_info line_info;
struct btf_ext_info offset_reloc_info;
__u32 data_size;
};
struct btf_ext_info_sec {
__u32 sec_name_off;
__u32 num_info;
/* Followed by num_info * record_size number of bytes */
__u8 data[0];
};
/* The minimum bpf_func_info checked by the loader */
struct bpf_func_info_min {
__u32 insn_off;
__u32 type_id;
};
/* The minimum bpf_line_info checked by the loader */
struct bpf_line_info_min {
__u32 insn_off;
__u32 file_name_off;
__u32 line_off;
__u32 line_col;
};
/* The minimum bpf_offset_reloc checked by the loader
*
* Offset relocation captures the following data:
* - insn_off - instruction offset (in bytes) within a BPF program that needs
* its insn->imm field to be relocated with actual offset;
* - type_id - BTF type ID of the "root" (containing) entity of a relocatable
* offset;
* - access_str_off - offset into corresponding .BTF string section. String
* itself encodes an accessed field using a sequence of field and array
* indicies, separated by colon (:). It's conceptually very close to LLVM's
* getelementptr ([0]) instruction's arguments for identifying offset to
* a field.
*
* Example to provide a better feel.
*
* struct sample {
* int a;
* struct {
* int b[10];
* };
* };
*
* struct sample *s = ...;
* int x = &s->a; // encoded as "0:0" (a is field #0)
* int y = &s->b[5]; // encoded as "0:1:0:5" (anon struct is field #1,
* // b is field #0 inside anon struct, accessing elem #5)
* int z = &s[10]->b; // encoded as "10:1" (ptr is used as an array)
*
* type_id for all relocs in this example will capture BTF type id of
* `struct sample`.
*
* Such relocation is emitted when using __builtin_preserve_access_index()
* Clang built-in, passing expression that captures field address, e.g.:
*
* bpf_probe_read(&dst, sizeof(dst),
* __builtin_preserve_access_index(&src->a.b.c));
*
* In this case Clang will emit offset relocation recording necessary data to
* be able to find offset of embedded `a.b.c` field within `src` struct.
*
* [0] https://llvm.org/docs/LangRef.html#getelementptr-instruction
*/
struct bpf_offset_reloc {
__u32 insn_off;
__u32 type_id;
__u32 access_str_off;
};
#endif /* __LIBBPF_LIBBPF_INTERNAL_H */

View File

@ -504,4 +504,24 @@ struct pt_regs;
(void *)(PT_REGS_FP(ctx) + sizeof(ip))); })
#endif
/*
* BPF_CORE_READ abstracts away bpf_probe_read() call and captures offset
* relocation for source address using __builtin_preserve_access_index()
* built-in, provided by Clang.
*
* __builtin_preserve_access_index() takes as an argument an expression of
* taking an address of a field within struct/union. It makes compiler emit
* a relocation, which records BTF type ID describing root struct/union and an
* accessor string which describes exact embedded field that was used to take
* an address. See detailed description of this relocation format and
* semantics in comments to struct bpf_offset_reloc in libbpf_internal.h.
*
* This relocation allows libbpf to adjust BPF instruction to use correct
* actual field offset, based on target kernel BTF type that matches original
* (local) BTF, used to record relocation.
*/
#define BPF_CORE_READ(dst, src) \
bpf_probe_read((dst), sizeof(*(src)), \
__builtin_preserve_access_index(src))
#endif

View File

@ -0,0 +1,385 @@
// SPDX-License-Identifier: GPL-2.0
#include <test_progs.h>
#include "progs/core_reloc_types.h"
#define STRUCT_TO_CHAR_PTR(struct_name) (const char *)&(struct struct_name)
#define FLAVORS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
.a = 42, \
.b = 0xc001, \
.c = 0xbeef, \
}
#define FLAVORS_CASE_COMMON(name) \
.case_name = #name, \
.bpf_obj_file = "test_core_reloc_flavors.o", \
.btf_src_file = "btf__core_reloc_" #name ".o" \
#define FLAVORS_CASE(name) { \
FLAVORS_CASE_COMMON(name), \
.input = FLAVORS_DATA(core_reloc_##name), \
.input_len = sizeof(struct core_reloc_##name), \
.output = FLAVORS_DATA(core_reloc_flavors), \
.output_len = sizeof(struct core_reloc_flavors), \
}
#define FLAVORS_ERR_CASE(name) { \
FLAVORS_CASE_COMMON(name), \
.fails = true, \
}
#define NESTING_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
.a = { .a = { .a = 42 } }, \
.b = { .b = { .b = 0xc001 } }, \
}
#define NESTING_CASE_COMMON(name) \
.case_name = #name, \
.bpf_obj_file = "test_core_reloc_nesting.o", \
.btf_src_file = "btf__core_reloc_" #name ".o"
#define NESTING_CASE(name) { \
NESTING_CASE_COMMON(name), \
.input = NESTING_DATA(core_reloc_##name), \
.input_len = sizeof(struct core_reloc_##name), \
.output = NESTING_DATA(core_reloc_nesting), \
.output_len = sizeof(struct core_reloc_nesting) \
}
#define NESTING_ERR_CASE(name) { \
NESTING_CASE_COMMON(name), \
.fails = true, \
}
#define ARRAYS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
.a = { [2] = 1 }, \
.b = { [1] = { [2] = { [3] = 2 } } }, \
.c = { [1] = { .c = 3 } }, \
.d = { [0] = { [0] = { .d = 4 } } }, \
}
#define ARRAYS_CASE_COMMON(name) \
.case_name = #name, \
.bpf_obj_file = "test_core_reloc_arrays.o", \
.btf_src_file = "btf__core_reloc_" #name ".o"
#define ARRAYS_CASE(name) { \
ARRAYS_CASE_COMMON(name), \
.input = ARRAYS_DATA(core_reloc_##name), \
.input_len = sizeof(struct core_reloc_##name), \
.output = STRUCT_TO_CHAR_PTR(core_reloc_arrays_output) { \
.a2 = 1, \
.b123 = 2, \
.c1c = 3, \
.d00d = 4, \
}, \
.output_len = sizeof(struct core_reloc_arrays_output) \
}
#define ARRAYS_ERR_CASE(name) { \
ARRAYS_CASE_COMMON(name), \
.fails = true, \
}
#define PRIMITIVES_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
.a = 1, \
.b = 2, \
.c = 3, \
.d = (void *)4, \
.f = (void *)5, \
}
#define PRIMITIVES_CASE_COMMON(name) \
.case_name = #name, \
.bpf_obj_file = "test_core_reloc_primitives.o", \
.btf_src_file = "btf__core_reloc_" #name ".o"
#define PRIMITIVES_CASE(name) { \
PRIMITIVES_CASE_COMMON(name), \
.input = PRIMITIVES_DATA(core_reloc_##name), \
.input_len = sizeof(struct core_reloc_##name), \
.output = PRIMITIVES_DATA(core_reloc_primitives), \
.output_len = sizeof(struct core_reloc_primitives), \
}
#define PRIMITIVES_ERR_CASE(name) { \
PRIMITIVES_CASE_COMMON(name), \
.fails = true, \
}
#define MODS_CASE(name) { \
.case_name = #name, \
.bpf_obj_file = "test_core_reloc_mods.o", \
.btf_src_file = "btf__core_reloc_" #name ".o", \
.input = STRUCT_TO_CHAR_PTR(core_reloc_##name) { \
.a = 1, \
.b = 2, \
.c = (void *)3, \
.d = (void *)4, \
.e = { [2] = 5 }, \
.f = { [1] = 6 }, \
.g = { .x = 7 }, \
.h = { .y = 8 }, \
}, \
.input_len = sizeof(struct core_reloc_##name), \
.output = STRUCT_TO_CHAR_PTR(core_reloc_mods_output) { \
.a = 1, .b = 2, .c = 3, .d = 4, \
.e = 5, .f = 6, .g = 7, .h = 8, \
}, \
.output_len = sizeof(struct core_reloc_mods_output), \
}
#define PTR_AS_ARR_CASE(name) { \
.case_name = #name, \
.bpf_obj_file = "test_core_reloc_ptr_as_arr.o", \
.btf_src_file = "btf__core_reloc_" #name ".o", \
.input = (const char *)&(struct core_reloc_##name []){ \
{ .a = 1 }, \
{ .a = 2 }, \
{ .a = 3 }, \
}, \
.input_len = 3 * sizeof(struct core_reloc_##name), \
.output = STRUCT_TO_CHAR_PTR(core_reloc_ptr_as_arr) { \
.a = 3, \
}, \
.output_len = sizeof(struct core_reloc_ptr_as_arr), \
}
#define INTS_DATA(struct_name) STRUCT_TO_CHAR_PTR(struct_name) { \
.u8_field = 1, \
.s8_field = 2, \
.u16_field = 3, \
.s16_field = 4, \
.u32_field = 5, \
.s32_field = 6, \
.u64_field = 7, \
.s64_field = 8, \
}
#define INTS_CASE_COMMON(name) \
.case_name = #name, \
.bpf_obj_file = "test_core_reloc_ints.o", \
.btf_src_file = "btf__core_reloc_" #name ".o"
#define INTS_CASE(name) { \
INTS_CASE_COMMON(name), \
.input = INTS_DATA(core_reloc_##name), \
.input_len = sizeof(struct core_reloc_##name), \
.output = INTS_DATA(core_reloc_ints), \
.output_len = sizeof(struct core_reloc_ints), \
}
#define INTS_ERR_CASE(name) { \
INTS_CASE_COMMON(name), \
.fails = true, \
}
struct core_reloc_test_case {
const char *case_name;
const char *bpf_obj_file;
const char *btf_src_file;
const char *input;
int input_len;
const char *output;
int output_len;
bool fails;
};
static struct core_reloc_test_case test_cases[] = {
/* validate we can find kernel image and use its BTF for relocs */
{
.case_name = "kernel",
.bpf_obj_file = "test_core_reloc_kernel.o",
.btf_src_file = NULL, /* load from /lib/modules/$(uname -r) */
.input = "",
.input_len = 0,
.output = "\1", /* true */
.output_len = 1,
},
/* validate BPF program can use multiple flavors to match against
* single target BTF type
*/
FLAVORS_CASE(flavors),
FLAVORS_ERR_CASE(flavors__err_wrong_name),
/* various struct/enum nesting and resolution scenarios */
NESTING_CASE(nesting),
NESTING_CASE(nesting___anon_embed),
NESTING_CASE(nesting___struct_union_mixup),
NESTING_CASE(nesting___extra_nesting),
NESTING_CASE(nesting___dup_compat_types),
NESTING_ERR_CASE(nesting___err_missing_field),
NESTING_ERR_CASE(nesting___err_array_field),
NESTING_ERR_CASE(nesting___err_missing_container),
NESTING_ERR_CASE(nesting___err_nonstruct_container),
NESTING_ERR_CASE(nesting___err_array_container),
NESTING_ERR_CASE(nesting___err_dup_incompat_types),
NESTING_ERR_CASE(nesting___err_partial_match_dups),
NESTING_ERR_CASE(nesting___err_too_deep),
/* various array access relocation scenarios */
ARRAYS_CASE(arrays),
ARRAYS_CASE(arrays___diff_arr_dim),
ARRAYS_CASE(arrays___diff_arr_val_sz),
ARRAYS_ERR_CASE(arrays___err_too_small),
ARRAYS_ERR_CASE(arrays___err_too_shallow),
ARRAYS_ERR_CASE(arrays___err_non_array),
ARRAYS_ERR_CASE(arrays___err_wrong_val_type1),
ARRAYS_ERR_CASE(arrays___err_wrong_val_type2),
/* enum/ptr/int handling scenarios */
PRIMITIVES_CASE(primitives),
PRIMITIVES_CASE(primitives___diff_enum_def),
PRIMITIVES_CASE(primitives___diff_func_proto),
PRIMITIVES_CASE(primitives___diff_ptr_type),
PRIMITIVES_ERR_CASE(primitives___err_non_enum),
PRIMITIVES_ERR_CASE(primitives___err_non_int),
PRIMITIVES_ERR_CASE(primitives___err_non_ptr),
/* const/volatile/restrict and typedefs scenarios */
MODS_CASE(mods),
MODS_CASE(mods___mod_swap),
MODS_CASE(mods___typedefs),
/* handling "ptr is an array" semantics */
PTR_AS_ARR_CASE(ptr_as_arr),
PTR_AS_ARR_CASE(ptr_as_arr___diff_sz),
/* int signedness/sizing/bitfield handling */
INTS_CASE(ints),
INTS_CASE(ints___bool),
INTS_CASE(ints___reverse_sign),
INTS_ERR_CASE(ints___err_bitfield),
INTS_ERR_CASE(ints___err_wrong_sz_8),
INTS_ERR_CASE(ints___err_wrong_sz_16),
INTS_ERR_CASE(ints___err_wrong_sz_32),
INTS_ERR_CASE(ints___err_wrong_sz_64),
/* validate edge cases of capturing relocations */
{
.case_name = "misc",
.bpf_obj_file = "test_core_reloc_misc.o",
.btf_src_file = "btf__core_reloc_misc.o",
.input = (const char *)&(struct core_reloc_misc_extensible[]){
{ .a = 1 },
{ .a = 2 }, /* not read */
{ .a = 3 },
},
.input_len = 4 * sizeof(int),
.output = STRUCT_TO_CHAR_PTR(core_reloc_misc_output) {
.a = 1,
.b = 1,
.c = 0, /* BUG in clang, should be 3 */
},
.output_len = sizeof(struct core_reloc_misc_output),
},
};
struct data {
char in[256];
char out[256];
};
void test_core_reloc(void)
{
const char *probe_name = "raw_tracepoint/sys_enter";
struct bpf_object_load_attr load_attr = {};
struct core_reloc_test_case *test_case;
int err, duration = 0, i, equal;
struct bpf_link *link = NULL;
struct bpf_map *data_map;
struct bpf_program *prog;
struct bpf_object *obj;
const int zero = 0;
struct data data;
for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
test_case = &test_cases[i];
if (!test__start_subtest(test_case->case_name))
continue;
obj = bpf_object__open(test_case->bpf_obj_file);
if (CHECK(IS_ERR_OR_NULL(obj), "obj_open",
"failed to open '%s': %ld\n",
test_case->bpf_obj_file, PTR_ERR(obj)))
continue;
prog = bpf_object__find_program_by_title(obj, probe_name);
if (CHECK(!prog, "find_probe",
"prog '%s' not found\n", probe_name))
goto cleanup;
bpf_program__set_type(prog, BPF_PROG_TYPE_RAW_TRACEPOINT);
load_attr.obj = obj;
load_attr.log_level = 0;
load_attr.target_btf_path = test_case->btf_src_file;
err = bpf_object__load_xattr(&load_attr);
if (test_case->fails) {
CHECK(!err, "obj_load_fail",
"should fail to load prog '%s'\n", probe_name);
goto cleanup;
} else {
if (CHECK(err, "obj_load",
"failed to load prog '%s': %d\n",
probe_name, err))
goto cleanup;
}
link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n",
PTR_ERR(link)))
goto cleanup;
data_map = bpf_object__find_map_by_name(obj, "test_cor.bss");
if (CHECK(!data_map, "find_data_map", "data map not found\n"))
goto cleanup;
memset(&data, 0, sizeof(data));
memcpy(data.in, test_case->input, test_case->input_len);
err = bpf_map_update_elem(bpf_map__fd(data_map),
&zero, &data, 0);
if (CHECK(err, "update_data_map",
"failed to update .data map: %d\n", err))
goto cleanup;
/* trigger test run */
usleep(1);
err = bpf_map_lookup_elem(bpf_map__fd(data_map), &zero, &data);
if (CHECK(err, "get_result",
"failed to get output data: %d\n", err))
goto cleanup;
equal = memcmp(data.out, test_case->output,
test_case->output_len) == 0;
if (CHECK(!equal, "check_result",
"input/output data don't match\n")) {
int j;
for (j = 0; j < test_case->input_len; j++) {
printf("input byte #%d: 0x%02hhx\n",
j, test_case->input[j]);
}
for (j = 0; j < test_case->output_len; j++) {
printf("output byte #%d: EXP 0x%02hhx GOT 0x%02hhx\n",
j, test_case->output[j], data.out[j]);
}
goto cleanup;
}
cleanup:
if (!IS_ERR_OR_NULL(link)) {
bpf_link__destroy(link);
link = NULL;
}
bpf_object__close(obj);
}
}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_arrays x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_arrays___diff_arr_dim x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_arrays___diff_arr_val_sz x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_arrays___err_non_array x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_arrays___err_too_shallow x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_arrays___err_too_small x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_arrays___err_wrong_val_type1 x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_arrays___err_wrong_val_type2 x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_flavors x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_flavors__err_wrong_name x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_ints x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_ints___bool x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_ints___err_bitfield x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_ints___err_wrong_sz_16 x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_ints___err_wrong_sz_32 x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_ints___err_wrong_sz_64 x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_ints___err_wrong_sz_8 x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_ints___reverse_sign x) {}

View File

@ -0,0 +1,5 @@
#include "core_reloc_types.h"
void f1(struct core_reloc_misc___a x) {}
void f2(struct core_reloc_misc___b x) {}
void f3(struct core_reloc_misc_extensible x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_mods x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_mods___mod_swap x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_mods___typedefs x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_nesting x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_nesting___anon_embed x) {}

View File

@ -0,0 +1,5 @@
#include "core_reloc_types.h"
void f1(struct core_reloc_nesting___dup_compat_types x) {}
void f2(struct core_reloc_nesting___dup_compat_types__2 x) {}
void f3(struct core_reloc_nesting___dup_compat_types__3 x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_nesting___err_array_container x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_nesting___err_array_field x) {}

View File

@ -0,0 +1,4 @@
#include "core_reloc_types.h"
void f1(struct core_reloc_nesting___err_dup_incompat_types__1 x) {}
void f2(struct core_reloc_nesting___err_dup_incompat_types__2 x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_nesting___err_missing_container x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_nesting___err_missing_field x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_nesting___err_nonstruct_container x) {}

View File

@ -0,0 +1,4 @@
#include "core_reloc_types.h"
void f1(struct core_reloc_nesting___err_partial_match_dups__a x) {}
void f2(struct core_reloc_nesting___err_partial_match_dups__b x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_nesting___err_too_deep x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_nesting___extra_nesting x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_nesting___struct_union_mixup x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_primitives x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_primitives___diff_enum_def x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_primitives___diff_func_proto x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_primitives___diff_ptr_type x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_primitives___err_non_enum x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_primitives___err_non_int x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_primitives___err_non_ptr x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_ptr_as_arr x) {}

View File

@ -0,0 +1,3 @@
#include "core_reloc_types.h"
void f(struct core_reloc_ptr_as_arr___diff_sz x) {}

View File

@ -0,0 +1,667 @@
#include <stdint.h>
#include <stdbool.h>
/*
* FLAVORS
*/
struct core_reloc_flavors {
int a;
int b;
int c;
};
/* this is not a flavor, as it doesn't have triple underscore */
struct core_reloc_flavors__err_wrong_name {
int a;
int b;
int c;
};
/*
* NESTING
*/
/* original set up, used to record relocations in BPF program */
struct core_reloc_nesting_substruct {
int a;
};
union core_reloc_nesting_subunion {
int b;
};
struct core_reloc_nesting {
union {
struct core_reloc_nesting_substruct a;
} a;
struct {
union core_reloc_nesting_subunion b;
} b;
};
/* inlined anonymous struct/union instead of named structs in original */
struct core_reloc_nesting___anon_embed {
int __just_for_padding;
union {
struct {
int a;
} a;
} a;
struct {
union {
int b;
} b;
} b;
};
/* different mix of nested structs/unions than in original */
struct core_reloc_nesting___struct_union_mixup {
int __a;
struct {
int __a;
union {
char __a;
int a;
} a;
} a;
int __b;
union {
int __b;
union {
char __b;
int b;
} b;
} b;
};
/* extra anon structs/unions, but still valid a.a.a and b.b.b accessors */
struct core_reloc_nesting___extra_nesting {
int __padding;
struct {
struct {
struct {
struct {
union {
int a;
} a;
};
};
} a;
int __some_more;
struct {
union {
union {
union {
struct {
int b;
};
} b;
};
} b;
};
};
};
/* three flavors of same struct with different structure but same layout for
* a.a.a and b.b.b, thus successfully resolved and relocatable */
struct core_reloc_nesting___dup_compat_types {
char __just_for_padding;
/* 3 more bytes of padding */
struct {
struct {
int a; /* offset 4 */
} a;
} a;
long long __more_padding;
struct {
struct {
int b; /* offset 16 */
} b;
} b;
};
struct core_reloc_nesting___dup_compat_types__2 {
int __aligned_padding;
struct {
int __trickier_noop[0];
struct {
char __some_more_noops[0];
int a; /* offset 4 */
} a;
} a;
int __more_padding;
struct {
struct {
struct {
int __critical_padding;
int b; /* offset 16 */
} b;
int __does_not_matter;
};
} b;
int __more_irrelevant_stuff;
};
struct core_reloc_nesting___dup_compat_types__3 {
char __correct_padding[4];
struct {
struct {
int a; /* offset 4 */
} a;
} a;
/* 8 byte padding due to next struct's alignment */
struct {
struct {
int b;
} b;
} b __attribute__((aligned(16)));
};
/* b.b.b field is missing */
struct core_reloc_nesting___err_missing_field {
struct {
struct {
int a;
} a;
} a;
struct {
struct {
int x;
} b;
} b;
};
/* b.b.b field is an array of integers instead of plain int */
struct core_reloc_nesting___err_array_field {
struct {
struct {
int a;
} a;
} a;
struct {
struct {
int b[1];
} b;
} b;
};
/* middle b container is missing */
struct core_reloc_nesting___err_missing_container {
struct {
struct {
int a;
} a;
} a;
struct {
int x;
} b;
};
/* middle b container is referenced through pointer instead of being embedded */
struct core_reloc_nesting___err_nonstruct_container {
struct {
struct {
int a;
} a;
} a;
struct {
struct {
int b;
} *b;
} b;
};
/* middle b container is an array of structs instead of plain struct */
struct core_reloc_nesting___err_array_container {
struct {
struct {
int a;
} a;
} a;
struct {
struct {
int b;
} b[1];
} b;
};
/* two flavors of same struct with incompatible layout for b.b.b */
struct core_reloc_nesting___err_dup_incompat_types__1 {
struct {
struct {
int a; /* offset 0 */
} a;
} a;
struct {
struct {
int b; /* offset 4 */
} b;
} b;
};
struct core_reloc_nesting___err_dup_incompat_types__2 {
struct {
struct {
int a; /* offset 0 */
} a;
} a;
int __extra_padding;
struct {
struct {
int b; /* offset 8 (!) */
} b;
} b;
};
/* two flavors of same struct having one of a.a.a and b.b.b, but not both */
struct core_reloc_nesting___err_partial_match_dups__a {
struct {
struct {
int a;
} a;
} a;
};
struct core_reloc_nesting___err_partial_match_dups__b {
struct {
struct {
int b;
} b;
} b;
};
struct core_reloc_nesting___err_too_deep {
struct {
struct {
int a;
} a;
} a;
/* 65 levels of nestedness for b.b.b */
struct {
struct {
struct { struct { struct { struct { struct {
struct { struct { struct { struct { struct {
struct { struct { struct { struct { struct {
struct { struct { struct { struct { struct {
struct { struct { struct { struct { struct {
struct { struct { struct { struct { struct {
struct { struct { struct { struct { struct {
struct { struct { struct { struct { struct {
struct { struct { struct { struct { struct {
struct { struct { struct { struct { struct {
struct { struct { struct { struct { struct {
struct { struct { struct { struct { struct {
/* this one is one too much */
struct {
int b;
};
}; }; }; }; };
}; }; }; }; };
}; }; }; }; };
}; }; }; }; };
}; }; }; }; };
}; }; }; }; };
}; }; }; }; };
}; }; }; }; };
}; }; }; }; };
}; }; }; }; };
}; }; }; }; };
}; }; }; }; };
} b;
} b;
};
/*
* ARRAYS
*/
struct core_reloc_arrays_output {
int a2;
char b123;
int c1c;
int d00d;
};
struct core_reloc_arrays_substruct {
int c;
int d;
};
struct core_reloc_arrays {
int a[5];
char b[2][3][4];
struct core_reloc_arrays_substruct c[3];
struct core_reloc_arrays_substruct d[1][2];
};
/* bigger array dimensions */
struct core_reloc_arrays___diff_arr_dim {
int a[7];
char b[3][4][5];
struct core_reloc_arrays_substruct c[4];
struct core_reloc_arrays_substruct d[2][3];
};
/* different size of array's value (struct) */
struct core_reloc_arrays___diff_arr_val_sz {
int a[5];
char b[2][3][4];
struct {
int __padding1;
int c;
int __padding2;
} c[3];
struct {
int __padding1;
int d;
int __padding2;
} d[1][2];
};
struct core_reloc_arrays___err_too_small {
int a[2]; /* this one is too small */
char b[2][3][4];
struct core_reloc_arrays_substruct c[3];
struct core_reloc_arrays_substruct d[1][2];
};
struct core_reloc_arrays___err_too_shallow {
int a[5];
char b[2][3]; /* this one lacks one dimension */
struct core_reloc_arrays_substruct c[3];
struct core_reloc_arrays_substruct d[1][2];
};
struct core_reloc_arrays___err_non_array {
int a; /* not an array */
char b[2][3][4];
struct core_reloc_arrays_substruct c[3];
struct core_reloc_arrays_substruct d[1][2];
};
struct core_reloc_arrays___err_wrong_val_type1 {
char a[5]; /* char instead of int */
char b[2][3][4];
struct core_reloc_arrays_substruct c[3];
struct core_reloc_arrays_substruct d[1][2];
};
struct core_reloc_arrays___err_wrong_val_type2 {
int a[5];
char b[2][3][4];
int c[3]; /* value is not a struct */
struct core_reloc_arrays_substruct d[1][2];
};
/*
* PRIMITIVES
*/
enum core_reloc_primitives_enum {
A = 0,
B = 1,
};
struct core_reloc_primitives {
char a;
int b;
enum core_reloc_primitives_enum c;
void *d;
int (*f)(const char *);
};
struct core_reloc_primitives___diff_enum_def {
char a;
int b;
void *d;
int (*f)(const char *);
enum {
X = 100,
Y = 200,
} c; /* inline enum def with differing set of values */
};
struct core_reloc_primitives___diff_func_proto {
void (*f)(int); /* incompatible function prototype */
void *d;
enum core_reloc_primitives_enum c;
int b;
char a;
};
struct core_reloc_primitives___diff_ptr_type {
const char * const d; /* different pointee type + modifiers */
char a;
int b;
enum core_reloc_primitives_enum c;
int (*f)(const char *);
};
struct core_reloc_primitives___err_non_enum {
char a[1];
int b;
int c; /* int instead of enum */
void *d;
int (*f)(const char *);
};
struct core_reloc_primitives___err_non_int {
char a[1];
int *b; /* ptr instead of int */
enum core_reloc_primitives_enum c;
void *d;
int (*f)(const char *);
};
struct core_reloc_primitives___err_non_ptr {
char a[1];
int b;
enum core_reloc_primitives_enum c;
int d; /* int instead of ptr */
int (*f)(const char *);
};
/*
* MODS
*/
struct core_reloc_mods_output {
int a, b, c, d, e, f, g, h;
};
typedef const int int_t;
typedef const char *char_ptr_t;
typedef const int arr_t[7];
struct core_reloc_mods_substruct {
int x;
int y;
};
typedef struct {
int x;
int y;
} core_reloc_mods_substruct_t;
struct core_reloc_mods {
int a;
int_t b;
char *c;
char_ptr_t d;
int e[3];
arr_t f;
struct core_reloc_mods_substruct g;
core_reloc_mods_substruct_t h;
};
/* a/b, c/d, e/f, and g/h pairs are swapped */
struct core_reloc_mods___mod_swap {
int b;
int_t a;
char *d;
char_ptr_t c;
int f[3];
arr_t e;
struct {
int y;
int x;
} h;
core_reloc_mods_substruct_t g;
};
typedef int int1_t;
typedef int1_t int2_t;
typedef int2_t int3_t;
typedef int arr1_t[5];
typedef arr1_t arr2_t;
typedef arr2_t arr3_t;
typedef arr3_t arr4_t;
typedef const char * const volatile restrict fancy_char_ptr_t;
typedef core_reloc_mods_substruct_t core_reloc_mods_substruct_tt;
/* we need more typedefs */
struct core_reloc_mods___typedefs {
core_reloc_mods_substruct_tt g;
core_reloc_mods_substruct_tt h;
arr4_t f;
arr4_t e;
fancy_char_ptr_t d;
fancy_char_ptr_t c;
int3_t b;
int3_t a;
};
/*
* PTR_AS_ARR
*/
struct core_reloc_ptr_as_arr {
int a;
};
struct core_reloc_ptr_as_arr___diff_sz {
int :32; /* padding */
char __some_more_padding;
int a;
};
/*
* INTS
*/
struct core_reloc_ints {
uint8_t u8_field;
int8_t s8_field;
uint16_t u16_field;
int16_t s16_field;
uint32_t u32_field;
int32_t s32_field;
uint64_t u64_field;
int64_t s64_field;
};
/* signed/unsigned types swap */
struct core_reloc_ints___reverse_sign {
int8_t u8_field;
uint8_t s8_field;
int16_t u16_field;
uint16_t s16_field;
int32_t u32_field;
uint32_t s32_field;
int64_t u64_field;
uint64_t s64_field;
};
struct core_reloc_ints___bool {
bool u8_field; /* bool instead of uint8 */
int8_t s8_field;
uint16_t u16_field;
int16_t s16_field;
uint32_t u32_field;
int32_t s32_field;
uint64_t u64_field;
int64_t s64_field;
};
struct core_reloc_ints___err_bitfield {
uint8_t u8_field;
int8_t s8_field;
uint16_t u16_field;
int16_t s16_field;
uint32_t u32_field: 32; /* bitfields are not supported */
int32_t s32_field;
uint64_t u64_field;
int64_t s64_field;
};
struct core_reloc_ints___err_wrong_sz_8 {
uint16_t u8_field; /* not 8-bit anymore */
int16_t s8_field; /* not 8-bit anymore */
uint16_t u16_field;
int16_t s16_field;
uint32_t u32_field;
int32_t s32_field;
uint64_t u64_field;
int64_t s64_field;
};
struct core_reloc_ints___err_wrong_sz_16 {
uint8_t u8_field;
int8_t s8_field;
uint32_t u16_field; /* not 16-bit anymore */
int32_t s16_field; /* not 16-bit anymore */
uint32_t u32_field;
int32_t s32_field;
uint64_t u64_field;
int64_t s64_field;
};
struct core_reloc_ints___err_wrong_sz_32 {
uint8_t u8_field;
int8_t s8_field;
uint16_t u16_field;
int16_t s16_field;
uint64_t u32_field; /* not 32-bit anymore */
int64_t s32_field; /* not 32-bit anymore */
uint64_t u64_field;
int64_t s64_field;
};
struct core_reloc_ints___err_wrong_sz_64 {
uint8_t u8_field;
int8_t s8_field;
uint16_t u16_field;
int16_t s16_field;
uint32_t u32_field;
int32_t s32_field;
uint32_t u64_field; /* not 64-bit anymore */
int32_t s64_field; /* not 64-bit anymore */
};
/*
* MISC
*/
struct core_reloc_misc_output {
int a, b, c;
};
struct core_reloc_misc___a {
int a1;
int a2;
};
struct core_reloc_misc___b {
int b1;
int b2;
};
/* this one extends core_reloc_misc_extensible struct from BPF prog */
struct core_reloc_misc_extensible {
int a;
int b;
int c;
int d;
};

View File

@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <stdint.h>
#include "bpf_helpers.h"
char _license[] SEC("license") = "GPL";
static volatile struct data {
char in[256];
char out[256];
} data;
struct core_reloc_arrays_output {
int a2;
char b123;
int c1c;
int d00d;
};
struct core_reloc_arrays_substruct {
int c;
int d;
};
struct core_reloc_arrays {
int a[5];
char b[2][3][4];
struct core_reloc_arrays_substruct c[3];
struct core_reloc_arrays_substruct d[1][2];
};
SEC("raw_tracepoint/sys_enter")
int test_core_arrays(void *ctx)
{
struct core_reloc_arrays *in = (void *)&data.in;
struct core_reloc_arrays_output *out = (void *)&data.out;
/* in->a[2] */
if (BPF_CORE_READ(&out->a2, &in->a[2]))
return 1;
/* in->b[1][2][3] */
if (BPF_CORE_READ(&out->b123, &in->b[1][2][3]))
return 1;
/* in->c[1].c */
if (BPF_CORE_READ(&out->c1c, &in->c[1].c))
return 1;
/* in->d[0][0].d */
if (BPF_CORE_READ(&out->d00d, &in->d[0][0].d))
return 1;
return 0;
}

View File

@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <stdint.h>
#include "bpf_helpers.h"
char _license[] SEC("license") = "GPL";
static volatile struct data {
char in[256];
char out[256];
} data;
struct core_reloc_flavors {
int a;
int b;
int c;
};
/* local flavor with reversed layout */
struct core_reloc_flavors___reversed {
int c;
int b;
int a;
};
/* local flavor with nested/overlapping layout */
struct core_reloc_flavors___weird {
struct {
int b;
};
/* a and c overlap in local flavor, but this should still work
* correctly with target original flavor
*/
union {
int a;
int c;
};
};
SEC("raw_tracepoint/sys_enter")
int test_core_flavors(void *ctx)
{
struct core_reloc_flavors *in_orig = (void *)&data.in;
struct core_reloc_flavors___reversed *in_rev = (void *)&data.in;
struct core_reloc_flavors___weird *in_weird = (void *)&data.in;
struct core_reloc_flavors *out = (void *)&data.out;
/* read a using weird layout */
if (BPF_CORE_READ(&out->a, &in_weird->a))
return 1;
/* read b using reversed layout */
if (BPF_CORE_READ(&out->b, &in_rev->b))
return 1;
/* read c using original layout */
if (BPF_CORE_READ(&out->c, &in_orig->c))
return 1;
return 0;
}

View File

@ -0,0 +1,44 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <stdint.h>
#include "bpf_helpers.h"
char _license[] SEC("license") = "GPL";
static volatile struct data {
char in[256];
char out[256];
} data;
struct core_reloc_ints {
uint8_t u8_field;
int8_t s8_field;
uint16_t u16_field;
int16_t s16_field;
uint32_t u32_field;
int32_t s32_field;
uint64_t u64_field;
int64_t s64_field;
};
SEC("raw_tracepoint/sys_enter")
int test_core_ints(void *ctx)
{
struct core_reloc_ints *in = (void *)&data.in;
struct core_reloc_ints *out = (void *)&data.out;
if (BPF_CORE_READ(&out->u8_field, &in->u8_field) ||
BPF_CORE_READ(&out->s8_field, &in->s8_field) ||
BPF_CORE_READ(&out->u16_field, &in->u16_field) ||
BPF_CORE_READ(&out->s16_field, &in->s16_field) ||
BPF_CORE_READ(&out->u32_field, &in->u32_field) ||
BPF_CORE_READ(&out->s32_field, &in->s32_field) ||
BPF_CORE_READ(&out->u64_field, &in->u64_field) ||
BPF_CORE_READ(&out->s64_field, &in->s64_field))
return 1;
return 0;
}

View File

@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <stdint.h>
#include "bpf_helpers.h"
char _license[] SEC("license") = "GPL";
static volatile struct data {
char in[256];
char out[256];
} data;
struct task_struct {
int pid;
int tgid;
};
SEC("raw_tracepoint/sys_enter")
int test_core_kernel(void *ctx)
{
struct task_struct *task = (void *)bpf_get_current_task();
uint64_t pid_tgid = bpf_get_current_pid_tgid();
int pid, tgid;
if (BPF_CORE_READ(&pid, &task->pid) ||
BPF_CORE_READ(&tgid, &task->tgid))
return 1;
/* validate pid + tgid matches */
data.out[0] = (((uint64_t)pid << 32) | tgid) == pid_tgid;
return 0;
}

View File

@ -0,0 +1,57 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <stdint.h>
#include "bpf_helpers.h"
char _license[] SEC("license") = "GPL";
static volatile struct data {
char in[256];
char out[256];
} data;
struct core_reloc_misc_output {
int a, b, c;
};
struct core_reloc_misc___a {
int a1;
int a2;
};
struct core_reloc_misc___b {
int b1;
int b2;
};
/* fixed two first members, can be extended with new fields */
struct core_reloc_misc_extensible {
int a;
int b;
};
SEC("raw_tracepoint/sys_enter")
int test_core_misc(void *ctx)
{
struct core_reloc_misc___a *in_a = (void *)&data.in;
struct core_reloc_misc___b *in_b = (void *)&data.in;
struct core_reloc_misc_extensible *in_ext = (void *)&data.in;
struct core_reloc_misc_output *out = (void *)&data.out;
/* record two different relocations with the same accessor string */
if (BPF_CORE_READ(&out->a, &in_a->a1) || /* accessor: 0:0 */
BPF_CORE_READ(&out->b, &in_b->b1)) /* accessor: 0:0 */
return 1;
/* Validate relocations capture array-only accesses for structs with
* fixed header, but with potentially extendable tail. This will read
* first 4 bytes of 2nd element of in_ext array of potentially
* variably sized struct core_reloc_misc_extensible. */
if (BPF_CORE_READ(&out->c, &in_ext[2])) /* accessor: 2 */
return 1;
return 0;
}

View File

@ -0,0 +1,62 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <stdint.h>
#include "bpf_helpers.h"
char _license[] SEC("license") = "GPL";
static volatile struct data {
char in[256];
char out[256];
} data;
struct core_reloc_mods_output {
int a, b, c, d, e, f, g, h;
};
typedef const int int_t;
typedef const char *char_ptr_t;
typedef const int arr_t[7];
struct core_reloc_mods_substruct {
int x;
int y;
};
typedef struct {
int x;
int y;
} core_reloc_mods_substruct_t;
struct core_reloc_mods {
int a;
int_t b;
char *c;
char_ptr_t d;
int e[3];
arr_t f;
struct core_reloc_mods_substruct g;
core_reloc_mods_substruct_t h;
};
SEC("raw_tracepoint/sys_enter")
int test_core_mods(void *ctx)
{
struct core_reloc_mods *in = (void *)&data.in;
struct core_reloc_mods_output *out = (void *)&data.out;
if (BPF_CORE_READ(&out->a, &in->a) ||
BPF_CORE_READ(&out->b, &in->b) ||
BPF_CORE_READ(&out->c, &in->c) ||
BPF_CORE_READ(&out->d, &in->d) ||
BPF_CORE_READ(&out->e, &in->e[2]) ||
BPF_CORE_READ(&out->f, &in->f[1]) ||
BPF_CORE_READ(&out->g, &in->g.x) ||
BPF_CORE_READ(&out->h, &in->h.y))
return 1;
return 0;
}

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <stdint.h>
#include "bpf_helpers.h"
char _license[] SEC("license") = "GPL";
static volatile struct data {
char in[256];
char out[256];
} data;
struct core_reloc_nesting_substruct {
int a;
};
union core_reloc_nesting_subunion {
int b;
};
/* int a.a.a and b.b.b accesses */
struct core_reloc_nesting {
union {
struct core_reloc_nesting_substruct a;
} a;
struct {
union core_reloc_nesting_subunion b;
} b;
};
SEC("raw_tracepoint/sys_enter")
int test_core_nesting(void *ctx)
{
struct core_reloc_nesting *in = (void *)&data.in;
struct core_reloc_nesting *out = (void *)&data.out;
if (BPF_CORE_READ(&out->a.a.a, &in->a.a.a))
return 1;
if (BPF_CORE_READ(&out->b.b.b, &in->b.b.b))
return 1;
return 0;
}

View File

@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <stdint.h>
#include "bpf_helpers.h"
char _license[] SEC("license") = "GPL";
static volatile struct data {
char in[256];
char out[256];
} data;
enum core_reloc_primitives_enum {
A = 0,
B = 1,
};
struct core_reloc_primitives {
char a;
int b;
enum core_reloc_primitives_enum c;
void *d;
int (*f)(const char *);
};
SEC("raw_tracepoint/sys_enter")
int test_core_primitives(void *ctx)
{
struct core_reloc_primitives *in = (void *)&data.in;
struct core_reloc_primitives *out = (void *)&data.out;
if (BPF_CORE_READ(&out->a, &in->a) ||
BPF_CORE_READ(&out->b, &in->b) ||
BPF_CORE_READ(&out->c, &in->c) ||
BPF_CORE_READ(&out->d, &in->d) ||
BPF_CORE_READ(&out->f, &in->f))
return 1;
return 0;
}

View File

@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
#include <linux/bpf.h>
#include <stdint.h>
#include "bpf_helpers.h"
char _license[] SEC("license") = "GPL";
static volatile struct data {
char in[256];
char out[256];
} data;
struct core_reloc_ptr_as_arr {
int a;
};
SEC("raw_tracepoint/sys_enter")
int test_core_ptr_as_arr(void *ctx)
{
struct core_reloc_ptr_as_arr *in = (void *)&data.in;
struct core_reloc_ptr_as_arr *out = (void *)&data.out;
if (BPF_CORE_READ(&out->a, &in[2].a))
return 1;
return 0;
}