Skip to content

Commit

Permalink
Merge branch 'btf-arbitrary-__attribute__-encoding'
Browse files Browse the repository at this point in the history
Ihor Solodrai says:

====================
BTF: arbitrary __attribute__ encoding

This patch series extends BPF Type Format (BTF) to support arbitrary
__attribute__ encoding.

Setting the kind_flag to 1 in BTF type tags and decl tags now changes
the meaning for the encoded tag, in particular with respect to
btf_dump in libbpf.

If the kflag is set, then the string encoded by the tag represents the
full attribute-list of an attribute specifier [1].

This feature will allow extending tools such as pahole and bpftool to
capture and use more granular type information, and make it easier to
manage compatibility between clang and gcc BPF compilers.

[1] https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Attribute-Syntax.html

v2->v3: nit fixes suggested by Andrii
v1->v2:
  - When checking for specific BTF tags in the verifier, make sure the
    tag's kflag is 0
  - Split docs and libbpf changes into separate patches
  - Various renames, as suggested by Andrii and Eduard

v2: https://lore.kernel.org/bpf/20250127233955.2275804-1-ihor.solodrai@linux.dev/
v1: https://lore.kernel.org/bpf/20250122025308.2717553-1-ihor.solodrai@pm.me
====================

Link: https://patch.msgid.link/20250130201239.1429648-1-ihor.solodrai@linux.dev
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
  • Loading branch information
Andrii Nakryiko committed Feb 6, 2025
2 parents 03f3aa4 + 770cdcf commit 12befeb
Show file tree
Hide file tree
Showing 11 changed files with 244 additions and 85 deletions.
25 changes: 21 additions & 4 deletions Documentation/bpf/btf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ Each type contains the following common data::
* bits 24-28: kind (e.g. int, ptr, array...etc)
* bits 29-30: unused
* bit 31: kind_flag, currently used by
* struct, union, fwd, enum and enum64.
* struct, union, enum, fwd, enum64,
* decl_tag and type_tag
*/
__u32 info;
/* "size" is used by INT, ENUM, STRUCT, UNION and ENUM64.
Expand Down Expand Up @@ -478,7 +479,7 @@ No additional type data follow ``btf_type``.

``struct btf_type`` encoding requirement:
* ``name_off``: offset to a non-empty string
* ``info.kind_flag``: 0
* ``info.kind_flag``: 0 or 1
* ``info.kind``: BTF_KIND_DECL_TAG
* ``info.vlen``: 0
* ``type``: ``struct``, ``union``, ``func``, ``var`` or ``typedef``
Expand All @@ -489,7 +490,6 @@ No additional type data follow ``btf_type``.
__u32 component_idx;
};

The ``name_off`` encodes btf_decl_tag attribute string.
The ``type`` should be ``struct``, ``union``, ``func``, ``var`` or ``typedef``.
For ``var`` or ``typedef`` type, ``btf_decl_tag.component_idx`` must be ``-1``.
For the other three types, if the btf_decl_tag attribute is
Expand All @@ -499,12 +499,21 @@ the attribute is applied to a ``struct``/``union`` member or
a ``func`` argument, and ``btf_decl_tag.component_idx`` should be a
valid index (starting from 0) pointing to a member or an argument.

If ``info.kind_flag`` is 0, then this is a normal decl tag, and the
``name_off`` encodes btf_decl_tag attribute string.

If ``info.kind_flag`` is 1, then the decl tag represents an arbitrary
__attribute__. In this case, ``name_off`` encodes a string
representing the attribute-list of the attribute specifier. For
example, for an ``__attribute__((aligned(4)))`` the string's contents
is ``aligned(4)``.

2.2.18 BTF_KIND_TYPE_TAG
~~~~~~~~~~~~~~~~~~~~~~~~

``struct btf_type`` encoding requirement:
* ``name_off``: offset to a non-empty string
* ``info.kind_flag``: 0
* ``info.kind_flag``: 0 or 1
* ``info.kind``: BTF_KIND_TYPE_TAG
* ``info.vlen``: 0
* ``type``: the type with ``btf_type_tag`` attribute
Expand All @@ -522,6 +531,14 @@ type_tag, then zero or more const/volatile/restrict/typedef
and finally the base type. The base type is one of
int, ptr, array, struct, union, enum, func_proto and float types.

Similarly to decl tags, if the ``info.kind_flag`` is 0, then this is a
normal type tag, and the ``name_off`` encodes btf_type_tag attribute
string.

If ``info.kind_flag`` is 1, then the type tag represents an arbitrary
__attribute__, and the ``name_off`` encodes a string representing the
attribute-list of the attribute specifier.

2.2.19 BTF_KIND_ENUM64
~~~~~~~~~~~~~~~~~~~~~~

Expand Down
3 changes: 2 additions & 1 deletion include/uapi/linux/btf.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ struct btf_type {
* bits 24-28: kind (e.g. int, ptr, array...etc)
* bits 29-30: unused
* bit 31: kind_flag, currently used by
* struct, union, enum, fwd and enum64
* struct, union, enum, fwd, enum64,
* decl_tag and type_tag
*/
__u32 info;
/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
Expand Down
26 changes: 12 additions & 14 deletions kernel/bpf/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -2575,7 +2575,7 @@ static int btf_ref_type_check_meta(struct btf_verifier_env *env,
return -EINVAL;
}

