mirror of https://gitee.com/openkylin/linux.git
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:
commit
726e333fd2
|
@ -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, ¶m);
|
||||
}
|
||||
|
||||
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, ¶m);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___diff_arr_dim x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___diff_arr_val_sz x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___err_non_array x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___err_too_shallow x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___err_too_small x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___err_wrong_val_type1 x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_arrays___err_wrong_val_type2 x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_flavors x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_flavors__err_wrong_name x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___bool x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___err_bitfield x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___err_wrong_sz_16 x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___err_wrong_sz_32 x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___err_wrong_sz_64 x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___err_wrong_sz_8 x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ints___reverse_sign x) {}
|
|
@ -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) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_mods x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_mods___mod_swap x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_mods___typedefs x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___anon_embed x) {}
|
|
@ -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) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___err_array_container x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___err_array_field x) {}
|
|
@ -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) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___err_missing_container x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___err_missing_field x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___err_nonstruct_container x) {}
|
|
@ -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) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___err_too_deep x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___extra_nesting x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_nesting___struct_union_mixup x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives___diff_enum_def x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives___diff_func_proto x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives___diff_ptr_type x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives___err_non_enum x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives___err_non_int x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_primitives___err_non_ptr x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ptr_as_arr x) {}
|
|
@ -0,0 +1,3 @@
|
|||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_ptr_as_arr___diff_sz x) {}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue