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:
commit
d352bd889b
@ -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
|
||||
=================
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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__ */
|
||||
|
142
kernel/bpf/btf.c
142
kernel/bpf/btf.c
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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__ */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_enum64val x) {}
|
@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_enum64val___diff x) {}
|
@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_enum64val___err_missing x) {}
|
@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_enum64val___val3_missing x) {}
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
@ -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))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user