Merge branch 'bpf: Add 64bit enum value support'

Yonghong Song says:

====================

Currently, btf only supports upto 32bit enum value with BTF_KIND_ENUM.
But in kernel, some enum has 64bit values, e.g., in uapi bpf.h, we have
  enum {
        BPF_F_INDEX_MASK                = 0xffffffffULL,
        BPF_F_CURRENT_CPU               = BPF_F_INDEX_MASK,
        BPF_F_CTXLEN_MASK               = (0xfffffULL << 32),
  };
With BTF_KIND_ENUM, the value for BPF_F_CTXLEN_MASK will be encoded
as 0 which is incorrect.

To solve this problem, BTF_KIND_ENUM64 is proposed in this patch set
to support enum 64bit values. Also, since sometimes there is a need
to generate C code from btf, e.g., vmlinux.h, btf kflag support
is also added for BTF_KIND_ENUM and BTF_KIND_ENUM64 to indicate
signedness, helping proper value printout.

Changelog:
  v4 -> v5:
    - skip newly-added enum64 C test if clang version <= 14.
  v3 -> v4:
    - rename btf_type_is_any_enum() to btf_is_any_enum() to favor
      consistency in libbpf.
    - fix sign extension issue in btf_dump_get_enum_value().
    - fix BPF_CORE_FIELD_SIGNED signedness issue in bpf_core_calc_field_relo().
  v2 -> v3:
    - Implement separate btf_equal_enum()/btf_equal_enum64() and
      btf_compat_enum()/btf_compat_enum64().
    - Add a new enum64 placeholder type dynamicly for enum64 sanitization.
    - For bpftool output and unit selftest, printed out signed/unsigned
      encoding as well.
    - fix some issues with BTF_KIND_ENUM is doc and clarified sign extension
      rules for enum values.
  v1 -> v2:
    - Changed kflag default from signed to unsigned
    - Fixed sanitization issue
    - Broke down libbpf related patches for easier review
    - Added more tests
    - More code refactorization
    - Corresponding llvm patch (to support enum64) is also updated
====================

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Alexei Starovoitov 2022-06-07 10:20:44 -07:00
commit d352bd889b
29 changed files with 1217 additions and 190 deletions

View File