if (btf_type_kflag(t)) {
if (btf_type_kflag(t) && !btf_type_is_type_tag(t)) {
btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
return -EINVAL;
}
Expand Down Expand Up @@ -3332,6 +3332,8 @@ static int btf_find_kptr(const struct btf *btf, const struct btf_type *t,
u32 off, int sz, struct btf_field_info *info, u32 field_mask)
{
enum btf_field_type type;
const char *tag_value;
bool is_type_tag;
u32 res_id;

/* Permit modifiers on the pointer itself */
Expand All @@ -3341,19 +3343,20 @@ static int btf_find_kptr(const struct btf *btf, const struct btf_type *t,
if (!btf_type_is_ptr(t))
return BTF_FIELD_IGNORE;
t = btf_type_by_id(btf, t->type);

if (!btf_type_is_type_tag(t))
is_type_tag = btf_type_is_type_tag(t) && !btf_type_kflag(t);
if (!is_type_tag)
return BTF_FIELD_IGNORE;
/* Reject extra tags */
if (btf_type_is_type_tag(btf_type_by_id(btf, t->type)))
return -EINVAL;
if (!strcmp("kptr_untrusted", __btf_name_by_offset(btf, t->name_off)))
tag_value = __btf_name_by_offset(btf, t->name_off);
if (!strcmp("kptr_untrusted", tag_value))
type = BPF_KPTR_UNREF;
else if (!strcmp("kptr", __btf_name_by_offset(btf, t->name_off)))
else if (!strcmp("kptr", tag_value))
type = BPF_KPTR_REF;
else if (!strcmp("percpu_kptr", __btf_name_by_offset(btf, t->name_off)))
else if (!strcmp("percpu_kptr", tag_value))
type = BPF_KPTR_PERCPU;
else if (!strcmp("uptr", __btf_name_by_offset(btf, t->name_off)))
else if (!strcmp("uptr", tag_value))
type = BPF_UPTR;
else
return -EINVAL;
Expand Down Expand Up @@ -4944,11 +4947,6 @@ static s32 btf_decl_tag_check_meta(struct btf_verifier_env *env,
return -EINVAL;
}

if (btf_type_kflag(t)) {
btf_verifier_log_type(env, t, "Invalid btf_info kind_flag");
return -EINVAL;
}

component_idx = btf_type_decl_tag(t)->component_idx;
if (component_idx < -1) {
btf_verifier_log_type(env, t, "Invalid component_idx");
Expand Down Expand Up @@ -6743,7 +6741,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
info->btf_id = t->type;
t = btf_type_by_id(btf, t->type);

if (btf_type_is_type_tag(t)) {
if (btf_type_is_type_tag(t) && !btf_type_kflag(t)) {
tag_value = __btf_name_by_offset(btf, t->name_off);
if (strcmp(tag_value, "user") == 0)
info->reg_type |= MEM_USER;
Expand Down Expand Up @@ -7002,7 +7000,7 @@ static int btf_struct_walk(struct bpf_verifier_log *log, const struct btf *btf,

/* check type tag */
t = btf_type_by_id(btf, mtype->type);
if (btf_type_is_type_tag(t)) {
if (btf_type_is_type_tag(t) && !btf_type_kflag(t)) {
tag_value = __btf_name_by_offset(btf, t->name_off);
/* check __user tag */
if (strcmp(tag_value, "user") == 0)
Expand Down
3 changes: 2 additions & 1 deletion tools/include/uapi/linux/btf.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ struct btf_type {
* bits 24-28: kind (e.g. int, ptr, array...etc)
* bits 29-30: unused
* bit 31: kind_flag, currently used by
* struct, union, enum, fwd and enum64
* struct, union, enum, fwd, enum64,
* decl_tag and type_tag
*/
__u32 info;
/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
Expand Down
86 changes: 63 additions & 23 deletions tools/lib/bpf/btf.c
Original file line number Diff line number Diff line change
Expand Up @@ -2090,7 +2090,7 @@ static int validate_type_id(int id)
}

/* generic append function for PTR, TYPEDEF, CONST/VOLATILE/RESTRICT */
static int btf_add_ref_kind(struct btf *btf, int kind, const char *name, int ref_type_id)
static int btf_add_ref_kind(struct btf *btf, int kind, const char *name, int ref_type_id, int kflag)
{
struct btf_type *t;
int sz, name_off = 0;
Expand All @@ -2113,7 +2113,7 @@ static int btf_add_ref_kind(struct btf *btf, int kind, const char *name, int ref
}

t->name_off = name_off;
t->info = btf_type_info(kind, 0, 0);
t->info = btf_type_info(kind, 0, kflag);
t->type = ref_type_id;

return btf_commit_type(btf, sz);
Expand All @@ -2128,7 +2128,7 @@ static int btf_add_ref_kind(struct btf *btf, int kind, const char *name, int ref
*/
int btf__add_ptr(struct btf *btf, int ref_type_id)
{
return btf_add_ref_kind(btf, BTF_KIND_PTR, NULL, ref_type_id);
return btf_add_ref_kind(btf, BTF_KIND_PTR, NULL, ref_type_id, 0);
}

/*
Expand Down Expand Up @@ -2506,7 +2506,7 @@ int btf__add_fwd(struct btf *btf, const char *name, enum btf_fwd_kind fwd_kind)
struct btf_type *t;
int id;

id = btf_add_ref_kind(btf, BTF_KIND_FWD, name, 0);
id = btf_add_ref_kind(btf, BTF_KIND_FWD, name, 0, 0);
if (id <= 0)
return id;
t = btf_type_by_id(btf, id);
Expand Down Expand Up @@ -2536,7 +2536,7 @@ int btf__add_typedef(struct btf *btf, const char *name, int ref_type_id)
if (!name || !name[0])
return libbpf_err(-EINVAL);

return btf_add_ref_kind(btf, BTF_KIND_TYPEDEF, name, ref_type_id);
return btf_add_ref_kind(btf, BTF_KIND_TYPEDEF, name, ref_type_id, 0);
}

/*
Expand All @@ -2548,7 +2548,7 @@ int btf__add_typedef(struct btf *btf, const char *name, int ref_type_id)
*/
int btf__add_volatile(struct btf *btf, int ref_type_id)
{
return btf_add_ref_kind(btf, BTF_KIND_VOLATILE, NULL, ref_type_id);
return btf_add_ref_kind(btf, BTF_KIND_VOLATILE, NULL, ref_type_id, 0);
}

/*
Expand All @@ -2560,7 +2560,7 @@ int btf__add_volatile(struct btf *btf, int ref_type_id)
*/
int btf__add_const(struct btf *btf, int ref_type_id)
{
return btf_add_ref_kind(btf, BTF_KIND_CONST, NULL, ref_type_id);
return btf_add_ref_kind(btf, BTF_KIND_CONST, NULL, ref_type_id, 0);
}

/*
Expand All @@ -2572,7 +2572,7 @@ int btf__add_const(struct btf *btf, int ref_type_id)
*/
int btf__add_restrict(struct btf *btf, int ref_type_id)
{
return btf_add_ref_kind(btf, BTF_KIND_RESTRICT, NULL, ref_type_id);
return btf_add_ref_kind(btf, BTF_KIND_RESTRICT, NULL, ref_type_id, 0);
}

/*
Expand All @@ -2588,7 +2588,24 @@ int btf__add_type_tag(struct btf *btf, const char *value, int ref_type_id)
if (!value || !value[0])
return libbpf_err(-EINVAL);

return btf_add_ref_kind(btf, BTF_KIND_TYPE_TAG, value, ref_type_id);
return btf_add_ref_kind(btf, BTF_KIND_TYPE_TAG, value, ref_type_id, 0);
}

/*
* Append new BTF_KIND_TYPE_TAG type with:
* - *value*, non-empty/non-NULL tag value;
* - *ref_type_id* - referenced type ID, it might not exist yet;
* Set info->kflag to 1, indicating this tag is an __attribute__
* Returns:
* - >0, type ID of newly added BTF type;
* - <0, on error.
*/
int btf__add_type_attr(struct btf *btf, const char *value, int ref_type_id)
{
if (!value || !value[0])
return libbpf_err(-EINVAL);

return btf_add_ref_kind(btf, BTF_KIND_TYPE_TAG, value, ref_type_id, 1);
}

/*
Expand All @@ -2610,7 +2627,7 @@ int btf__add_func(struct btf *btf, const char *name,
linkage != BTF_FUNC_EXTERN)
return libbpf_err(-EINVAL);

id = btf_add_ref_kind(btf, BTF_KIND_FUNC, name, proto_type_id);
id = btf_add_ref_kind(btf, BTF_KIND_FUNC, name, proto_type_id, 0);
if (id > 0) {
struct btf_type *t = btf_type_by_id(btf, id);

Expand Down Expand Up @@ -2845,18 +2862,8 @@ int btf__add_datasec_var_info(struct btf *btf, int var_type_id, __u32 offset, __
return 0;
}

/*
* Append new BTF_KIND_DECL_TAG type with:
* - *value* - non-empty/non-NULL string;
* - *ref_type_id* - referenced type ID, it might not exist yet;
* - *component_idx* - -1 for tagging reference type, otherwise struct/union
* member or function argument index;
* Returns:
* - >0, type ID of newly added BTF type;
* - <0, on error.
*/
int btf__add_decl_tag(struct btf *btf, const char *value, int ref_type_id,
int component_idx)
static int btf_add_decl_tag(struct btf *btf, const char *value, int ref_type_id,
int component_idx, int kflag)
{
struct btf_type *t;
int sz, value_off;
Expand All @@ -2880,13 +2887,46 @@ int btf__add_decl_tag(struct btf *btf, const char *value, int ref_type_id,
return value_off;

t->name_off = value_off;
t->info = btf_type_info(BTF_KIND_DECL_TAG, 0, false);
t->info = btf_type_info(BTF_KIND_DECL_TAG, 0, kflag);
t->type = ref_type_id;
btf_decl_tag(t)->component_idx = component_idx;

return btf_commit_type(btf, sz);
}

/*
* Append new BTF_KIND_DECL_TAG type with:
* - *value* - non-empty/non-NULL string;
* - *ref_type_id* - referenced type ID, it might not exist yet;
* - *component_idx* - -1 for tagging reference type, otherwise struct/union
* member or function argument index;
* Returns:
* - >0, type ID of newly added BTF type;
* - <0, on error.
*/
int btf__add_decl_tag(struct btf *btf, const char *value, int ref_type_id,
int component_idx)
{
return btf_add_decl_tag(btf, value, ref_type_id, component_idx, 0);
}

/*
* Append new BTF_KIND_DECL_TAG type with:
* - *value* - non-empty/non-NULL string;
* - *ref_type_id* - referenced type ID, it might not exist yet;
* - *component_idx* - -1 for tagging reference type, otherwise struct/union
* member or function argument index;
* Set info->kflag to 1, indicating this tag is an __attribute__
* Returns:
* - >0, type ID of newly added BTF type;
* - <0, on error.
*/
int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id,
int component_idx)
{
return btf_add_decl_tag(btf, value, ref_type_id, component_idx, 1);
}

struct btf_ext_sec_info_param {
__u32 off;
__u32 len;
Expand Down
3 changes: 3 additions & 0 deletions tools/lib/bpf/btf.h
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ LIBBPF_API int btf__add_volatile(struct btf *btf, int ref_type_id);
LIBBPF_API int btf__add_const(struct btf *btf, int ref_type_id);
LIBBPF_API int btf__add_restrict(struct btf *btf, int ref_type_id);
LIBBPF_API int btf__add_type_tag(struct btf *btf, const char *value, int ref_type_id);
LIBBPF_API int btf__add_type_attr(struct btf *btf, const char *value, int ref_type_id);

/* func and func_proto construction APIs */
LIBBPF_API int btf__add_func(struct btf *btf, const char *name,
Expand All @@ -243,6 +244,8 @@ LIBBPF_API int btf__add_datasec_var_info(struct btf *btf, int var_type_id,
/* tag construction API */
LIBBPF_API int btf__add_decl_tag(struct btf *btf, const char *value, int ref_type_id,
int component_idx);
LIBBPF_API int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id,
int component_idx);

struct btf_dedup_opts {
size_t sz;
Expand Down
5 changes: 4 additions & 1 deletion tools/lib/bpf/btf_dump.c
Original file line number Diff line number Diff line change
Expand Up @@ -1494,7 +1494,10 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
case BTF_KIND_TYPE_TAG:
btf_dump_emit_mods(d, decls);
name = btf_name_of(d, t->name_off);
btf_dump_printf(d, " __attribute__((btf_type_tag(\"%s\")))", name);
if (btf_kflag(t))
btf_dump_printf(d, " __attribute__((%s))", name);
else
btf_dump_printf(d, " __attribute__((btf_type_tag(\"%s\")))", name);
break;
case BTF_KIND_ARRAY: {
const struct btf_array *a = btf_array(t);
Expand Down
2 changes: 2 additions & 0 deletions tools/lib/bpf/libbpf.map
Original file line number Diff line number Diff line change
Expand Up @@ -436,4 +436,6 @@ LIBBPF_1.6.0 {
bpf_linker__add_buf;
bpf_linker__add_fd;
bpf_linker__new_fd;
btf__add_decl_attr;
btf__add_type_attr;
} LIBBPF_1.5.0;
Loading

0 comments on commit 12befeb

Please sign in to comment.