Add support for new kinds to libbpf. BTF_KIND_LOC_PARAM and BTF_KIND_LOC_PROTO are dedup-able so add support for their deduplication, whereas since BTF_KIND_LOCSEC contains a unique offset it is not. LOC_PARAM is considered a primary type since it contains no external references; LOC_PROTO is a reference type consisting of LOC_PARAM references so they are handled in the primary and reference dedup phases respectively.
For BTF field iteration, BTF_KIND_LOCSEC needs 2 m_offs[] values for the associated KIND_FUNC and KIND_LOC_PROTO type ids in each LOCSEC entry. Add APIs to add location param, location prototypes and location sections and btf_is_* tests, data accessors for each. For BTF distillation we add location info to split BTF. Signed-off-by: Alan Maguire <[email protected]> --- tools/lib/bpf/btf.c | 372 +++++++++++++++++++++++++++++++- tools/lib/bpf/btf.h | 50 +++++ tools/lib/bpf/btf_dump.c | 9 + tools/lib/bpf/btf_iter.c | 22 ++ tools/lib/bpf/libbpf.map | 6 + tools/lib/bpf/libbpf_internal.h | 2 +- 6 files changed, 457 insertions(+), 4 deletions(-) diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index c783359977b4..9449f6f50f18 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c @@ -57,6 +57,9 @@ static struct btf_layout layouts[NR_BTF_KINDS] = { [BTF_KIND_DECL_TAG] = { sizeof(struct btf_decl_tag), 0, 0 }, [BTF_KIND_TYPE_TAG] = { 0, 0, 0 }, [BTF_KIND_ENUM64] = { 0, sizeof(struct btf_enum64), 0 }, +[BTF_KIND_LOC_PARAM] = { sizeof(struct btf_loc_param), sizeof(__u32), 0 }, +[BTF_KIND_LOC_PROTO] = { 0, sizeof(__u32), 0 }, +[BTF_KIND_LOCSEC] = { 0, sizeof(struct btf_loc), 0 }, }; struct btf { @@ -486,6 +489,12 @@ static int btf_type_size(const struct btf *btf, const struct btf_type *t) return base_size + vlen * sizeof(struct btf_var_secinfo); case BTF_KIND_DECL_TAG: return base_size + sizeof(struct btf_decl_tag); + case BTF_KIND_LOC_PARAM: + return base_size + sizeof(struct btf_loc_param) + vlen * sizeof(__u32); + case BTF_KIND_LOC_PROTO: + return base_size + vlen * sizeof(__u32); + case BTF_KIND_LOCSEC: + return base_size + vlen * sizeof(struct btf_loc); default: return btf_type_size_unknown(btf, t); } @@ -501,12 +510,15 @@ 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_loc_param *lp; struct btf_enum64 *e64; struct btf_member *m; struct btf_array *a; struct btf_param *p; struct btf_enum *e; + struct btf_loc *l; __u32 vlen = btf_vlen(t); + __u32 *d; int i; switch (btf_kind(t)) { @@ -569,6 +581,23 @@ static int btf_bswap_type_rest(struct btf_type *t) case BTF_KIND_DECL_TAG: btf_decl_tag(t)->component_idx = bswap_32(btf_decl_tag(t)->component_idx); return 0; + case BTF_KIND_LOC_PARAM: + lp = btf_loc_param(t); + lp->flags = bswap_32(lp->flags); + for (i = 0, d = (__u32 *)(lp + 1); i < vlen; i++, d++) + *d = bswap_32(*d); + return 0; + case BTF_KIND_LOC_PROTO: + for (i = 0, d = btf_loc_proto_params(t); i < vlen; i++, d++) + *d = bswap_32(*d); + return 0; + case BTF_KIND_LOCSEC: + for (i = 0, l = btf_locsec_locs(t); i < vlen; i++, l++) { + l->func = bswap_32(l->func); + l->loc_proto = bswap_32(l->loc_proto); + l->offset = bswap_32(l->offset); + } + return 0; default: pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t)); return -EINVAL; @@ -745,6 +774,33 @@ static int btf_validate_type(const struct btf *btf, const struct btf_type *t, __ } break; } + case BTF_KIND_LOC_PARAM: + break; + case BTF_KIND_LOC_PROTO: { + __u32 *p = btf_loc_proto_params(t); + + n = btf_vlen(t); + for (i = 0; i < n; i++, p++) { + err = btf_validate_id(btf, *p, id); + if (err) + return err; + } + break; + } + case BTF_KIND_LOCSEC: { + const struct btf_loc *l = btf_locsec_locs(t); + + n = btf_vlen(t); + for (i = 0; i < n; i++, l++) { + if (!err) + err = btf_validate_id(btf, l->func, id); + if (!err) + err = btf_validate_id(btf, l->loc_proto, id); + if (err) + return err; + } + break; + } default: /* Kind may be represented in kind layout information. */ if (btf_type_size_unknown(btf, t) < 0) { @@ -3344,6 +3400,208 @@ int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id, return btf_add_decl_tag(btf, value, ref_type_id, component_idx, 1); } +/* + * Append new BTF_KIND_LOC_PARAM with specified size and flags. Values are + * added via btf__add_loc_param_value(). + * + * Returns: + * - >0, type ID of newly added BTF type; + * - <0, on error. + */ +int btf__add_loc_param(struct btf *btf, __u32 size, __u32 flags) +{ + struct btf_loc_param *p; + struct btf_type *t; + int sz, err; + + err = btf_ensure_modifiable(btf); + if (err) + return libbpf_err(err); + + sz = sizeof(struct btf_type) + sizeof(*p); + t = btf_add_type_mem(btf, sz); + if (!t) + return libbpf_err(-ENOMEM); + + t->name_off = 0; + t->info = btf_type_info(BTF_KIND_LOC_PARAM, 0, 0); + t->size = size; + + p = btf_loc_param(t); + p->flags = flags; + + return btf_commit_type(btf, sz); +} + +int btf__add_loc_param_value(struct btf *btf, __u32 value) +{ + struct btf_type *t; + int sz, err; + __u32 *v; + + /* last type should be BTF_KIND_LOC_PARAM */ + if (btf->nr_types == 0) + return libbpf_err(-EINVAL); + t = btf_last_type(btf); + if (!btf_is_loc_param(t)) + return libbpf_err(-EINVAL); + + /* decompose and invalidate raw data */ + err = btf_ensure_modifiable(btf); + if (err) + return libbpf_err(err); + + sz = sizeof(value); + v = btf_add_type_mem(btf, sz); + if (!v) + return libbpf_err(-ENOMEM); + *v = value; + + /* update parent type's vlen */ + t = btf_last_type(btf); + err = btf_type_inc_vlen(t); + if (err) + return libbpf_err(err); + + btf_hdr_update_type_len(btf, btf->hdr.type_len + sz); + return 0; +} + +/* + * Append new BTF_KIND_LOC_PROTO + * + * The prototype is then populated with 0 or more BTF_KIND_LOC_PARAMs via + * btf__add_loc_proto_param(); similar to how btf__add_func_param() adds + * parameters to a FUNC_PROTO. + * + * Returns: + * - >0, type ID of newly added BTF type; + * - <0, on error. + */ +int btf__add_loc_proto(struct btf *btf) +{ + struct btf_type *t; + int err; + + err = btf_ensure_modifiable(btf); + if (err) + return libbpf_err(err); + + t = btf_add_type_mem(btf, sizeof(struct btf_type)); + if (!t) + return libbpf_err(-ENOMEM); + + t->name_off = 0; + t->info = btf_type_info(BTF_KIND_LOC_PROTO, 0, 0); + t->size = 0; + + return btf_commit_type(btf, sizeof(struct btf_type)); +} + +int btf__add_loc_proto_param(struct btf *btf, __u32 id) +{ + struct btf_type *t; + int sz, err; + __u32 *p; + + if (validate_type_id(id)) + return libbpf_err(-EINVAL); + + /* last type should be BTF_KIND_LOC_PROTO */ + if (btf->nr_types == 0) + return libbpf_err(-EINVAL); + t = btf_last_type(btf); + if (!btf_is_loc_proto(t)) + return libbpf_err(-EINVAL); + + /* decompose and invalidate raw data */ + err = btf_ensure_modifiable(btf); + if (err) + return libbpf_err(err); + + sz = sizeof(__u32); + p = btf_add_type_mem(btf, sz); + if (!p) + return libbpf_err(-ENOMEM); + *p = id; + + /* update parent type's vlen */ + t = btf_last_type(btf); + err = btf_type_inc_vlen(t); + if (err) + return libbpf_err(err); + + btf_hdr_update_type_len(btf, btf->hdr.type_len + sz); + return 0; +} + +int btf__add_locsec(struct btf *btf, const char *name) +{ + struct btf_type *t; + int name_off = 0; + int err; + + err = btf_ensure_modifiable(btf); + if (err) + return libbpf_err(err); + + t = btf_add_type_mem(btf, sizeof(struct btf_type)); + if (!t) + return libbpf_err(-ENOMEM); + + if (!str_is_empty(name)) { + name_off = btf__add_str(btf, name); + if (name_off < 0) + return name_off; + } + t->name_off = name_off; + t->info = btf_type_info(BTF_KIND_LOCSEC, 0, 0); + t->size = 0; + + return btf_commit_type(btf, sizeof(struct btf_type)); +} + +int btf__add_locsec_loc(struct btf *btf, __u32 func, __u32 loc_proto, + __u32 offset) +{ + struct btf_type *t; + struct btf_loc *l; + int sz, err; + + if (validate_type_id(func) || validate_type_id(loc_proto)) + return libbpf_err(-EINVAL); + + /* last type should be BTF_KIND_LOCSEC */ + if (btf->nr_types == 0) + return libbpf_err(-EINVAL); + t = btf_last_type(btf); + if (!btf_is_locsec(t)) + return libbpf_err(-EINVAL); + + /* decompose and invalidate raw data */ + err = btf_ensure_modifiable(btf); + if (err) + return libbpf_err(err); + + sz = sizeof(*l); + l = btf_add_type_mem(btf, sz); + if (!l) + return libbpf_err(-ENOMEM); + + l->func = func; + l->loc_proto = loc_proto; + l->offset = offset; + + /* update parent type's vlen */ + t = btf_last_type(btf); + err = btf_type_inc_vlen(t); + if (err) + return libbpf_err(err); + + btf_hdr_update_type_len(btf, btf->hdr.type_len + sz); + return 0; +} + struct btf_ext_sec_info_param { __u32 off; __u32 len; @@ -4114,8 +4372,8 @@ static struct btf_dedup *btf_dedup_new(struct btf *btf, const struct btf_dedup_o for (i = 1; i < type_cnt; i++) { struct btf_type *t = btf_type_by_id(d->btf, i); - /* VAR and DATASEC are never deduped and are self-canonical */ - if (btf_is_var(t) || btf_is_datasec(t)) + /* VAR, DATASEC and LOCSEC are never deduped and are self-canonical */ + if (btf_is_var(t) || btf_is_datasec(t) || btf_is_locsec(t)) d->map[i] = i; else d->map[i] = BTF_UNPROCESSED_ID; @@ -4395,6 +4653,45 @@ static bool btf_compat_enum(struct btf_type *t1, struct btf_type *t2) btf_is_any_enum(t1) && btf_is_any_enum(t2); } +static long btf_hash_loc_param(struct btf_type *t) +{ + long h = btf_hash_common(t); + __u32 *v = (__u32 *)btf_loc_param(t); + int i, vlen = btf_vlen(t); + + for (i = 0; i <= vlen; i++, v++) + h = hash_combine(h, *v); + return h; +} + +static long btf_hash_loc_proto(struct btf_type *t) +{ + __u32 *p = btf_loc_proto_params(t); + long h = btf_hash_common(t); + int i, vlen = btf_vlen(t); + + for (i = 0; i < vlen; i++, p++) + h = hash_combine(h, *p); + return h; +} + +static bool btf_equal_loc_param(struct btf_type *t1, struct btf_type *t2) +{ + struct btf_loc_param *p1 = btf_loc_param(t1); + struct btf_loc_param *p2 = btf_loc_param(t2); + __u32 *v1 = (__u32 *)(p1 + 1); + __u32 *v2 = (__u32 *)(p2 + 1); + int i, vlen = btf_vlen(t1); + + if (!btf_equal_common(t1, t2)) + return false; + for (i = 0; i < vlen; i++, v1++, v2++) { + if (*v1 != *v2) + return false; + } + return true; +} + /* * Calculate type signature hash of STRUCT/UNION, ignoring referenced type IDs, * as referenced type IDs equivalence is established separately during type @@ -4622,6 +4919,12 @@ static int btf_dedup_prep(struct btf_dedup *d) case BTF_KIND_FUNC_PROTO: h = btf_hash_fnproto(t); break; + case BTF_KIND_LOC_PARAM: + h = btf_hash_loc_param(t); + break; + case BTF_KIND_LOC_PROTO: + h = btf_hash_loc_proto(t); + break; default: pr_debug("unknown kind %d for type [%d]\n", btf_kind(t), type_id); return -EINVAL; @@ -4664,6 +4967,8 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id) case BTF_KIND_DATASEC: case BTF_KIND_DECL_TAG: case BTF_KIND_TYPE_TAG: + case BTF_KIND_LOC_PROTO: + case BTF_KIND_LOCSEC: return 0; case BTF_KIND_INT: @@ -4713,6 +5018,18 @@ static int btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id) } break; + case BTF_KIND_LOC_PARAM: + h = btf_hash_loc_param(t); + for_each_dedup_cand(d, hash_entry, h) { + cand_id = hash_entry->value; + cand = btf_type_by_id(d->btf, cand_id); + if (btf_equal_loc_param(t, cand)) { + new_id = cand_id; + break; + } + } + break; + default: return -EINVAL; } @@ -5145,6 +5462,13 @@ static int btf_dedup_is_equiv(struct btf_dedup *d, __u32 cand_id, return 1; } + case BTF_KIND_LOC_PARAM: + return btf_equal_loc_param(cand_type, canon_type); + + case BTF_KIND_LOC_PROTO: + case BTF_KIND_LOCSEC: + return 0; + default: return -EINVAL; } @@ -5489,6 +5813,41 @@ static int btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id) break; } + case BTF_KIND_LOC_PROTO: { + __u32 *p1, *p2; + __u32 i, vlen; + + p1 = btf_loc_proto_params(t); + vlen = btf_vlen(t); + + for (i = 0; i < vlen; i++, p1++) { + ref_type_id = btf_dedup_ref_type(d, *p1); + if (ref_type_id < 0) + return ref_type_id; + *p1 = ref_type_id; + } + + h = btf_hash_loc_proto(t); + for_each_dedup_cand(d, hash_entry, h) { + cand_id = hash_entry->value; + cand = btf_type_by_id(d->btf, cand_id); + if (!btf_equal_common(t, cand)) + continue; + vlen = btf_vlen(cand); + p1 = btf_loc_proto_params(t); + p2 = btf_loc_proto_params(cand); + if (vlen == 0) { + new_id = cand_id; + break; + } + if (memcmp(p1, p2, vlen * sizeof(__u32)) == 0) { + new_id = cand_id; + break; + } + } + break; + } + default: return -EINVAL; } @@ -5970,8 +6329,11 @@ static int btf_add_distilled_type_ids(struct btf_distill *dist, __u32 i) case BTF_KIND_CONST: case BTF_KIND_RESTRICT: case BTF_KIND_VOLATILE: + case BTF_KIND_FUNC: case BTF_KIND_FUNC_PROTO: case BTF_KIND_TYPE_TAG: + case BTF_KIND_LOC_PARAM: + case BTF_KIND_LOC_PROTO: dist->id_map[*id] = *id; break; default: @@ -5997,7 +6359,7 @@ static int btf_add_distilled_type_ids(struct btf_distill *dist, __u32 i) static int btf_add_distilled_types(struct btf_distill *dist) { - bool adding_to_base = dist->pipe.dst->start_id == 1; + bool adding_to_base = dist->pipe.dst->base_btf == NULL; int id = btf__type_cnt(dist->pipe.dst); struct btf_type *t; int i, err = 0; @@ -6065,8 +6427,12 @@ static int btf_add_distilled_types(struct btf_distill *dist) case BTF_KIND_CONST: case BTF_KIND_RESTRICT: case BTF_KIND_VOLATILE: + case BTF_KIND_FUNC: case BTF_KIND_FUNC_PROTO: case BTF_KIND_TYPE_TAG: + case BTF_KIND_LOC_PARAM: + case BTF_KIND_LOC_PROTO: + case BTF_KIND_LOCSEC: /* All other types are added to split BTF. */ if (adding_to_base) continue; diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h index 587172c0de08..57f12630c5d1 100644 --- a/tools/lib/bpf/btf.h +++ b/tools/lib/bpf/btf.h @@ -274,6 +274,20 @@ LIBBPF_API int btf__add_decl_tag(struct btf *btf, const char *value, int ref_typ LIBBPF_API int btf__add_decl_attr(struct btf *btf, const char *value, int ref_type_id, int component_idx); +/* location construction APIs */ +LIBBPF_API int btf__add_loc_param(struct btf *btf, __u32 size, __u32 flags); + +LIBBPF_API int btf__add_loc_param_value(struct btf *btf, __u32 value); + +LIBBPF_API int btf__add_loc_proto(struct btf *btf); + +LIBBPF_API int btf__add_loc_proto_param(struct btf *btf, __u32 id); + +LIBBPF_API int btf__add_locsec(struct btf *btf, const char *name); + +LIBBPF_API int btf__add_locsec_loc(struct btf *btf, __u32 func, __u32 loc_proto, + __u32 offset); + struct btf_dedup_opts { size_t sz; /* optional .BTF.ext info to dedup along the main BTF info */ @@ -431,6 +445,12 @@ btf_dump__dump_type_data(struct btf_dump *d, __u32 id, #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 */ +#define BTF_KIND_LOC_PARAM 20 /* Parameter at location */ +#define BTF_KIND_LOC_PROTO 21 /* Parameter set at location */ +#define BTF_KIND_LOCSEC 22 /* Section containing location info */ + +struct btf_loc_param; +struct btf_loc; static inline __u16 btf_kind(const struct btf_type *t) { @@ -569,6 +589,21 @@ 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_is_loc_param(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_LOC_PARAM; +} + +static inline bool btf_is_loc_proto(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_LOC_PROTO; +} + +static inline bool btf_is_locsec(const struct btf_type *t) +{ + return btf_kind(t) == BTF_KIND_LOCSEC; +} + static inline bool btf_kind_core_compat(const struct btf_type *t1, const struct btf_type *t2) { @@ -683,6 +718,21 @@ static inline struct btf_decl_tag *btf_decl_tag(const struct btf_type *t) return (struct btf_decl_tag *)(t + 1); } +static inline struct btf_loc_param *btf_loc_param(const struct btf_type *t) +{ + return (struct btf_loc_param *)(t + 1); +} + +static inline __u32 *btf_loc_proto_params(const struct btf_type *t) +{ + return (__u32 *)(t + 1); +} + +static inline struct btf_loc *btf_locsec_locs(const struct btf_type *t) +{ + return (struct btf_loc *)(t + 1); +} + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c index 123c448f20c7..fa995c02a170 100644 --- a/tools/lib/bpf/btf_dump.c +++ b/tools/lib/bpf/btf_dump.c @@ -328,6 +328,9 @@ static int btf_dump_mark_referenced(struct btf_dump *d) case BTF_KIND_ENUM64: case BTF_KIND_FWD: case BTF_KIND_FLOAT: + case BTF_KIND_LOC_PARAM: + case BTF_KIND_LOC_PROTO: + case BTF_KIND_LOCSEC: break; case BTF_KIND_VOLATILE: @@ -609,6 +612,9 @@ static int btf_dump_order_type(struct btf_dump *d, __u32 id, bool through_ptr) case BTF_KIND_VAR: case BTF_KIND_DATASEC: case BTF_KIND_DECL_TAG: + case BTF_KIND_LOC_PARAM: + case BTF_KIND_LOC_PROTO: + case BTF_KIND_LOCSEC: d->type_states[id].order_state = ORDERED; return 0; @@ -2525,6 +2531,9 @@ static int btf_dump_dump_type_data(struct btf_dump *d, case BTF_KIND_FUNC: case BTF_KIND_FUNC_PROTO: case BTF_KIND_DECL_TAG: + case BTF_KIND_LOC_PARAM: + case BTF_KIND_LOC_PROTO: + case BTF_KIND_LOCSEC: err = btf_dump_unsupported_data(d, t, id); break; case BTF_KIND_INT: diff --git a/tools/lib/bpf/btf_iter.c b/tools/lib/bpf/btf_iter.c index 9a6c822c2294..54428e422ec3 100644 --- a/tools/lib/bpf/btf_iter.c +++ b/tools/lib/bpf/btf_iter.c @@ -29,6 +29,7 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t, case BTF_KIND_FLOAT: case BTF_KIND_ENUM: case BTF_KIND_ENUM64: + case BTF_KIND_LOC_PARAM: it->desc = (struct btf_field_desc) {}; break; case BTF_KIND_FWD: @@ -71,6 +72,20 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t, 1, {offsetof(struct btf_var_secinfo, type)} }; break; + case BTF_KIND_LOC_PROTO: + it->desc = (struct btf_field_desc) { + 0, {}, + sizeof(__u32), + 1, {0}}; + break; + case BTF_KIND_LOCSEC: + it->desc = (struct btf_field_desc) { + 0, {}, + sizeof(struct btf_loc), + 2, {offsetof(struct btf_loc, func), + offsetof(struct btf_loc, loc_proto)}}; + break; + default: return -EINVAL; } @@ -94,6 +109,8 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t, case BTF_KIND_DECL_TAG: case BTF_KIND_TYPE_TAG: case BTF_KIND_DATASEC: + case BTF_KIND_LOC_PARAM: + case BTF_KIND_LOC_PROTO: it->desc = (struct btf_field_desc) { 1, {offsetof(struct btf_type, name_off)} }; @@ -127,6 +144,11 @@ int btf_field_iter_init(struct btf_field_iter *it, struct btf_type *t, 1, {offsetof(struct btf_param, name_off)} }; break; + case BTF_KIND_LOCSEC: + it->desc = (struct btf_field_desc) { + 1, {offsetof(struct btf_type, name_off)} + }; + break; default: return -EINVAL; } diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 08ab2ea881fb..7f51783df129 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -460,6 +460,12 @@ LIBBPF_1.8.0 { global: bpf_program__attach_tracing_multi; bpf_program__clone; + btf__add_loc_param; + btf__add_loc_param_value; + btf__add_loc_proto; + btf__add_loc_proto_param; + btf__add_locsec; + btf__add_locsec_loc; btf__find_by_name_kind_own; btf__new_empty_opts; } LIBBPF_1.7.0; diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h index cb4d96233844..ebac8db1ccfd 100644 --- a/tools/lib/bpf/libbpf_internal.h +++ b/tools/lib/bpf/libbpf_internal.h @@ -580,7 +580,7 @@ struct btf_field_desc { /* member struct size, or zero, if no members */ int m_sz; /* repeated per-member offsets */ - int m_off_cnt, m_offs[1]; + int m_off_cnt, m_offs[22]; }; struct btf_field_iter { -- 2.43.5