@ -74,7 +74,7 @@ sequentially and type id is assigned to each recognized type starting from id
#define BTF_KIND_ARRAY 3 /* Array */
#define BTF_KIND_STRUCT 4 /* Struct */
#define BTF_KIND_UNION 5 /* Union */
#define BTF_KIND_ENUM 6 /* Enumeration */
#define BTF_KIND_ENUM 6 /* Enumeration up to 32-bit values */
#define BTF_KIND_FWD 7 /* Forward */
#define BTF_KIND_TYPEDEF 8 /* Typedef */
#define BTF_KIND_VOLATILE 9 /* Volatile */
@ -87,6 +87,7 @@ sequentially and type id is assigned to each recognized type starting from id
#define BTF_KIND_FLOAT 16 /* Floating point */
#define BTF_KIND_DECL_TAG 17 /* Decl Tag */
#define BTF_KIND_TYPE_TAG 18 /* Type Tag */
#define BTF_KIND_ENUM64 19 /* Enumeration up to 64-bit values */
Note that the type section encodes debug info, not just pure types.
``BTF_KIND_FUNC`` is not a type, and it represents a defined subprogram.
@ -101,10 +102,10 @@ 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 and fwd
* struct, union, fwd, enum and enum64.
*/
__u32 info;
/* "size" is used by INT, ENUM, STRUCT and UNION.
/* "size" is used by INT, ENUM, STRUCT, UNION and ENUM64.
* "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
@ -281,10 +282,10 @@ modes exist:
``struct btf_type`` encoding requirement:
* ``name_off``: 0 or offset to a valid C identifier
* ``info.kind_flag``: 0
* ``info.kind_flag``: 0 for unsigned, 1 for signed
* ``info.kind``: BTF_KIND_ENUM
* ``info.vlen``: number of enum values
* ``size``: 4
* ``size``: 1/2/4/8
``btf_type`` is followed by ``info.vlen`` number of ``struct btf_enum``.::
@ -297,6 +298,10 @@ The ``btf_enum`` encoding:
* ``name_off``: offset to a valid C identifier
* ``val``: any value
If the original enum value is signed and the size is less than 4,
that value will be sign extended into 4 bytes. If the size is 8,
the value will be truncated into 4 bytes.
2.2.7 BTF_KIND_FWD
~~~~~~~~~~~~~~~~~~
@ -493,7 +498,7 @@ 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.
2.2.17 BTF_KIND_TYPE_TAG
2.2.18 BTF_KIND_TYPE_TAG
~~~~~~~~~~~~~~~~~~~~~~~~
``struct btf_type`` encoding requirement:
@ -516,6 +521,32 @@ 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.
2.2.19 BTF_KIND_ENUM64
~~~~~~~~~~~~~~~~~~~~~~
``struct btf_type`` encoding requirement:
* ``name_off``: 0 or offset to a valid C identifier
* ``info.kind_flag``: 0 for unsigned, 1 for signed
* ``info.kind``: BTF_KIND_ENUM64
* ``info.vlen``: number of enum values
* ``size``: 1/2/4/8
``btf_type`` is followed by ``info.vlen`` number of ``struct btf_enum64``.::
struct btf_enum64 {
__u32 name_off;
__u32 val_lo32;
__u32 val_hi32;
};
The ``btf_enum64`` encoding:
* ``name_off``: offset to a valid C identifier
* ``val_lo32``: lower 32-bit value for a 64-bit value
* ``val_hi32``: high 32-bit value for a 64-bit value
If the original enum value is signed and the size is less than 8,
that value will be sign extended into 8 bytes.
3. BTF Kernel API
=================

View File

@ -177,6 +177,19 @@ static inline bool btf_type_is_enum(const struct btf_type *t)
return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM;
}
static inline bool btf_is_any_enum(const struct btf_type *t)
{
return BTF_INFO_KIND(t->info) == BTF_KIND_ENUM ||
BTF_INFO_KIND(t->info) == BTF_KIND_ENUM64;
}
static inline bool btf_kind_core_compat(const struct btf_type *t1,
const struct btf_type *t2)
{
return BTF_INFO_KIND(t1->info) == BTF_INFO_KIND(t2->info) ||
(btf_is_any_enum(t1) && btf_is_any_enum(t2));
}
static inline bool str_is_empty(const char *s)
{
return !s || !s[0];
@ -192,6 +205,16 @@ static inline bool btf_is_enum(const struct btf_type *t)
return btf_kind(t) == BTF_KIND_ENUM;
}
static inline bool btf_is_enum64(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_ENUM64;
}
static inline u64 btf_enum64_value(const struct btf_enum64 *e)
{
return ((u64)e->val_hi32 << 32) | e->val_lo32;
}
static inline bool btf_is_composite(const struct btf_type *t)
{
u16 kind = btf_kind(t);
@ -332,6 +355,11 @@ static inline struct btf_enum *btf_enum(const struct btf_type *t)
return (struct btf_enum *)(t + 1);
}
static inline struct btf_enum64 *btf_enum64(const struct btf_type *t)
{
return (struct btf_enum64 *)(t + 1);
}
static inline const struct btf_var_secinfo *btf_type_var_secinfo(
const struct btf_type *t)
{

View File

@ -36,10 +36,10 @@ 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 and fwd
* struct, union, enum, fwd and enum64
*/
__u32 info;
/* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC.
/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
* "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
@ -63,7 +63,7 @@ enum {
BTF_KIND_ARRAY = 3, /* Array */
BTF_KIND_STRUCT = 4, /* Struct */
BTF_KIND_UNION = 5, /* Union */
BTF_KIND_ENUM = 6, /* Enumeration */
BTF_KIND_ENUM = 6, /* Enumeration up to 32-bit values */
BTF_KIND_FWD = 7, /* Forward */
BTF_KIND_TYPEDEF = 8, /* Typedef */
BTF_KIND_VOLATILE = 9, /* Volatile */
@ -76,6 +76,7 @@ enum {
BTF_KIND_FLOAT = 16, /* Floating point */
BTF_KIND_DECL_TAG = 17, /* Decl Tag */
BTF_KIND_TYPE_TAG = 18, /* Type Tag */
BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */
NR_BTF_KINDS,
BTF_KIND_MAX = NR_BTF_KINDS - 1,
@ -186,4 +187,14 @@ struct btf_decl_tag {
__s32 component_idx;
};
/* BTF_KIND_ENUM64 is followed by multiple "struct btf_enum64".
* The exact number of btf_enum64 is stored in the vlen (of the
* info in "struct btf_type").
*/
struct btf_enum64 {
__u32 name_off;
__u32 val_lo32;
__u32 val_hi32;
};
#endif /* _UAPI__LINUX_BTF_H__ */

View File

@ -309,6 +309,7 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_FLOAT] = "FLOAT",
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
};
const char *btf_type_str(const struct btf_type *t)
@ -666,6 +667,7 @@ static bool btf_type_has_size(const struct btf_type *t)
case BTF_KIND_ENUM:
case BTF_KIND_DATASEC:
case BTF_KIND_FLOAT:
case BTF_KIND_ENUM64:
return true;
}
@ -711,6 +713,11 @@ static const struct btf_decl_tag *btf_type_decl_tag(const struct btf_type *t)
return (const struct btf_decl_tag *)(t + 1);
}
static const struct btf_enum64 *btf_type_enum64(const struct btf_type *t)
{
return (const struct btf_enum64 *)(t + 1);
}
static const struct btf_kind_operations *btf_type_ops(const struct btf_type *t)
{
return kind_ops[BTF_INFO_KIND(t->info)];
@ -1019,6 +1026,7 @@ static const char *btf_show_name(struct btf_show *show)
parens = "{";
break;
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
prefix = "enum";
break;
default:
@ -1834,6 +1842,7 @@ __btf_resolve_size(const struct btf *btf, const struct btf_type *type,
case BTF_KIND_UNION:
case BTF_KIND_ENUM:
case BTF_KIND_FLOAT:
case BTF_KIND_ENUM64:
size = type->size;
goto resolved;
@ -3670,6 +3679,7 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
{
const struct btf_enum *enums = btf_type_enum(t);
struct btf *btf = env->btf;
const char *fmt_str;
u16 i, nr_enums;
u32 meta_needed;
@ -3683,11 +3693,6 @@ static s32 btf_enum_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;
}
if (t->size > 8 || !is_power_of_2(t->size)) {
btf_verifier_log_type(env, t, "Unexpected size");
return -EINVAL;
@ -3718,7 +3723,8 @@ static s32 btf_enum_check_meta(struct btf_verifier_env *env,
if (env->log.level == BPF_LOG_KERNEL)
continue;
btf_verifier_log(env, "\t%s val=%d\n",
fmt_str = btf_type_kflag(t) ? "\t%s val=%d\n" : "\t%s val=%u\n";
btf_verifier_log(env, fmt_str,
__btf_name_by_offset(btf, enums[i].name_off),
enums[i].val);
}
@ -3759,7 +3765,10 @@ static void btf_enum_show(const struct btf *btf, const struct btf_type *t,
return;
}
btf_show_type_value(show, "%d", v);
if (btf_type_kflag(t))
btf_show_type_value(show, "%d", v);
else
btf_show_type_value(show, "%u", v);
btf_show_end_type(show);
}
@ -3772,6 +3781,109 @@ static struct btf_kind_operations enum_ops = {
.show = btf_enum_show,
};
static s32 btf_enum64_check_meta(struct btf_verifier_env *env,
const struct btf_type *t,
u32 meta_left)
{
const struct btf_enum64 *enums = btf_type_enum64(t);
struct btf *btf = env->btf;
const char *fmt_str;
u16 i, nr_enums;
u32 meta_needed;
nr_enums = btf_type_vlen(t);
meta_needed = nr_enums * sizeof(*enums);
if (meta_left < meta_needed) {
btf_verifier_log_basic(env, t,
"meta_left:%u meta_needed:%u",
meta_left, meta_needed);
return -EINVAL;
}
if (t->size > 8 || !is_power_of_2(t->size)) {
btf_verifier_log_type(env, t, "Unexpected size");
return -EINVAL;
}
/* enum type either no name or a valid one */
if (t->name_off &&
!btf_name_valid_identifier(env->btf, t->name_off)) {
btf_verifier_log_type(env, t, "Invalid name");
return -EINVAL;
}
btf_verifier_log_type(env, t, NULL);
for (i = 0; i < nr_enums; i++) {
if (!btf_name_offset_valid(btf, enums[i].name_off)) {
btf_verifier_log(env, "\tInvalid name_offset:%u",
enums[i].name_off);
return -EINVAL;
}
/* enum member must have a valid name */
if (!enums[i].name_off ||
!btf_name_valid_identifier(btf, enums[i].name_off)) {
btf_verifier_log_type(env, t, "Invalid name");
return -EINVAL;
}
if (env->log.level == BPF_LOG_KERNEL)
continue;
fmt_str = btf_type_kflag(t) ? "\t%s val=%lld\n" : "\t%s val=%llu\n";
btf_verifier_log(env, fmt_str,
__btf_name_by_offset(btf, enums[i].name_off),
btf_enum64_value(enums + i));
}
return meta_needed;
}
static void btf_enum64_show(const struct btf *btf, const struct btf_type *t,
u32 type_id, void *data, u8 bits_offset,
struct btf_show *show)
{
const struct btf_enum64 *enums = btf_type_enum64(t);
u32 i, nr_enums = btf_type_vlen(t);
void *safe_data;
s64 v;
safe_data = btf_show_start_type(show, t, type_id, data);
if (!safe_data)
return;
v = *(u64 *)safe_data;
for (i = 0; i < nr_enums; i++) {
if (v != btf_enum64_value(enums + i))
continue;
btf_show_type_value(show, "%s",
__btf_name_by_offset(btf,
enums[i].name_off));
btf_show_end_type(show);
return;
}
if (btf_type_kflag(t))
btf_show_type_value(show, "%lld", v);
else
btf_show_type_value(show, "%llu", v);
btf_show_end_type(show);
}
static struct btf_kind_operations enum64_ops = {
.check_meta = btf_enum64_check_meta,
.resolve = btf_df_resolve,
.check_member = btf_enum_check_member,
.check_kflag_member = btf_enum_check_kflag_member,
.log_details = btf_enum_log,
.show = btf_enum64_show,
};
static s32 btf_func_proto_check_meta(struct btf_verifier_env *env,
const struct btf_type *t,
u32 meta_left)
@ -4438,6 +4550,7 @@ static const struct btf_kind_operations * const kind_ops[NR_BTF_KINDS] = {
[BTF_KIND_FLOAT] = &float_ops,
[BTF_KIND_DECL_TAG] = &decl_tag_ops,
[BTF_KIND_TYPE_TAG] = &modifier_ops,
[BTF_KIND_ENUM64] = &enum64_ops,
};
static s32 btf_check_meta(struct btf_verifier_env *env,
@ -5299,7 +5412,7 @@ bool btf_ctx_access(int off, int size, enum bpf_access_type type,
/* skip modifiers */
while (btf_type_is_modifier(t))
t = btf_type_by_id(btf, t->type);
if (btf_type_is_small_int(t) || btf_type_is_enum(t))
if (btf_type_is_small_int(t) || btf_is_any_enum(t))
/* accessing a scalar */
return true;
if (!btf_type_is_ptr(t)) {
@ -5763,7 +5876,7 @@ static int __get_type_size(struct btf *btf, u32 btf_id,
if (btf_type_is_ptr(t))
/* kernel size of pointer. Not BPF's size of pointer*/
return sizeof(void *);
if (btf_type_is_int(t) || btf_type_is_enum(t))
if (btf_type_is_int(t) || btf_is_any_enum(t))
return t->size;
*bad_type = t;
return -EINVAL;
@ -5911,7 +6024,7 @@ static int btf_check_func_type_match(struct bpf_verifier_log *log,
* to context only. And only global functions can be replaced.
* Hence type check only those types.
*/
if (btf_type_is_int(t1) || btf_type_is_enum(t1))
if (btf_type_is_int(t1) || btf_is_any_enum(t1))
continue;
if (!btf_type_is_ptr(t1)) {
bpf_log(log,
@ -6408,7 +6521,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
t = btf_type_by_id(btf, t->type);
while (btf_type_is_modifier(t))
t = btf_type_by_id(btf, t->type);
if (!btf_type_is_int(t) && !btf_type_is_enum(t)) {
if (!btf_type_is_int(t) && !btf_is_any_enum(t)) {
bpf_log(log,
"Global function %s() doesn't return scalar. Only those are supported.\n",
tname);
@ -6423,7 +6536,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
t = btf_type_by_id(btf, args[i].type);
while (btf_type_is_modifier(t))
t = btf_type_by_id(btf, t->type);
if (btf_type_is_int(t) || btf_type_is_enum(t)) {
if (btf_type_is_int(t) || btf_is_any_enum(t)) {
reg->type = SCALAR_VALUE;
continue;
}
@ -7335,6 +7448,7 @@ recur:
case BTF_KIND_UNION:
case BTF_KIND_ENUM:
case BTF_KIND_FWD:
case BTF_KIND_ENUM64:
return 1;
case BTF_KIND_INT:
/* just reject deprecated bitfield-like integers; all other
@ -7387,10 +7501,10 @@ recur:
* field-based relocations. This function assumes that root types were already
* checked for name match. Beyond that initial root-level name check, names
* are completely ignored. Compatibility rules are as follows:
* - any two STRUCTs/UNIONs/FWDs/ENUMs/INTs are considered compatible, but
* - any two STRUCTs/UNIONs/FWDs/ENUMs/INTs/ENUM64s are considered compatible, but
* kind should match for local and target types (i.e., STRUCT is not
* compatible with UNION);
* - for ENUMs, the size is ignored;
* - for ENUMs/ENUM64s, the size is ignored;
* - for INT, size and signedness are ignored;
* - for ARRAY, dimensionality is ignored, element types are checked for
* compatibility recursively;

View File

@ -10901,7 +10901,7 @@ static int check_btf_func(struct bpf_verifier_env *env,
goto err_free;
ret_type = btf_type_skip_modifiers(btf, func_proto->type, NULL);
scalar_return =
btf_type_is_small_int(ret_type) || btf_type_is_enum(ret_type);
btf_type_is_small_int(ret_type) || btf_is_any_enum(ret_type);
if (i && !scalar_return && env->subprog_info[i].has_ld_abs) {
verbose(env, "LD_ABS is only allowed in functions that return 'int'.\n");
goto err_free;

View File

@ -40,6 +40,7 @@ static const char * const btf_kind_str[NR_BTF_KINDS] = {
[BTF_KIND_FLOAT] = "FLOAT",
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
};
struct btf_attach_point {
@ -212,15 +213,18 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
case BTF_KIND_ENUM: {
const struct btf_enum *v = (const void *)(t + 1);
__u16 vlen = BTF_INFO_VLEN(t->info);
const char *encoding;
int i;
encoding = btf_kflag(t) ? "SIGNED" : "UNSIGNED";
if (json_output) {
jsonw_string_field(w, "encoding", encoding);
jsonw_uint_field(w, "size", t->size);
jsonw_uint_field(w, "vlen", vlen);
jsonw_name(w, "values");
jsonw_start_array(w);
} else {
printf(" size=%u vlen=%u", t->size, vlen);
printf(" encoding=%s size=%u vlen=%u", encoding, t->size, vlen);
}
for (i = 0; i < vlen; i++, v++) {
const char *name = btf_str(btf, v->name_off);
@ -228,10 +232,57 @@ static int dump_btf_type(const struct btf *btf, __u32 id,
if (json_output) {
jsonw_start_object(w);
jsonw_string_field(w, "name", name);
jsonw_uint_field(w, "val", v->val);
if (btf_kflag(t))
jsonw_int_field(w, "val", v->val);
else
jsonw_uint_field(w, "val", v->val);
jsonw_end_object(w);
} else {
printf("\n\t'%s' val=%u", name, v->val);
if (btf_kflag(t))
printf("\n\t'%s' val=%d", name, v->val);
else
printf("\n\t'%s' val=%u", name, v->val);
}
}
if (json_output)
jsonw_end_array(w);
break;
}
case BTF_KIND_ENUM64: {
const struct btf_enum64 *v = btf_enum64(t);
__u16 vlen = btf_vlen(t);
const char *encoding;
int i;
encoding = btf_kflag(t) ? "SIGNED" : "UNSIGNED";
if (json_output) {
jsonw_string_field(w, "encoding", encoding);
jsonw_uint_field(w, "size", t->size);
jsonw_uint_field(w, "vlen", vlen);
jsonw_name(w, "values");
jsonw_start_array(w);
} else {
printf(" encoding=%s size=%u vlen=%u", encoding, t->size, vlen);
}
for (i = 0; i < vlen; i++, v++) {
const char *name = btf_str(btf, v->name_off);
__u64 val = ((__u64)v->val_hi32 << 32) | v->val_lo32;
if (json_output) {
jsonw_start_object(w);
jsonw_string_field(w, "name", name);
if (btf_kflag(t))
jsonw_int_field(w, "val", val);
else
jsonw_uint_field(w, "val", val);
jsonw_end_object(w);
} else {
if (btf_kflag(t))
printf("\n\t'%s' val=%lldLL", name,
(unsigned long long)val);
else
printf("\n\t'%s' val=%lluULL", name,
(unsigned long long)val);
}
}
if (json_output)

View File

@ -182,6 +182,32 @@ static int btf_dumper_enum(const struct btf_dumper *d,
return 0;
}
static int btf_dumper_enum64(const struct btf_dumper *d,
const struct btf_type *t,
const void *data)
{
const struct btf_enum64 *enums = btf_enum64(t);
__u32 val_lo32, val_hi32;
__u64 value;
__u16 i;
value = *(__u64 *)data;
val_lo32 = (__u32)value;
val_hi32 = value >> 32;
for (i = 0; i < btf_vlen(t); i++) {
if (val_lo32 == enums[i].val_lo32 && val_hi32 == enums[i].val_hi32) {
jsonw_string(d->jw,
btf__name_by_offset(d->btf,
enums[i].name_off));
return 0;
}
}
jsonw_int(d->jw, value);
return 0;
}
static bool is_str_array(const struct btf *btf, const struct btf_array *arr,
const char *s)
{
@ -542,6 +568,8 @@ static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
return btf_dumper_array(d, type_id, data);
case BTF_KIND_ENUM:
return btf_dumper_enum(d, t, data);
case BTF_KIND_ENUM64:
return btf_dumper_enum64(d, t, data);
case BTF_KIND_PTR:
btf_dumper_ptr(d, t, data);
return 0;
@ -618,6 +646,7 @@ static int __btf_dumper_type_only(const struct btf *btf, __u32 type_id,
btf__name_by_offset(btf, t->name_off));
break;
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
BTF_PRINT_ARG("enum %s ",
btf__name_by_offset(btf, t->name_off));
break;

View File

@ -1750,6 +1750,7 @@ btfgen_mark_type(struct btfgen_info *info, unsigned int type_id, bool follow_poi
case BTF_KIND_INT:
case BTF_KIND_FLOAT:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
break;

View File

@ -36,10 +36,10 @@ 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 and fwd
* struct, union, enum, fwd and enum64
*/
__u32 info;
/* "size" is used by INT, ENUM, STRUCT, UNION and DATASEC.
/* "size" is used by INT, ENUM, STRUCT, UNION, DATASEC and ENUM64.
* "size" tells the size of the type it is describing.
*
* "type" is used by PTR, TYPEDEF, VOLATILE, CONST, RESTRICT,
@ -63,7 +63,7 @@ enum {
BTF_KIND_ARRAY = 3, /* Array */
BTF_KIND_STRUCT = 4, /* Struct */
BTF_KIND_UNION = 5, /* Union */
BTF_KIND_ENUM = 6, /* Enumeration */
BTF_KIND_ENUM = 6, /* Enumeration up to 32-bit values */
BTF_KIND_FWD = 7, /* Forward */
BTF_KIND_TYPEDEF = 8, /* Typedef */
BTF_KIND_VOLATILE = 9, /* Volatile */
@ -76,6 +76,7 @@ enum {
BTF_KIND_FLOAT = 16, /* Floating point */
BTF_KIND_DECL_TAG = 17, /* Decl Tag */
BTF_KIND_TYPE_TAG = 18, /* Type Tag */
BTF_KIND_ENUM64 = 19, /* Enumeration up to 64-bit values */
NR_BTF_KINDS,
BTF_KIND_MAX = NR_BTF_KINDS - 1,
@ -186,4 +187,14 @@ struct btf_decl_tag {
__s32 component_idx;
};
/* BTF_KIND_ENUM64 is followed by multiple "struct btf_enum64".
* The exact number of btf_enum64 is stored in the vlen (of the
* info in "struct btf_type").
*/
struct btf_enum64 {
__u32 name_off;
__u32 val_lo32;
__u32 val_hi32;
};
#endif /* _UAPI__LINUX_BTF_H__ */

View File

@ -305,6 +305,8 @@ static int btf_type_size(const struct btf_type *t)
return base_size + sizeof(__u32);
case BTF_KIND_ENUM:
return base_size + vlen * sizeof(struct btf_enum);
case BTF_KIND_ENUM64:
return base_size + vlen * sizeof(struct btf_enum64);
case BTF_KIND_ARRAY:
return base_size + sizeof(struct btf_array);
case BTF_KIND_STRUCT:
@ -334,6 +336,7 @@ static void btf_bswap_type_base(struct btf_type *t)
static int btf_bswap_type_rest(struct btf_type *t)
{
struct btf_var_secinfo *v;
struct btf_enum64 *e64;
struct btf_member *m;
struct btf_array *a;
struct btf_param *p;
@ -361,6 +364,13 @@ static int btf_bswap_type_rest(struct btf_type *t)
e->val = bswap_32(e->val);
}
return 0;
case BTF_KIND_ENUM64:
for (i = 0, e64 = btf_enum64(t); i < vlen; i++, e64++) {
e64->name_off = bswap_32(e64->name_off);
e64->val_lo32 = bswap_32(e64->val_lo32);
e64->val_hi32 = bswap_32(e64->val_hi32);
}
return 0;
case BTF_KIND_ARRAY:
a = btf_array(t);
a->type = bswap_32(a->type);
@ -611,6 +621,7 @@ __s64 btf__resolve_size(const struct btf *btf, __u32 type_id)
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
case BTF_KIND_DATASEC:
case BTF_KIND_FLOAT:
size = t->size;
@ -658,6 +669,7 @@ int btf__align_of(const struct btf *btf, __u32 id)
switch (kind) {
case BTF_KIND_INT:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
case BTF_KIND_FLOAT:
return min(btf_ptr_sz(btf), (size_t)t->size);
case BTF_KIND_PTR:
@ -2129,20 +2141,8 @@ int btf__add_field(struct btf *btf, const char *name, int type_id,
return 0;
}
/*
* Append new BTF_KIND_ENUM type with:
* - *name* - name of the enum, can be NULL or empty for anonymous enums;
* - *byte_sz* - size of the enum, in bytes.
*
* Enum initially has no enum values in it (and corresponds to enum forward
* declaration). Enumerator values can be added by btf__add_enum_value()
* immediately after btf__add_enum() succeeds.
*
* Returns:
* - >0, type ID of newly added BTF type;
* - <0, on error.
*/
int btf__add_enum(struct btf *btf, const char *name, __u32 byte_sz)
static int btf_add_enum_common(struct btf *btf, const char *name, __u32 byte_sz,
bool is_signed, __u8 kind)
{
struct btf_type *t;
int sz, name_off = 0;
@ -2167,12 +2167,34 @@ int btf__add_enum(struct btf *btf, const char *name, __u32 byte_sz)
/* start out with vlen=0; it will be adjusted when adding enum values */
t->name_off = name_off;
t->info = btf_type_info(BTF_KIND_ENUM, 0, 0);
t->info = btf_type_info(kind, 0, is_signed);
t->size = byte_sz;
return btf_commit_type(btf, sz);
}
/*
* Append new BTF_KIND_ENUM type with:
* - *name* - name of the enum, can be NULL or empty for anonymous enums;
* - *byte_sz* - size of the enum, in bytes.
*
* Enum initially has no enum values in it (and corresponds to enum forward
* declaration). Enumerator values can be added by btf__add_enum_value()
* immediately after btf__add_enum() succeeds.
*
* Returns:
* - >0, type ID of newly added BTF type;
* - <0, on error.
*/
int btf__add_enum(struct btf *btf, const char *name, __u32 byte_sz)
{
/*
* set the signedness to be unsigned, it will change to signed
* if any later enumerator is negative.
*/
return btf_add_enum_common(btf, name, byte_sz, false, BTF_KIND_ENUM);
}
/*
* Append new enum value for the current ENUM type with:
* - *name* - name of the enumerator value, can't be NULL or empty;
@ -2220,6 +2242,82 @@ int btf__add_enum_value(struct btf *btf, const char *name, __s64 value)
t = btf_last_type(btf);
btf_type_inc_vlen(t);
/* if negative value, set signedness to signed */
if (value < 0)
t->info = btf_type_info(btf_kind(t), btf_vlen(t), true);
btf->hdr->type_len += sz;
btf->hdr->str_off += sz;
return 0;
}
/*
* Append new BTF_KIND_ENUM64 type with:
* - *name* - name of the enum, can be NULL or empty for anonymous enums;
* - *byte_sz* - size of the enum, in bytes.
* - *is_signed* - whether the enum values are signed or not;
*
* Enum initially has no enum values in it (and corresponds to enum forward
* declaration). Enumerator values can be added by btf__add_enum64_value()
* immediately after btf__add_enum64() succeeds.
*
* Returns:
* - >0, type ID of newly added BTF type;
* - <0, on error.
*/
int btf__add_enum64(struct btf *btf, const char *name, __u32 byte_sz,
bool is_signed)
{
return btf_add_enum_common(btf, name, byte_sz, is_signed,
BTF_KIND_ENUM64);
}
/*
* Append new enum value for the current ENUM64 type with:
* - *name* - name of the enumerator value, can't be NULL or empty;
* - *value* - integer value corresponding to enum value *name*;
* Returns:
* - 0, on success;
* - <0, on error.
*/
int btf__add_enum64_value(struct btf *btf, const char *name, __u64 value)
{
struct btf_enum64 *v;
struct btf_type *t;
int sz, name_off;
/* last type should be BTF_KIND_ENUM64 */
if (btf->nr_types == 0)
return libbpf_err(-EINVAL);
t = btf_last_type(btf);
if (!btf_is_enum64(t))
return libbpf_err(-EINVAL);
/* non-empty name */
if (!name || !name[0])
return libbpf_err(-EINVAL);
/* decompose and invalidate raw data */
if (btf_ensure_modifiable(btf))
return libbpf_err(-ENOMEM);
sz = sizeof(struct btf_enum64);
v = btf_add_type_mem(btf, sz);
if (!v)
return libbpf_err(-ENOMEM);
name_off = btf__add_str(btf, name);
if (name_off < 0)
return name_off;
v->name_off = name_off;
v->val_lo32 = (__u32)value;
v->val_hi32 = value >> 32;
/* update parent type's vlen */
t = btf_last_type(btf);
btf_type_inc_vlen(t);
btf->hdr->type_len += sz;
btf->hdr->str_off += sz;
return 0;
@ -3484,7 +3582,7 @@ static bool btf_equal_int_tag(struct btf_type *t1, struct btf_type *t2)
return info1 == info2;
}
/* Calculate type signature hash of ENUM. */
/* Calculate type signature hash of ENUM/ENUM64. */
static long btf_hash_enum(struct btf_type *t)
{
long h;
@ -3518,9 +3616,31 @@ static bool btf_equal_enum(struct btf_type *t1, struct btf_type *t2)
return true;
}
static bool btf_equal_enum64(struct btf_type *t1, struct btf_type *t2)
{
const struct btf_enum64 *m1, *m2;
__u16 vlen;
int i;
if (!btf_equal_common(t1, t2))
return false;
vlen = btf_vlen(t1);
m1 = btf_enum64(t1);
m2 = btf_enum64(t2);
for (i = 0; i < vlen; i++) {
if (m1->name_off != m2->name_off || m1->val_lo32 != m2->val_lo32 ||
m1->val_hi32 != m2->val_hi32)
return false;
m1++;
m2++;
}
return true;
}
static inline bool btf_is_enum_fwd(struct btf_type *t)
{
return btf_is_enum(t) && btf_vlen(t) == 0;
return btf_is_any_enum(t) && btf_vlen(t) == 0;
}
static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)
@ -3533,6 +3653,17 @@ static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)
t1->size == t2->size;
}
static bool btf_compat_enum64(struct btf_type *t1, struct btf_type *t2)
{
if (!btf_is_enum_fwd(t1) && !btf_is_enum_fwd(t2))
return btf_equal_enum64(t1, t2);
/* ignore vlen when comparing */
return t1->name_off == t2->name_off &&
(t1->info & ~0xffff) == (t2->info & ~0xffff) &&
t1->size == t2->size;
}
/*
* Calculate type signature hash of STRUCT/UNION, ignoring referenced type IDs,
* as referenced type IDs equivalence is established separately during type
@ -3745,6 +3876,7 @@ static int btf_dedup_prep(struct btf_dedup *d)
h = btf_hash_int_decl_tag(t);
break;
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
h = btf_hash_enum(t);
break;
case BTF_KIND_STRUCT:
@ -3834,6 +3966,27 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
}
break;
case BTF_KIND_ENUM64:
h = btf_hash_enum(t);
for_each_dedup_cand(d, hash_entry, h) {
cand_id = (__u32)(long)hash_entry->value;
cand = btf_type_by_id(d->btf, cand_id);
if (btf_equal_enum64(t, cand)) {
new_id = cand_id;
break;
}
if (btf_compat_enum64(t, cand)) {
if (btf_is_enum_fwd(t)) {
/* resolve fwd to full enum */
new_id = cand_id;
break;
}
/* resolve canonical enum fwd to full enum */
d->map[cand_id] = type_id;
}
}
break;
case BTF_KIND_FWD:
case BTF_KIND_FLOAT:
h = btf_hash_common(t);
@ -4129,6 +4282,9 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id,
case BTF_KIND_ENUM:
return btf_compat_enum(cand_type, canon_type);
case BTF_KIND_ENUM64:
return btf_compat_enum64(cand_type, canon_type);
case BTF_KIND_FWD:
case BTF_KIND_FLOAT:
return btf_equal_common(cand_type, canon_type);
@ -4731,6 +4887,7 @@ int btf_type_visit_type_ids(struct btf_type *t, type_id_visit_fn visit, void *ct
case BTF_KIND_INT:
case BTF_KIND_FLOAT:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
return 0;
case BTF_KIND_FWD:
@ -4825,6 +4982,16 @@ int btf_type_visit_str_offs(struct btf_type *t, str_off_visit_fn visit, void *ct
}
break;
}
case BTF_KIND_ENUM64: {
struct btf_enum64 *m = btf_enum64(t);
for (i = 0, n = btf_vlen(t); i < n; i++, m++) {
err = visit(&m->name_off, ctx);
if (err)
return err;
}
break;
}
case BTF_KIND_FUNC_PROTO: {
struct btf_param *m = btf_params(t);

View File

@ -215,6 +215,8 @@ LIBBPF_API int btf__add_field(struct btf *btf, const char *name, int field_type_
/* enum construction APIs */
LIBBPF_API int btf__add_enum(struct btf *btf, const char *name, __u32 bytes_sz);
LIBBPF_API int btf__add_enum_value(struct btf *btf, const char *name, __s64 value);
LIBBPF_API int btf__add_enum64(struct btf *btf, const char *name, __u32 bytes_sz, bool is_signed);
LIBBPF_API int btf__add_enum64_value(struct btf *btf, const char *name, __u64 value);
enum btf_fwd_kind {
BTF_FWD_STRUCT = 0,
@ -393,9 +395,10 @@ btf_dump__dump_type_data(struct btf_dump *d, __u32 id,
#ifndef BTF_KIND_FLOAT
#define BTF_KIND_FLOAT 16 /* Floating point */
#endif
/* The kernel header switched to enums, so these two were never #defined */
/* The kernel header switched to enums, so the following were never #defined */
#define BTF_KIND_DECL_TAG 17 /* Decl Tag */
#define BTF_KIND_TYPE_TAG 18 /* Type Tag */
#define BTF_KIND_ENUM64 19 /* Enum for up-to 64bit values */
static inline __u16 btf_kind(const struct btf_type *t)
{
@ -454,6 +457,11 @@ static inline bool btf_is_enum(const struct btf_type *t)
return btf_kind(t) == BTF_KIND_ENUM;
}
static inline bool btf_is_enum64(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_ENUM64;
}
static inline bool btf_is_fwd(const struct btf_type *t)
{
return btf_kind(t) == BTF_KIND_FWD;
@ -524,6 +532,18 @@ static inline bool btf_is_type_tag(const struct btf_type *t)
return btf_kind(t) == BTF_KIND_TYPE_TAG;
}
static inline bool btf_is_any_enum(const struct btf_type *t)
{
return btf_is_enum(t) || btf_is_enum64(t);
}
static inline bool btf_kind_core_compat(const struct btf_type *t1,
const struct btf_type *t2)
{
return btf_kind(t1) == btf_kind(t2) ||
(btf_is_any_enum(t1) && btf_is_any_enum(t2));
}
static inline __u8 btf_int_encoding(const struct btf_type *t)
{
return BTF_INT_ENCODING(*(__u32 *)(t + 1));
@ -549,6 +569,16 @@ static inline struct btf_enum *btf_enum(const struct btf_type *t)
return (struct btf_enum *)(t + 1);
}
static inline struct btf_enum64 *btf_enum64(const struct btf_type *t)
{
return (struct btf_enum64 *)(t + 1);
}
static inline __u64 btf_enum64_value(const struct btf_enum64 *e)
{
return ((__u64)e->val_hi32 << 32) | e->val_lo32;
}
static inline struct btf_member *btf_members(const struct btf_type *t)
{
return (struct btf_member *)(t + 1);

View File

@ -318,6 +318,7 @@ static int btf_dump_mark_referenced(struct btf_dump *d)
switch (btf_kind(t)) {
case BTF_KIND_INT:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
case BTF_KIND_FWD:
case BTF_KIND_FLOAT:
break;
@ -538,6 +539,7 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr)
return 1;
}
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
case BTF_KIND_FWD:
/*
* non-anonymous or non-referenced enums are top-level
@ -739,6 +741,7 @@ static void btf_dump_emit_type(struct btf_dump *d, __u32 id, __u32 cont_id)
tstate->emit_state = EMITTED;
break;
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
if (top_level_def) {
btf_dump_emit_enum_def(d, id, t, 0);
btf_dump_printf(d, ";\n\n");
@ -989,38 +992,81 @@ static void btf_dump_emit_enum_fwd(struct btf_dump *d, __u32 id,
btf_dump_printf(d, "enum %s", btf_dump_type_name(d, id));
}
static void btf_dump_emit_enum32_val(struct btf_dump *d,
const struct btf_type *t,
int lvl, __u16 vlen)
{
const struct btf_enum *v = btf_enum(t);
bool is_signed = btf_kflag(t);
const char *fmt_str;
const char *name;
size_t dup_cnt;
int i;
for (i = 0; i < vlen; i++, v++) {
name = btf_name_of(d, v->name_off);
/* enumerators share namespace with typedef idents */
dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
if (dup_cnt > 1) {
fmt_str = is_signed ? "\n%s%s___%zd = %d," : "\n%s%s___%zd = %u,";
btf_dump_printf(d, fmt_str, pfx(lvl + 1), name, dup_cnt, v->val);
} else {
fmt_str = is_signed ? "\n%s%s = %d," : "\n%s%s = %u,";
btf_dump_printf(d, fmt_str, pfx(lvl + 1), name, v->val);
}
}
}
static void btf_dump_emit_enum64_val(struct btf_dump *d,
const struct btf_type *t,
int lvl, __u16 vlen)
{
const struct btf_enum64 *v = btf_enum64(t);
bool is_signed = btf_kflag(t);
const char *fmt_str;
const char *name;
size_t dup_cnt;
__u64 val;
int i;
for (i = 0; i < vlen; i++, v++) {
name = btf_name_of(d, v->name_off);
dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
val = btf_enum64_value(v);
if (dup_cnt > 1) {
fmt_str = is_signed ? "\n%s%s___%zd = %lldLL,"
: "\n%s%s___%zd = %lluULL,";
btf_dump_printf(d, fmt_str,
pfx(lvl + 1), name, dup_cnt,
(unsigned long long)val);
} else {
fmt_str = is_signed ? "\n%s%s = %lldLL,"
: "\n%s%s = %lluULL,";
btf_dump_printf(d, fmt_str,
pfx(lvl + 1), name,
(unsigned long long)val);
}
}
}
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 = btf_enum(t);
__u16 vlen = btf_vlen(t);
const char *name;
size_t dup_cnt;
int i;
btf_dump_printf(d, "enum%s%s",
t->name_off ? " " : "",
btf_dump_type_name(d, id));
if (vlen) {
btf_dump_printf(d, " {");
for (i = 0; i < vlen; i++, v++) {
name = btf_name_of(d, v->name_off);
/* enumerators share namespace with typedef idents */
dup_cnt = btf_dump_name_dups(d, d->ident_names, name);
if (dup_cnt > 1) {
btf_dump_printf(d, "\n%s%s___%zu = %u,",
pfx(lvl + 1), name, dup_cnt,
(__u32)v->val);
} else {
btf_dump_printf(d, "\n%s%s = %u,",
pfx(lvl + 1), name,
(__u32)v->val);
}
}
btf_dump_printf(d, "\n%s}", pfx(lvl));
}
if (!vlen)
return;
btf_dump_printf(d, " {");
if (btf_is_enum(t))
btf_dump_emit_enum32_val(d, t, lvl, vlen);
else
btf_dump_emit_enum64_val(d, t, lvl, vlen);
btf_dump_printf(d, "\n%s}", pfx(lvl));
}
static void btf_dump_emit_fwd_def(struct btf_dump *d, __u32 id,
@ -1178,6 +1224,7 @@ skip_mod:
break;
case BTF_KIND_INT:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
case BTF_KIND_FWD:
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
@ -1312,6 +1359,7 @@ static void btf_dump_emit_type_chain(struct btf_dump *d,
btf_dump_emit_struct_fwd(d, id, t);
break;
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
btf_dump_emit_mods(d, decls);
/* inline anonymous enum */
if (t->name_off == 0 && !d->skip_anon_defs)
@ -1988,7 +2036,8 @@ static int btf_dump_get_enum_value(struct btf_dump *d,
__u32 id,
__s64 *value)
{
/* handle unaligned enum value */
bool is_signed = btf_kflag(t);
if (!ptr_is_aligned(d->btf, id, data)) {
__u64 val;
int err;
@ -2005,13 +2054,13 @@ static int btf_dump_get_enum_value(struct btf_dump *d,
*value = *(__s64 *)data;
return 0;
case 4:
*value = *(__s32 *)data;
*value = is_signed ? *(__s32 *)data : *(__u32 *)data;
return 0;
case 2:
*value = *(__s16 *)data;
*value = is_signed ? *(__s16 *)data : *(__u16 *)data;
return 0;
case 1:
*value = *(__s8 *)data;
*value = is_signed ? *(__s8 *)data : *(__u8 *)data;
return 0;
default:
pr_warn("unexpected size %d for enum, id:[%u]\n", t->size, id);
@ -2024,7 +2073,7 @@ static int btf_dump_enum_data(struct btf_dump *d,
__u32 id,
const void *data)
{
const struct btf_enum *e;
bool is_signed;
__s64 value;
int i, err;
@ -2032,14 +2081,31 @@ static int btf_dump_enum_data(struct btf_dump *d,
if (err)
return err;
for (i = 0, e = btf_enum(t); i < btf_vlen(t); i++, e++) {
if (value != e->val)
continue;
btf_dump_type_values(d, "%s", btf_name_of(d, e->name_off));
return 0;
}
is_signed = btf_kflag(t);
if (btf_is_enum(t)) {
const struct btf_enum *e;
btf_dump_type_values(d, "%d", value);
for (i = 0, e = btf_enum(t); i < btf_vlen(t); i++, e++) {
if (value != e->val)
continue;
btf_dump_type_values(d, "%s", btf_name_of(d, e->name_off));
return 0;
}
btf_dump_type_values(d, is_signed ? "%d" : "%u", value);
} else {
const struct btf_enum64 *e;
for (i = 0, e = btf_enum64(t); i < btf_vlen(t); i++, e++) {
if (value != btf_enum64_value(e))
continue;
btf_dump_type_values(d, "%s", btf_name_of(d, e->name_off));
return 0;
}
btf_dump_type_values(d, is_signed ? "%lldLL" : "%lluULL",
(unsigned long long)value);
}
return 0;
}
@ -2099,6 +2165,7 @@ static int btf_dump_type_data_check_overflow(struct btf_dump *d,
case BTF_KIND_FLOAT:
case BTF_KIND_PTR:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
if (data + bits_offset / 8 + size > d->typed_dump->data_end)
return -E2BIG;
break;
@ -2203,6 +2270,7 @@ static int btf_dump_type_data_check_zero(struct btf_dump *d,
return -ENODATA;
}
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
err = btf_dump_get_enum_value(d, t, data, id, &value);
if (err)
return err;
@ -2275,6 +2343,7 @@ static int btf_dump_dump_type_data(struct btf_dump *d,
err = btf_dump_struct_data(d, t, id, data);
break;
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
/* handle bitfield and int enum values */
if (bit_sz) {
__u64 print_num;

View File

@ -2242,6 +2242,7 @@ static const char *__btf_kind_str(__u16 kind)
case BTF_KIND_FLOAT: return "float";
case BTF_KIND_DECL_TAG: return "decl_tag";
case BTF_KIND_TYPE_TAG: return "type_tag";
case BTF_KIND_ENUM64: return "enum64";
default: return "unknown";
}
}
@ -2770,12 +2771,13 @@ static bool btf_needs_sanitization(struct bpf_object *obj)
bool has_func = kernel_supports(obj, FEAT_BTF_FUNC);
bool has_decl_tag = kernel_supports(obj, FEAT_BTF_DECL_TAG);
bool has_type_tag = kernel_supports(obj, FEAT_BTF_TYPE_TAG);
bool has_enum64 = kernel_supports(obj, FEAT_BTF_ENUM64);
return !has_func || !has_datasec || !has_func_global || !has_float ||
!has_decl_tag || !has_type_tag;
!has_decl_tag || !has_type_tag || !has_enum64;
}
static void bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
static int bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
{
bool has_func_global = kernel_supports(obj, FEAT_BTF_GLOBAL_FUNC);
bool has_datasec = kernel_supports(obj, FEAT_BTF_DATASEC);
@ -2783,6 +2785,8 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
bool has_func = kernel_supports(obj, FEAT_BTF_FUNC);
bool has_decl_tag = kernel_supports(obj, FEAT_BTF_DECL_TAG);
bool has_type_tag = kernel_supports(obj, FEAT_BTF_TYPE_TAG);
bool has_enum64 = kernel_supports(obj, FEAT_BTF_ENUM64);
__u32 enum64_placeholder_id = 0;
struct btf_type *t;
int i, j, vlen;
@ -2845,8 +2849,32 @@ static void bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
/* replace TYPE_TAG with a CONST */
t->name_off = 0;
t->info = BTF_INFO_ENC(BTF_KIND_CONST, 0, 0);
}
} else if (!has_enum64 && btf_is_enum(t)) {
/* clear the kflag */
t->info = btf_type_info(btf_kind(t), btf_vlen(t), false);
} else if (!has_enum64 && btf_is_enum64(t)) {
/* replace ENUM64 with a union */
struct btf_member *m;
if (enum64_placeholder_id == 0) {
enum64_placeholder_id = btf__add_int(btf, "enum64_placeholder", 1, 0);
if (enum64_placeholder_id < 0)
return enum64_placeholder_id;
t = (struct btf_type *)btf__type_by_id(btf, i);
}
m = btf_members(t);
vlen = btf_vlen(t);
t->info = BTF_INFO_ENC(BTF_KIND_UNION, 0, vlen);
for (j = 0; j < vlen; j++, m++) {
m->type = enum64_placeholder_id;
m->offset = 0;
}
}
}
return 0;
}
static bool libbpf_needs_btf(const struct bpf_object *obj)
@ -3184,7 +3212,9 @@ static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj)
/* enforce 8-byte pointers for BPF-targeted BTFs */
btf__set_pointer_size(obj->btf, 8);
bpf_object__sanitize_btf(obj, kern_btf);
err = bpf_object__sanitize_btf(obj, kern_btf);
if (err)
return err;
}
if (obj->gen_loader) {
@ -3691,6 +3721,10 @@ static enum kcfg_type find_kcfg_type(const struct btf *btf, int id,
if (strcmp(name, "libbpf_tristate"))
return KCFG_UNKNOWN;
return KCFG_TRISTATE;
case BTF_KIND_ENUM64:
if (strcmp(name, "libbpf_tristate"))
return KCFG_UNKNOWN;
return KCFG_TRISTATE;
case BTF_KIND_ARRAY:
if (btf_array(t)->nelems == 0)
return KCFG_UNKNOWN;
@ -4874,6 +4908,17 @@ static int probe_kern_bpf_cookie(void)
return probe_fd(ret);
}
static int probe_kern_btf_enum64(void)
{
static const char strs[] = "\0enum64";
__u32 types[] = {
BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 0), 8),
};
return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
strs, sizeof(strs)));
}
enum kern_feature_result {
FEAT_UNKNOWN = 0,
FEAT_SUPPORTED = 1,
@ -4939,6 +4984,9 @@ static struct kern_feature_desc {
[FEAT_BPF_COOKIE] = {
"BPF cookie support", probe_kern_bpf_cookie,
},
[FEAT_BTF_ENUM64] = {
"BTF_KIND_ENUM64 support", probe_kern_btf_enum64,
},
};
bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id)
@ -5476,7 +5524,7 @@ int bpf_core_add_cands(struct bpf_core_cand *local_cand,
n = btf__type_cnt(targ_btf);
for (i = targ_start_id; i < n; i++) {
t = btf__type_by_id(targ_btf, i);
if (btf_kind(t) != btf_kind(local_t))
if (!btf_kind_core_compat(t, local_t))
continue;
targ_name = btf__name_by_offset(targ_btf, t->name_off);
@ -5690,7 +5738,7 @@ int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
/* caller made sure that names match (ignoring flavor suffix) */
local_type = btf__type_by_id(local_btf, local_id);
targ_type = btf__type_by_id(targ_btf, targ_id);
if (btf_kind(local_type) != btf_kind(targ_type))
if (!btf_kind_core_compat(local_type, targ_type))
return 0;
recur:
@ -5703,7 +5751,7 @@ recur:
if (!local_type || !targ_type)
return -EINVAL;
if (btf_kind(local_type) != btf_kind(targ_type))
if (!btf_kind_core_compat(local_type, targ_type))
return 0;
switch (btf_kind(local_type)) {
@ -5711,6 +5759,7 @@ recur:
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
case BTF_KIND_FWD:
return 1;
case BTF_KIND_INT:

View File

@ -462,6 +462,8 @@ LIBBPF_0.8.0 {
LIBBPF_1.0.0 {
global:
btf__add_enum64;
btf__add_enum64_value;
libbpf_bpf_attach_type_str;
libbpf_bpf_link_type_str;
libbpf_bpf_map_type_str;

View File

@ -351,6 +351,8 @@ enum kern_feature_id {
FEAT_MEMCG_ACCOUNT,
/* BPF cookie (bpf_get_attach_cookie() BPF helper) support */
FEAT_BPF_COOKIE,
/* BTF_KIND_ENUM64 support and BTF_KIND_ENUM kflag support */
FEAT_BTF_ENUM64,
__FEAT_CNT,
};

View File

@ -1335,6 +1335,7 @@ recur:
case BTF_KIND_STRUCT:
case BTF_KIND_UNION:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
case BTF_KIND_FWD:
case BTF_KIND_FUNC:
case BTF_KIND_VAR:
@ -1357,6 +1358,7 @@ recur:
case BTF_KIND_INT:
case BTF_KIND_FLOAT:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
/* ignore encoding for int and enum values for enum */
if (t1->size != t2->size) {
pr_warn("global '%s': incompatible %s '%s' size %u and %u\n",

View File

@ -186,7 +186,7 @@ int bpf_core_parse_spec(const char *prog_name, const struct btf *btf,
struct bpf_core_accessor *acc;
const struct btf_type *t;
const char *name, *spec_str;
__u32 id;
__u32 id, name_off;
__s64 sz;
spec_str = btf__name_by_offset(btf, relo->access_str_off);
@ -231,11 +231,13 @@ int bpf_core_parse_spec(const char *prog_name, const struct btf *btf,
spec->len++;
if (core_relo_is_enumval_based(relo->kind)) {
if (!btf_is_enum(t) || spec->raw_len > 1 || access_idx >= btf_vlen(t))
if (!btf_is_any_enum(t) || spec->raw_len > 1 || access_idx >= btf_vlen(t))
return -EINVAL;
/* record enumerator name in a first accessor */
acc->name = btf__name_by_offset(btf, btf_enum(t)[access_idx].name_off);
name_off = btf_is_enum(t) ? btf_enum(t)[access_idx].name_off
: btf_enum64(t)[access_idx].name_off;
acc->name = btf__name_by_offset(btf, name_off);
return 0;
}
@ -340,7 +342,7 @@ recur:
if (btf_is_composite(local_type) && btf_is_composite(targ_type))
return 1;
if (btf_kind(local_type) != btf_kind(targ_type))
if (!btf_kind_core_compat(local_type, targ_type))
return 0;
switch (btf_kind(local_type)) {
@ -348,6 +350,7 @@ recur:
case BTF_KIND_FLOAT:
return 1;
case BTF_KIND_FWD:
case BTF_KIND_ENUM64:
case BTF_KIND_ENUM: {
const char *local_name, *targ_name;
size_t local_len, targ_len;
@ -477,6 +480,7 @@ static int bpf_core_spec_match(struct bpf_core_spec *local_spec,
const struct bpf_core_accessor *local_acc;
struct bpf_core_accessor *targ_acc;
int i, sz, matched;
__u32 name_off;
memset(targ_spec, 0, sizeof(*targ_spec));
targ_spec->btf = targ_btf;
@ -494,18 +498,22 @@ static int bpf_core_spec_match(struct bpf_core_spec *local_spec,
if (core_relo_is_enumval_based(local_spec->relo_kind)) {
size_t local_essent_len, targ_essent_len;
const struct btf_enum *e;
const char *targ_name;
/* has to resolve to an enum */
targ_type = skip_mods_and_typedefs(targ_spec->btf, targ_id, &targ_id);
if (!btf_is_enum(targ_type))
if (!btf_is_any_enum(targ_type))
return 0;
local_essent_len = bpf_core_essential_name_len(local_acc->name);
for (i = 0, e = btf_enum(targ_type); i < btf_vlen(targ_type); i++, e++) {
targ_name = btf__name_by_offset(targ_spec->btf, e->name_off);
for (i = 0; i < btf_vlen(targ_type); i++) {
if (btf_is_enum(targ_type))
name_off = btf_enum(targ_type)[i].name_off;
else
name_off = btf_enum64(targ_type)[i].name_off;
targ_name = btf__name_by_offset(targ_spec->btf, name_off);
targ_essent_len = bpf_core_essential_name_len(targ_name);
if (targ_essent_len != local_essent_len)
continue;
@ -583,7 +591,7 @@ static int bpf_core_spec_match(struct bpf_core_spec *local_spec,
static int bpf_core_calc_field_relo(const char *prog_name,
const struct bpf_core_relo *relo,
const struct bpf_core_spec *spec,
__u32 *val, __u32 *field_sz, __u32 *type_id,
__u64 *val, __u32 *field_sz, __u32 *type_id,
bool *validate)
{
const struct bpf_core_accessor *acc;
@ -680,8 +688,7 @@ static int bpf_core_calc_field_relo(const char *prog_name,
*val = byte_sz;
break;
case BPF_CORE_FIELD_SIGNED:
/* enums will be assumed unsigned */
*val = btf_is_enum(mt) ||
*val = (btf_is_any_enum(mt) && BTF_INFO_KFLAG(mt->info)) ||
(btf_int_encoding(mt) & BTF_INT_SIGNED);
if (validate)
*validate = true; /* signedness is never ambiguous */
@ -708,7 +715,7 @@ static int bpf_core_calc_field_relo(const char *prog_name,
static int bpf_core_calc_type_relo(const struct bpf_core_relo *relo,
const struct bpf_core_spec *spec,
__u32 *val, bool *validate)
__u64 *val, bool *validate)
{
__s64 sz;
@ -751,10 +758,9 @@ static int bpf_core_calc_type_relo(const struct bpf_core_relo *relo,
static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
const struct bpf_core_spec *spec,
__u32 *val)
__u64 *val)
{
const struct btf_type *t;
const struct btf_enum *e;
switch (relo->kind) {
case BPF_CORE_ENUMVAL_EXISTS:
@ -764,8 +770,10 @@ static int bpf_core_calc_enumval_relo(const struct bpf_core_relo *relo,
if (!spec)
return -EUCLEAN; /* request instruction poisoning */
t = btf_type_by_id(spec->btf, spec->spec[0].type_id);
e = btf_enum(t) + spec->spec[0].idx;
*val = e->val;
if (btf_is_enum(t))
*val = btf_enum(t)[spec->spec[0].idx].val;
else
*val = btf_enum64_value(btf_enum64(t) + spec->spec[0].idx);
break;
default:
return -EOPNOTSUPP;
@ -929,7 +937,7 @@ int bpf_core_patch_insn(const char *prog_name, struct bpf_insn *insn,
int insn_idx, const struct bpf_core_relo *relo,
int relo_idx, const struct bpf_core_relo_res *res)
{
__u32 orig_val, new_val;
__u64 orig_val, new_val;
__u8 class;
class = BPF_CLASS(insn->code);
@ -954,28 +962,30 @@ poison:
if (BPF_SRC(insn->code) != BPF_K)
return -EINVAL;
if (res->validate && insn->imm != orig_val) {
pr_warn("prog '%s': relo #%d: unexpected insn #%d (ALU/ALU64) value: got %u, exp %u -> %u\n",
pr_warn("prog '%s': relo #%d: unexpected insn #%d (ALU/ALU64) value: got %u, exp %llu -> %llu\n",
prog_name, relo_idx,
insn_idx, insn->imm, orig_val, new_val);
insn_idx, insn->imm, (unsigned long long)orig_val,
(unsigned long long)new_val);
return -EINVAL;
}
orig_val = insn->imm;
insn->imm = new_val;
pr_debug("prog '%s': relo #%d: patched insn #%d (ALU/ALU64) imm %u -> %u\n",
pr_debug("prog '%s': relo #%d: patched insn #%d (ALU/ALU64) imm %llu -> %llu\n",
prog_name, relo_idx, insn_idx,
orig_val, new_val);
(unsigned long long)orig_val, (unsigned long long)new_val);
break;
case BPF_LDX:
case BPF_ST:
case BPF_STX:
if (res->validate && insn->off != orig_val) {
pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDX/ST/STX) value: got %u, exp %u -> %u\n",
prog_name, relo_idx, insn_idx, insn->off, orig_val, new_val);
pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDX/ST/STX) value: got %u, exp %llu -> %llu\n",
prog_name, relo_idx, insn_idx, insn->off, (unsigned long long)orig_val,
(unsigned long long)new_val);
return -EINVAL;
}
if (new_val > SHRT_MAX) {
pr_warn("prog '%s': relo #%d: insn #%d (LDX/ST/STX) value too big: %u\n",
prog_name, relo_idx, insn_idx, new_val);
pr_warn("prog '%s': relo #%d: insn #%d (LDX/ST/STX) value too big: %llu\n",
prog_name, relo_idx, insn_idx, (unsigned long long)new_val);
return -ERANGE;
}
if (res->fail_memsz_adjust) {
@ -987,8 +997,9 @@ poison:
orig_val = insn->off;
insn->off = new_val;
pr_debug("prog '%s': relo #%d: patched insn #%d (LDX/ST/STX) off %u -> %u\n",
prog_name, relo_idx, insn_idx, orig_val, new_val);
pr_debug("prog '%s': relo #%d: patched insn #%d (LDX/ST/STX) off %llu -> %llu\n",
prog_name, relo_idx, insn_idx, (unsigned long long)orig_val,
(unsigned long long)new_val);
if (res->new_sz != res->orig_sz) {
int insn_bytes_sz, insn_bpf_sz;
@ -1024,20 +1035,20 @@ poison:
return -EINVAL;
}
imm = insn[0].imm + ((__u64)insn[1].imm << 32);
imm = (__u32)insn[0].imm | ((__u64)insn[1].imm << 32);
if (res->validate && imm != orig_val) {
pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %u -> %u\n",
pr_warn("prog '%s': relo #%d: unexpected insn #%d (LDIMM64) value: got %llu, exp %llu -> %llu\n",
prog_name, relo_idx,
insn_idx, (unsigned long long)imm,
orig_val, new_val);
(unsigned long long)orig_val, (unsigned long long)new_val);
return -EINVAL;
}
insn[0].imm = new_val;
insn[1].imm = 0; /* currently only 32-bit values are supported */
pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %u\n",
insn[1].imm = new_val >> 32;
pr_debug("prog '%s': relo #%d: patched insn #%d (LDIMM64) imm64 %llu -> %llu\n",
prog_name, relo_idx, insn_idx,
(unsigned long long)imm, new_val);
(unsigned long long)imm, (unsigned long long)new_val);
break;
}
default:
@ -1057,7 +1068,6 @@ poison:
int bpf_core_format_spec(char *buf, size_t buf_sz, const struct bpf_core_spec *spec)
{
const struct btf_type *t;
const struct btf_enum *e;
const char *s;
__u32 type_id;
int i, len = 0;
@ -1086,10 +1096,23 @@ int bpf_core_format_spec(char *buf, size_t buf_sz, const struct bpf_core_spec *s
if (core_relo_is_enumval_based(spec->relo_kind)) {
t = skip_mods_and_typedefs(spec->btf, type_id, NULL);
e = btf_enum(t) + spec->raw_spec[0];
s = btf__name_by_offset(spec->btf, e->name_off);
if (btf_is_enum(t)) {
const struct btf_enum *e;
const char *fmt_str;
append_buf("::%s = %u", s, e->val);
e = btf_enum(t) + spec->raw_spec[0];
s = btf__name_by_offset(spec->btf, e->name_off);
fmt_str = BTF_INFO_KFLAG(t->info) ? "::%s = %d" : "::%s = %u";
append_buf(fmt_str, s, e->val);
} else {
const struct btf_enum64 *e;
const char *fmt_str;
e = btf_enum64(t) + spec->raw_spec[0];
s = btf__name_by_offset(spec->btf, e->name_off);
fmt_str = BTF_INFO_KFLAG(t->info) ? "::%s = %lld" : "::%s = %llu";
append_buf(fmt_str, s, (unsigned long long)btf_enum64_value(e));
}
return len;
}
@ -1261,10 +1284,12 @@ int bpf_core_calc_relo_insn(const char *prog_name,
* decision and value, otherwise it's dangerous to
* proceed due to ambiguity
*/
pr_warn("prog '%s': relo #%d: relocation decision ambiguity: %s %u != %s %u\n",
pr_warn("prog '%s': relo #%d: relocation decision ambiguity: %s %llu != %s %llu\n",
prog_name, relo_idx,
cand_res.poison ? "failure" : "success", cand_res.new_val,
targ_res->poison ? "failure" : "success", targ_res->new_val);
cand_res.poison ? "failure" : "success",
(unsigned long long)cand_res.new_val,
targ_res->poison ? "failure" : "success",
(unsigned long long)targ_res->new_val);
return -EINVAL;
}

View File

@ -46,9 +46,9 @@ struct bpf_core_spec {
struct bpf_core_relo_res {
/* expected value in the instruction, unless validate == false */
__u32 orig_val;
__u64 orig_val;
/* new value that needs to be patched up to */
__u32 new_val;
__u64 new_val;
/* relocation unsuccessful, poison instruction, but don't fail load */
bool poison;
/* some relocations can't be validated against orig_val */

View File

@ -26,11 +26,12 @@ static const char * const btf_kind_str_mapping[] = {
[BTF_KIND_FLOAT] = "FLOAT",
[BTF_KIND_DECL_TAG] = "DECL_TAG",
[BTF_KIND_TYPE_TAG] = "TYPE_TAG",
[BTF_KIND_ENUM64] = "ENUM64",
};
static const char *btf_kind_str(__u16 kind)
{
if (kind > BTF_KIND_TYPE_TAG)
if (kind > BTF_KIND_ENUM64)
return "UNKNOWN";
return btf_kind_str_mapping[kind];
}
@ -139,14 +140,32 @@ int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id)
}
case BTF_KIND_ENUM: {
const struct btf_enum *v = btf_enum(t);
const char *fmt_str;
fprintf(out, " size=%u vlen=%u", t->size, vlen);
fmt_str = btf_kflag(t) ? "\n\t'%s' val=%d" : "\n\t'%s' val=%u";
fprintf(out, " encoding=%s size=%u vlen=%u",
btf_kflag(t) ? "SIGNED" : "UNSIGNED", t->size, vlen);
for (i = 0; i < vlen; i++, v++) {
fprintf(out, "\n\t'%s' val=%u",
fprintf(out, fmt_str,
btf_str(btf, v->name_off), v->val);
}
break;
}
case BTF_KIND_ENUM64: {
const struct btf_enum64 *v = btf_enum64(t);
const char *fmt_str;
fmt_str = btf_kflag(t) ? "\n\t'%s' val=%lld" : "\n\t'%s' val=%llu";
fprintf(out, " encoding=%s size=%u vlen=%u",
btf_kflag(t) ? "SIGNED" : "UNSIGNED", t->size, vlen);
for (i = 0; i < vlen; i++, v++) {
fprintf(out, fmt_str,
btf_str(btf, v->name_off),
((__u64)v->val_hi32 << 32) | v->val_lo32);
}
break;
}
case BTF_KIND_FWD:
fprintf(out, " fwd_kind=%s", btf_kflag(t) ? "union" : "struct");
break;

View File

@ -2896,26 +2896,6 @@ static struct btf_raw_test raw_tests[] = {
.err_str = "Invalid btf_info kind_flag",
},
{
.descr = "invalid enum kind_flag",
.raw_types = {
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_ENUM, 1, 1), 4), /* [2] */
BTF_ENUM_ENC(NAME_TBD, 0),
BTF_END_RAW,
},
BTF_STR_SEC("\0A"),
.map_type = BPF_MAP_TYPE_ARRAY,
.map_name = "enum_type_check_btf",
.key_size = sizeof(int),
.value_size = sizeof(int),
.key_type_id = 1,
.value_type_id = 1,
.max_entries = 4,
.btf_load_err = true,
.err_str = "Invalid btf_info kind_flag",
},
{
.descr = "valid fwd kind_flag",
.raw_types = {
@ -4072,6 +4052,42 @@ static struct btf_raw_test raw_tests[] = {
.btf_load_err = true,
.err_str = "Type tags don't precede modifiers",
},
{
.descr = "enum64 test #1, unsigned, size 8",
.raw_types = {
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 2), 8), /* [2] */
BTF_ENUM64_ENC(NAME_TBD, 0, 0),
BTF_ENUM64_ENC(NAME_TBD, 1, 1),
BTF_END_RAW,
},
BTF_STR_SEC("\0a\0b\0c"),
.map_type = BPF_MAP_TYPE_ARRAY,
.map_name = "tag_type_check_btf",
.key_size = sizeof(int),
.value_size = 8,
.key_type_id = 1,
.value_type_id = 2,
.max_entries = 1,
},
{
.descr = "enum64 test #2, signed, size 4",
.raw_types = {
BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */
BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_ENUM64, 1, 2), 4), /* [2] */
BTF_ENUM64_ENC(NAME_TBD, -1, 0),
BTF_ENUM64_ENC(NAME_TBD, 1, 0),
BTF_END_RAW,
},
BTF_STR_SEC("\0a\0b\0c"),
.map_type = BPF_MAP_TYPE_ARRAY,
.map_name = "tag_type_check_btf",
.key_size = sizeof(int),
.value_size = 4,
.key_type_id = 1,
.value_type_id = 2,
.max_entries = 1,
},
}; /* struct btf_raw_test raw_tests[] */
@ -7000,9 +7016,12 @@ static struct btf_dedup_test dedup_tests[] = {
BTF_DECL_TAG_ENC(NAME_TBD, 13, 1), /* [16] decl_tag */
BTF_DECL_TAG_ENC(NAME_TBD, 7, -1), /* [17] decl_tag */
BTF_TYPE_TAG_ENC(NAME_TBD, 8), /* [18] type_tag */
BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 2), 8), /* [19] enum64 */
BTF_ENUM64_ENC(NAME_TBD, 0, 0),
BTF_ENUM64_ENC(NAME_TBD, 1, 1),
BTF_END_RAW,
},
BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N\0O\0P\0Q\0R"),
BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N\0O\0P\0Q\0R\0S\0T\0U"),
},
.expect = {
.raw_types = {
@ -7030,9 +7049,12 @@ static struct btf_dedup_test dedup_tests[] = {
BTF_DECL_TAG_ENC(NAME_TBD, 13, 1), /* [16] decl_tag */
BTF_DECL_TAG_ENC(NAME_TBD, 7, -1), /* [17] decl_tag */
BTF_TYPE_TAG_ENC(NAME_TBD, 8), /* [18] type_tag */
BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 2), 8), /* [19] enum64 */
BTF_ENUM64_ENC(NAME_TBD, 0, 0),
BTF_ENUM64_ENC(NAME_TBD, 1, 1),
BTF_END_RAW,
},
BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N\0O\0P\0Q\0R"),
BTF_STR_SEC("\0A\0B\0C\0D\0E\0F\0G\0H\0I\0J\0K\0L\0M\0N\0O\0P\0Q\0R\0S\0T\0U"),
},
},
{
@ -7493,6 +7515,91 @@ static struct btf_dedup_test dedup_tests[] = {
BTF_STR_SEC("\0tag1\0t\0m"),
},
},
{
.descr = "dedup: enum64, standalone",
.input = {
.raw_types = {
BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
BTF_ENUM64_ENC(NAME_NTH(2), 1, 123),
BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
BTF_ENUM64_ENC(NAME_NTH(2), 1, 123),
BTF_END_RAW,
},
BTF_STR_SEC("\0e1\0e1_val"),
},
.expect = {
.raw_types = {
BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
BTF_ENUM64_ENC(NAME_NTH(2), 1, 123),
BTF_END_RAW,
},
BTF_STR_SEC("\0e1\0e1_val"),
},
},
{
.descr = "dedup: enum64, fwd resolution",
.input = {
.raw_types = {
/* [1] fwd enum64 'e1' before full enum */
BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 0), 8),
/* [2] full enum64 'e1' after fwd */
BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
BTF_ENUM64_ENC(NAME_NTH(2), 1, 123),
/* [3] full enum64 'e2' before fwd */
BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
BTF_ENUM64_ENC(NAME_NTH(4), 0, 456),
/* [4] fwd enum64 'e2' after full enum */
BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 0), 8),
/* [5] incompatible full enum64 with different value */
BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
BTF_ENUM64_ENC(NAME_NTH(2), 0, 321),
BTF_END_RAW,
},
BTF_STR_SEC("\0e1\0e1_val\0e2\0e2_val"),
},
.expect = {
.raw_types = {
/* [1] full enum64 'e1' */
BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
BTF_ENUM64_ENC(NAME_NTH(2), 1, 123),
/* [2] full enum64 'e2' */
BTF_TYPE_ENC(NAME_NTH(3), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
BTF_ENUM64_ENC(NAME_NTH(4), 0, 456),
/* [3] incompatible full enum64 with different value */
BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 8),
BTF_ENUM64_ENC(NAME_NTH(2), 0, 321),
BTF_END_RAW,
},
BTF_STR_SEC("\0e1\0e1_val\0e2\0e2_val"),
},
},
{
.descr = "dedup: enum and enum64, no dedup",
.input = {
.raw_types = {
/* [1] enum 'e1' */
BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4),
BTF_ENUM_ENC(NAME_NTH(2), 1),
/* [2] enum64 'e1' */
BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 4),
BTF_ENUM64_ENC(NAME_NTH(2), 1, 0),
BTF_END_RAW,
},
BTF_STR_SEC("\0e1\0e1_val"),
},
.expect = {
.raw_types = {
/* [1] enum 'e1' */
BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM, 0, 1), 4),
BTF_ENUM_ENC(NAME_NTH(2), 1),
/* [2] enum64 'e1' */
BTF_TYPE_ENC(NAME_NTH(1), BTF_INFO_ENC(BTF_KIND_ENUM64, 0, 1), 4),
BTF_ENUM64_ENC(NAME_NTH(2), 1, 0),
BTF_END_RAW,
},
BTF_STR_SEC("\0e1\0e1_val"),
},
},
};
@ -7517,6 +7624,8 @@ static int btf_type_size(const struct btf_type *t)
return base_size + sizeof(__u32);
case BTF_KIND_ENUM:
return base_size + vlen * sizeof(struct btf_enum);
case BTF_KIND_ENUM64:
return base_size + vlen * sizeof(struct btf_enum64);
case BTF_KIND_ARRAY:
return base_size + sizeof(struct btf_array);
case BTF_KIND_STRUCT:

View File

@ -9,6 +9,7 @@ static void gen_btf(struct btf *btf)
const struct btf_var_secinfo *vi;
const struct btf_type *t;
const struct btf_member *m;
const struct btf_enum64 *v64;
const struct btf_enum *v;
const struct btf_param *p;
int id, err, str_off;
@ -171,7 +172,7 @@ static void gen_btf(struct btf *btf)
ASSERT_STREQ(btf__str_by_offset(btf, v->name_off), "v2", "v2_name");
ASSERT_EQ(v->val, 2, "v2_val");
ASSERT_STREQ(btf_type_raw_dump(btf, 9),
"[9] ENUM 'e1' size=4 vlen=2\n"
"[9] ENUM 'e1' encoding=UNSIGNED size=4 vlen=2\n"
"\t'v1' val=1\n"
"\t'v2' val=2", "raw_dump");
@ -202,7 +203,7 @@ static void gen_btf(struct btf *btf)
ASSERT_EQ(btf_vlen(t), 0, "enum_fwd_kind");
ASSERT_EQ(t->size, 4, "enum_fwd_sz");
ASSERT_STREQ(btf_type_raw_dump(btf, 12),
"[12] ENUM 'enum_fwd' size=4 vlen=0", "raw_dump");
"[12] ENUM 'enum_fwd' encoding=UNSIGNED size=4 vlen=0", "raw_dump");
/* TYPEDEF */
id = btf__add_typedef(btf, "typedef1", 1);
@ -307,6 +308,48 @@ static void gen_btf(struct btf *btf)
ASSERT_EQ(t->type, 1, "tag_type");
ASSERT_STREQ(btf_type_raw_dump(btf, 20),
"[20] TYPE_TAG 'tag1' type_id=1", "raw_dump");
/* ENUM64 */
id = btf__add_enum64(btf, "e1", 8, true);
ASSERT_EQ(id, 21, "enum64_id");
err = btf__add_enum64_value(btf, "v1", -1);
ASSERT_OK(err, "v1_res");
err = btf__add_enum64_value(btf, "v2", 0x123456789); /* 4886718345 */
ASSERT_OK(err, "v2_res");
t = btf__type_by_id(btf, 21);
ASSERT_STREQ(btf__str_by_offset(btf, t->name_off), "e1", "enum64_name");
ASSERT_EQ(btf_kind(t), BTF_KIND_ENUM64, "enum64_kind");
ASSERT_EQ(btf_vlen(t), 2, "enum64_vlen");
ASSERT_EQ(t->size, 8, "enum64_sz");
v64 = btf_enum64(t) + 0;
ASSERT_STREQ(btf__str_by_offset(btf, v64->name_off), "v1", "v1_name");
ASSERT_EQ(v64->val_hi32, 0xffffffff, "v1_val");
ASSERT_EQ(v64->val_lo32, 0xffffffff, "v1_val");
v64 = btf_enum64(t) + 1;
ASSERT_STREQ(btf__str_by_offset(btf, v64->name_off), "v2", "v2_name");
ASSERT_EQ(v64->val_hi32, 0x1, "v2_val");
ASSERT_EQ(v64->val_lo32, 0x23456789, "v2_val");
ASSERT_STREQ(btf_type_raw_dump(btf, 21),
"[21] ENUM64 'e1' encoding=SIGNED size=8 vlen=2\n"
"\t'v1' val=-1\n"
"\t'v2' val=4886718345", "raw_dump");
id = btf__add_enum64(btf, "e1", 8, false);
ASSERT_EQ(id, 22, "enum64_id");
err = btf__add_enum64_value(btf, "v1", 0xffffffffFFFFFFFF); /* 18446744073709551615 */
ASSERT_OK(err, "v1_res");
t = btf__type_by_id(btf, 22);
ASSERT_STREQ(btf__str_by_offset(btf, t->name_off), "e1", "enum64_name");
ASSERT_EQ(btf_kind(t), BTF_KIND_ENUM64, "enum64_kind");
ASSERT_EQ(btf_vlen(t), 1, "enum64_vlen");
ASSERT_EQ(t->size, 8, "enum64_sz");
v64 = btf_enum64(t) + 0;
ASSERT_STREQ(btf__str_by_offset(btf, v64->name_off), "v1", "v1_name");
ASSERT_EQ(v64->val_hi32, 0xffffffff, "v1_val");
ASSERT_EQ(v64->val_lo32, 0xffffffff, "v1_val");
ASSERT_STREQ(btf_type_raw_dump(btf, 22),
"[22] ENUM64 'e1' encoding=UNSIGNED size=8 vlen=1\n"
"\t'v1' val=18446744073709551615", "raw_dump");
}
static void test_btf_add()
@ -332,12 +375,12 @@ static void test_btf_add()
"\t'f2' type_id=1 bits_offset=32 bitfield_size=16",
"[8] UNION 'u1' size=8 vlen=1\n"
"\t'f1' type_id=1 bits_offset=0 bitfield_size=16",
"[9] ENUM 'e1' size=4 vlen=2\n"
"[9] ENUM 'e1' encoding=UNSIGNED size=4 vlen=2\n"
"\t'v1' val=1\n"
"\t'v2' val=2",
"[10] FWD 'struct_fwd' fwd_kind=struct",
"[11] FWD 'union_fwd' fwd_kind=union",
"[12] ENUM 'enum_fwd' size=4 vlen=0",
"[12] ENUM 'enum_fwd' encoding=UNSIGNED size=4 vlen=0",
"[13] TYPEDEF 'typedef1' type_id=1",
"[14] FUNC 'func1' type_id=15 linkage=global",
"[15] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
@ -348,7 +391,12 @@ static void test_btf_add()
"\ttype_id=1 offset=4 size=8",
"[18] DECL_TAG 'tag1' type_id=16 component_idx=-1",
"[19] DECL_TAG 'tag2' type_id=14 component_idx=1",
"[20] TYPE_TAG 'tag1' type_id=1");
"[20] TYPE_TAG 'tag1' type_id=1",
"[21] ENUM64 'e1' encoding=SIGNED size=8 vlen=2\n"
"\t'v1' val=-1\n"
"\t'v2' val=4886718345",
"[22] ENUM64 'e1' encoding=UNSIGNED size=8 vlen=1\n"
"\t'v1' val=18446744073709551615");
btf__free(btf);
}
@ -370,7 +418,7 @@ static void test_btf_add_btf()
gen_btf(btf2);
id = btf__add_btf(btf1, btf2);
if (!ASSERT_EQ(id, 21, "id"))
if (!ASSERT_EQ(id, 23, "id"))
goto cleanup;
VALIDATE_RAW_BTF(
@ -386,12 +434,12 @@ static void test_btf_add_btf()
"\t'f2' type_id=1 bits_offset=32 bitfield_size=16",
"[8] UNION 'u1' size=8 vlen=1\n"
"\t'f1' type_id=1 bits_offset=0 bitfield_size=16",
"[9] ENUM 'e1' size=4 vlen=2\n"
"[9] ENUM 'e1' encoding=UNSIGNED size=4 vlen=2\n"
"\t'v1' val=1\n"
"\t'v2' val=2",
"[10] FWD 'struct_fwd' fwd_kind=struct",
"[11] FWD 'union_fwd' fwd_kind=union",
"[12] ENUM 'enum_fwd' size=4 vlen=0",
"[12] ENUM 'enum_fwd' encoding=UNSIGNED size=4 vlen=0",
"[13] TYPEDEF 'typedef1' type_id=1",
"[14] FUNC 'func1' type_id=15 linkage=global",
"[15] FUNC_PROTO '(anon)' ret_type_id=1 vlen=2\n"
@ -403,36 +451,46 @@ static void test_btf_add_btf()
"[18] DECL_TAG 'tag1' type_id=16 component_idx=-1",
"[19] DECL_TAG 'tag2' type_id=14 component_idx=1",
"[20] TYPE_TAG 'tag1' type_id=1",
"[21] ENUM64 'e1' encoding=SIGNED size=8 vlen=2\n"
"\t'v1' val=-1\n"
"\t'v2' val=4886718345",
"[22] ENUM64 'e1' encoding=UNSIGNED size=8 vlen=1\n"
"\t'v1' val=18446744073709551615",
/* types appended from the second BTF */
"[21] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
"[22] PTR '(anon)' type_id=21",
"[23] CONST '(anon)' type_id=25",
"[24] VOLATILE '(anon)' type_id=23",
"[25] RESTRICT '(anon)' type_id=24",
"[26] ARRAY '(anon)' type_id=22 index_type_id=21 nr_elems=10",
"[27] STRUCT 's1' size=8 vlen=2\n"
"\t'f1' type_id=21 bits_offset=0\n"
"\t'f2' type_id=21 bits_offset=32 bitfield_size=16",
"[28] UNION 'u1' size=8 vlen=1\n"
"\t'f1' type_id=21 bits_offset=0 bitfield_size=16",
"[29] ENUM 'e1' size=4 vlen=2\n"
"[23] INT 'int' size=4 bits_offset=0 nr_bits=32 encoding=SIGNED",
"[24] PTR '(anon)' type_id=23",
"[25] CONST '(anon)' type_id=27",
"[26] VOLATILE '(anon)' type_id=25",
"[27] RESTRICT '(anon)' type_id=26",
"[28] ARRAY '(anon)' type_id=24 index_type_id=23 nr_elems=10",
"[29] STRUCT 's1' size=8 vlen=2\n"
"\t'f1' type_id=23 bits_offset=0\n"
"\t'f2' type_id=23 bits_offset=32 bitfield_size=16",
"[30] UNION 'u1' size=8 vlen=1\n"
"\t'f1' type_id=23 bits_offset=0 bitfield_size=16",
"[31] ENUM 'e1' encoding=UNSIGNED size=4 vlen=2\n"
"\t'v1' val=1\n"
"\t'v2' val=2",
"[30] FWD 'struct_fwd' fwd_kind=struct",
"[31] FWD 'union_fwd' fwd_kind=union",
"[32] ENUM 'enum_fwd' size=4 vlen=0",
"[33] TYPEDEF 'typedef1' type_id=21",
"[34] FUNC 'func1' type_id=35 linkage=global",
"[35] FUNC_PROTO '(anon)' ret_type_id=21 vlen=2\n"
"\t'p1' type_id=21\n"
"\t'p2' type_id=22",
"[36] VAR 'var1' type_id=21, linkage=global-alloc",
"[37] DATASEC 'datasec1' size=12 vlen=1\n"
"\ttype_id=21 offset=4 size=8",
"[38] DECL_TAG 'tag1' type_id=36 component_idx=-1",
"[39] DECL_TAG 'tag2' type_id=34 component_idx=1",
"[40] TYPE_TAG 'tag1' type_id=21");
"[32] FWD 'struct_fwd' fwd_kind=struct",
"[33] FWD 'union_fwd' fwd_kind=union",
"[34] ENUM 'enum_fwd' encoding=UNSIGNED size=4 vlen=0",
"[35] TYPEDEF 'typedef1' type_id=23",
"[36] FUNC 'func1' type_id=37 linkage=global",
"[37] FUNC_PROTO '(anon)' ret_type_id=23 vlen=2\n"
"\t'p1' type_id=23\n"
"\t'p2' type_id=24",
"[38] VAR 'var1' type_id=23, linkage=global-alloc",
"[39] DATASEC 'datasec1' size=12 vlen=1\n"
"\ttype_id=23 offset=4 size=8",
"[40] DECL_TAG 'tag1' type_id=38 component_idx=-1",
"[41] DECL_TAG 'tag2' type_id=36 component_idx=1",
"[42] TYPE_TAG 'tag1' type_id=23",
"[43] ENUM64 'e1' encoding=SIGNED size=8 vlen=2\n"
"\t'v1' val=-1\n"
"\t'v2' val=4886718345",
"[44] ENUM64 'e1' encoding=UNSIGNED size=8 vlen=1\n"
"\t'v1' val=18446744073709551615");
cleanup:
btf__free(btf1);

View File

@ -363,6 +363,25 @@ static int duration = 0;
.fails = true, \
}
#define ENUM64VAL_CASE_COMMON(name) \
.case_name = #name, \
.bpf_obj_file = "test_core_reloc_enum64val.o", \
.btf_src_file = "btf__core_reloc_" #name ".o", \
.raw_tp_name = "sys_enter", \
.prog_name = "test_core_enum64val"
#define ENUM64VAL_CASE(name, ...) { \
ENUM64VAL_CASE_COMMON(name), \
.output = STRUCT_TO_CHAR_PTR(core_reloc_enum64val_output) \
__VA_ARGS__, \
.output_len = sizeof(struct core_reloc_enum64val_output), \
}
#define ENUM64VAL_ERR_CASE(name) { \
ENUM64VAL_CASE_COMMON(name), \
.fails = true, \
}
struct core_reloc_test_case;
typedef int (*setup_test_fn)(struct core_reloc_test_case *test);
@ -831,6 +850,45 @@ static const struct core_reloc_test_case test_cases[] = {
.anon_val2 = 0x222,
}),
ENUMVAL_ERR_CASE(enumval___err_missing),
/* 64bit enumerator value existence and value relocations */
ENUM64VAL_CASE(enum64val, {
.unsigned_val1_exists = true,
.unsigned_val2_exists = true,
.unsigned_val3_exists = true,
.signed_val1_exists = true,
.signed_val2_exists = true,
.signed_val3_exists = true,
.unsigned_val1 = 0x1ffffffffULL,
.unsigned_val2 = 0x2,
.signed_val1 = 0x1ffffffffLL,
.signed_val2 = -2,
}),
ENUM64VAL_CASE(enum64val___diff, {
.unsigned_val1_exists = true,
.unsigned_val2_exists = true,
.unsigned_val3_exists = true,
.signed_val1_exists = true,
.signed_val2_exists = true,
.signed_val3_exists = true,
.unsigned_val1 = 0x101ffffffffULL,
.unsigned_val2 = 0x202ffffffffULL,
.signed_val1 = -101,
.signed_val2 = -202,
}),
ENUM64VAL_CASE(enum64val___val3_missing, {
.unsigned_val1_exists = true,
.unsigned_val2_exists = true,
.unsigned_val3_exists = false,
.signed_val1_exists = true,
.signed_val2_exists = true,
.signed_val3_exists = false,
.unsigned_val1 = 0x111ffffffffULL,
.unsigned_val2 = 0x222,
.signed_val1 = 0x111ffffffffLL,
.signed_val2 = -222,
}),
ENUM64VAL_ERR_CASE(enum64val___err_missing),
};
struct data {

View File

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

View File

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

View File

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

View File

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

View File

@ -1117,6 +1117,20 @@ struct core_reloc_enumval_output {
int anon_val2;
};
struct core_reloc_enum64val_output {
bool unsigned_val1_exists;
bool unsigned_val2_exists;
bool unsigned_val3_exists;
bool signed_val1_exists;
bool signed_val2_exists;
bool signed_val3_exists;
long unsigned_val1;
long unsigned_val2;
long signed_val1;
long signed_val2;
};
enum named_enum {
NAMED_ENUM_VAL1 = 1,
NAMED_ENUM_VAL2 = 2,
@ -1134,6 +1148,23 @@ struct core_reloc_enumval {
anon_enum f2;
};
enum named_unsigned_enum64 {
UNSIGNED_ENUM64_VAL1 = 0x1ffffffffULL,
UNSIGNED_ENUM64_VAL2 = 0x2,
UNSIGNED_ENUM64_VAL3 = 0x3ffffffffULL,
};
enum named_signed_enum64 {
SIGNED_ENUM64_VAL1 = 0x1ffffffffLL,
SIGNED_ENUM64_VAL2 = -2,
SIGNED_ENUM64_VAL3 = 0x3ffffffffLL,
};
struct core_reloc_enum64val {
enum named_unsigned_enum64 f1;
enum named_signed_enum64 f2;
};
/* differing enumerator values */
enum named_enum___diff {
NAMED_ENUM_VAL1___diff = 101,
@ -1152,6 +1183,23 @@ struct core_reloc_enumval___diff {
anon_enum___diff f2;
};
enum named_unsigned_enum64___diff {
UNSIGNED_ENUM64_VAL1___diff = 0x101ffffffffULL,
UNSIGNED_ENUM64_VAL2___diff = 0x202ffffffffULL,
UNSIGNED_ENUM64_VAL3___diff = 0x303ffffffffULL,
};
enum named_signed_enum64___diff {
SIGNED_ENUM64_VAL1___diff = -101,
SIGNED_ENUM64_VAL2___diff = -202,
SIGNED_ENUM64_VAL3___diff = -303,
};
struct core_reloc_enum64val___diff {
enum named_unsigned_enum64___diff f1;
enum named_signed_enum64___diff f2;
};
/* missing (optional) third enum value */
enum named_enum___val3_missing {
NAMED_ENUM_VAL1___val3_missing = 111,
@ -1168,6 +1216,21 @@ struct core_reloc_enumval___val3_missing {
anon_enum___val3_missing f2;
};
enum named_unsigned_enum64___val3_missing {
UNSIGNED_ENUM64_VAL1___val3_missing = 0x111ffffffffULL,
UNSIGNED_ENUM64_VAL2___val3_missing = 0x222,
};
enum named_signed_enum64___val3_missing {
SIGNED_ENUM64_VAL1___val3_missing = 0x111ffffffffLL,
SIGNED_ENUM64_VAL2___val3_missing = -222,
};
struct core_reloc_enum64val___val3_missing {
enum named_unsigned_enum64___val3_missing f1;
enum named_signed_enum64___val3_missing f2;
};
/* missing (mandatory) second enum value, should fail */
enum named_enum___err_missing {
NAMED_ENUM_VAL1___err_missing = 1,
@ -1183,3 +1246,18 @@ struct core_reloc_enumval___err_missing {
enum named_enum___err_missing f1;
anon_enum___err_missing f2;
};
enum named_unsigned_enum64___err_missing {
UNSIGNED_ENUM64_VAL1___err_missing = 0x1ffffffffULL,
UNSIGNED_ENUM64_VAL3___err_missing = 0x3ffffffffULL,
};
enum named_signed_enum64___err_missing {
SIGNED_ENUM64_VAL1___err_missing = 0x1ffffffffLL,
SIGNED_ENUM64_VAL3___err_missing = -3,
};
struct core_reloc_enum64val___err_missing {
enum named_unsigned_enum64___err_missing f1;
enum named_signed_enum64___err_missing f2;
};

View File

@ -0,0 +1,70 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
#include <linux/bpf.h>
#include <stdint.h>
#include <stdbool.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
char _license[] SEC("license") = "GPL";
struct {
char in[256];
char out[256];
bool skip;
} data = {};
enum named_unsigned_enum64 {
UNSIGNED_ENUM64_VAL1 = 0x1ffffffffULL,
UNSIGNED_ENUM64_VAL2 = 0x2ffffffffULL,
UNSIGNED_ENUM64_VAL3 = 0x3ffffffffULL,
};
enum named_signed_enum64 {
SIGNED_ENUM64_VAL1 = 0x1ffffffffLL,
SIGNED_ENUM64_VAL2 = -2,
SIGNED_ENUM64_VAL3 = 0x3ffffffffLL,
};
struct core_reloc_enum64val_output {
bool unsigned_val1_exists;
bool unsigned_val2_exists;
bool unsigned_val3_exists;
bool signed_val1_exists;
bool signed_val2_exists;
bool signed_val3_exists;
long unsigned_val1;
long unsigned_val2;
long signed_val1;
long signed_val2;
};
SEC("raw_tracepoint/sys_enter")
int test_core_enum64val(void *ctx)
{
#if __clang_major__ >= 15
struct core_reloc_enum64val_output *out = (void *)&data.out;
enum named_unsigned_enum64 named_unsigned = 0;
enum named_signed_enum64 named_signed = 0;
out->unsigned_val1_exists = bpf_core_enum_value_exists(named_unsigned, UNSIGNED_ENUM64_VAL1);
out->unsigned_val2_exists = bpf_core_enum_value_exists(enum named_unsigned_enum64, UNSIGNED_ENUM64_VAL2);
out->unsigned_val3_exists = bpf_core_enum_value_exists(enum named_unsigned_enum64, UNSIGNED_ENUM64_VAL3);
out->signed_val1_exists = bpf_core_enum_value_exists(named_signed, SIGNED_ENUM64_VAL1);
out->signed_val2_exists = bpf_core_enum_value_exists(enum named_signed_enum64, SIGNED_ENUM64_VAL2);
out->signed_val3_exists = bpf_core_enum_value_exists(enum named_signed_enum64, SIGNED_ENUM64_VAL3);
out->unsigned_val1 = bpf_core_enum_value(named_unsigned, UNSIGNED_ENUM64_VAL1);
out->unsigned_val2 = bpf_core_enum_value(named_unsigned, UNSIGNED_ENUM64_VAL2);
out->signed_val1 = bpf_core_enum_value(named_signed, SIGNED_ENUM64_VAL1);
out->signed_val2 = bpf_core_enum_value(named_signed, SIGNED_ENUM64_VAL2);
/* NAMED_ENUM64_VAL3 value is optional */
#else
data.skip = true;
#endif
return 0;
}

View File

@ -39,6 +39,7 @@
#define BTF_MEMBER_ENC(name, type, bits_offset) \
(name), (type), (bits_offset)
#define BTF_ENUM_ENC(name, val) (name), (val)
#define BTF_ENUM64_ENC(name, val_lo32, val_hi32) (name), (val_lo32), (val_hi32)
#define BTF_MEMBER_OFFSET(bitfield_size, bits_offset) \
((bitfield_size) << 24 | (bits_offset))