struct btf { /* raw BTF data in native endianness */ void *raw_data; /* raw BTF data in non-native endianness */ void *raw_data_swapped;
__u32 raw_size; /* whether target endianness differs from the native one */ bool swapped_endian;
void *types_data;
size_t types_data_cap; /* used size stored in hdr->type_len */
/* type ID to `struct btf_type *` lookup index *type_offs[0]correspondstothefirstnon-VOIDtype: *-forbaseBTFit'stype[1]; *-forsplitBTFit'sthefirstnon-baseBTFtype.
*/
__u32 *type_offs;
size_t type_offs_cap; /* number of types in this BTF instance: *-doesn'tincludespecial[0]voidtype; *-forsplitBTFcountsnumberoftypesaddedontopofbaseBTF.
*/
__u32 nr_types; /* if not NULL, points to the base BTF on top of which the current *splitBTFisbased
*/ struct btf *base_btf; /* BTF type ID of the first type in this BTF instance: *-forbaseBTFit'sequalto1; *-forsplitBTFit'sequaltobiggesttypeIDofbaseBTFplus1.
*/ int start_id; /* logical string offset of this BTF instance: *-forbaseBTFit'sequalto0; *-forsplitBTFit'sequaltototalsizeofbaseBTF'sstringsectionsize.
*/ int start_str_off;
/* only one of strs_data or strs_set can be non-NULL, depending on *whetherBTFisinamodifiablestate(strs_setisused)ornot *(strs_datapointsinsideraw_data)
*/ void *strs_data; /* a set of unique strings */ struct strset *strs_set; /* whether strings are already deduplicated */ bool strs_deduped;
/* whether base_btf should be freed in btf_free for this instance */ bool owns_base;
/* whether raw_data is a (read-only) mmap */ bool raw_data_is_mmap;
/* BTF object FD, if loaded into kernel */ int fd;
/* Pointer size (in bytes) for a target architecture of this BTF */ int ptr_sz;
};
/* requested more than the set limit */ if (cur_cnt + add_cnt > max_cnt) return NULL;
new_cnt = *cap_cnt;
new_cnt += new_cnt / 4; /* expand by 25% */ if (new_cnt < 16) /* but at least 16 elements */
new_cnt = 16; if (new_cnt > max_cnt) /* but not exceeding a set limit */
new_cnt = max_cnt; if (new_cnt < cur_cnt + add_cnt) /* also ensure we have enough memory */
new_cnt = cur_cnt + add_cnt;
new_data = libbpf_reallocarray(*data, new_cnt, elem_sz); if (!new_data) return NULL;
/* zero out newly allocated portion of memory */
memset(new_data + (*cap_cnt) * elem_sz, 0, (new_cnt - *cap_cnt) * elem_sz);
/* Ensure given dynamically allocated memory region has enough allocated space *toaccommodate*need_cnt*elementsofsize*elem_sz*byteseach
*/ int libbpf_ensure_mem(void **data, size_t *cap_cnt, size_t elem_sz, size_t need_cnt)
{ void *p;
if (need_cnt <= *cap_cnt) return0;
p = libbpf_add_mem(data, cap_cnt, elem_sz, *cap_cnt, SIZE_MAX, need_cnt - *cap_cnt); if (!p) return -ENOMEM;
if ((longlong)hdr->type_off + hdr->type_len > hdr->str_off) {
pr_debug("Invalid BTF data sections layout: type data at %u + %u, strings data at %u + %u\n",
hdr->type_off, hdr->type_len, hdr->str_off, hdr->str_len); return -EINVAL;
}
if (hdr->type_off % 4) {
pr_debug("BTF type section is not aligned to 4 bytes\n"); return -EINVAL;
}
switch (btf_kind(t)) { case BTF_KIND_FWD: case BTF_KIND_CONST: case BTF_KIND_VOLATILE: case BTF_KIND_RESTRICT: case BTF_KIND_PTR: case BTF_KIND_TYPEDEF: case BTF_KIND_FUNC: case BTF_KIND_FLOAT: case BTF_KIND_TYPE_TAG: return0; case BTF_KIND_INT:
*(__u32 *)(t + 1) = bswap_32(*(__u32 *)(t + 1)); return0; case BTF_KIND_ENUM: for (i = 0, e = btf_enum(t); i < vlen; i++, e++) {
e->name_off = bswap_32(e->name_off);
e->val = bswap_32(e->val);
} return0; 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);
} return0; case BTF_KIND_ARRAY:
a = btf_array(t);
a->type = bswap_32(a->type);
a->index_type = bswap_32(a->index_type);
a->nelems = bswap_32(a->nelems); return0; case BTF_KIND_STRUCT: case BTF_KIND_UNION: for (i = 0, m = btf_members(t); i < vlen; i++, m++) {
m->name_off = bswap_32(m->name_off);
m->type = bswap_32(m->type);
m->offset = bswap_32(m->offset);
} return0; case BTF_KIND_FUNC_PROTO: for (i = 0, p = btf_params(t); i < vlen; i++, p++) {
p->name_off = bswap_32(p->name_off);
p->type = bswap_32(p->type);
} return0; case BTF_KIND_VAR:
btf_var(t)->linkage = bswap_32(btf_var(t)->linkage); return0; case BTF_KIND_DATASEC: for (i = 0, v = btf_var_secinfos(t); i < vlen; i++, v++) {
v->type = bswap_32(v->type);
v->offset = bswap_32(v->offset);
v->size = bswap_32(v->size);
} return0; case BTF_KIND_DECL_TAG:
btf_decl_tag(t)->component_idx = bswap_32(btf_decl_tag(t)->component_idx); return0; default:
pr_debug("Unsupported BTF_KIND:%u\n", btf_kind(t)); return -EINVAL;
}
}
switch (kind) { case BTF_KIND_UNKN: case BTF_KIND_INT: case BTF_KIND_FWD: case BTF_KIND_FLOAT: break; case BTF_KIND_PTR: case BTF_KIND_TYPEDEF: case BTF_KIND_VOLATILE: case BTF_KIND_CONST: case BTF_KIND_RESTRICT: case BTF_KIND_VAR: case BTF_KIND_DECL_TAG: case BTF_KIND_TYPE_TAG:
err = btf_validate_id(btf, t->type, id); if (err) return err; break; case BTF_KIND_ARRAY: { conststruct btf_array *a = btf_array(t);
err = btf_validate_id(btf, a->type, id);
err = err ?: btf_validate_id(btf, a->index_type, id); if (err) return err; break;
} case BTF_KIND_STRUCT: case BTF_KIND_UNION: { conststruct btf_member *m = btf_members(t);
n = btf_vlen(t); for (i = 0; i < n; i++, m++) {
err = btf_validate_str(btf, m->name_off, "field name", id);
err = err ?: btf_validate_id(btf, m->type, id); if (err) return err;
} break;
} case BTF_KIND_ENUM: { conststruct btf_enum *m = btf_enum(t);
n = btf_vlen(t); for (i = 0; i < n; i++, m++) {
err = btf_validate_str(btf, m->name_off, "enum name", id); if (err) return err;
} break;
} case BTF_KIND_ENUM64: { conststruct btf_enum64 *m = btf_enum64(t);
n = btf_vlen(t); for (i = 0; i < n; i++, m++) {
err = btf_validate_str(btf, m->name_off, "enum name", id); if (err) return err;
} break;
} case BTF_KIND_FUNC: { conststruct btf_type *ft;
err = btf_validate_id(btf, t->type, id); if (err) return err;
ft = btf__type_by_id(btf, t->type); if (btf_kind(ft) != BTF_KIND_FUNC_PROTO) {
pr_warn("btf: type [%u]: referenced type [%u] is not FUNC_PROTO\n", id, t->type); return -EINVAL;
} break;
} case BTF_KIND_FUNC_PROTO: { conststruct btf_param *m = btf_params(t);
n = btf_vlen(t); for (i = 0; i < n; i++, m++) {
err = btf_validate_str(btf, m->name_off, "param name", id);
err = err ?: btf_validate_id(btf, m->type, id); if (err) return err;
} break;
} case BTF_KIND_DATASEC: { conststruct btf_var_secinfo *m = btf_var_secinfos(t);
n = btf_vlen(t); for (i = 0; i < n; i++, m++) {
err = btf_validate_id(btf, m->type, id); if (err) return err;
} break;
} default:
pr_warn("btf: type [%u]: unrecognized kind %u\n", id, kind); return -EINVAL;
} return0;
}
/* Validate basic sanity of BTF. It's intentionally less thorough than *kernel'svalidationandvalidatesonlypropertiesofBTFthatlibbpfrelies *ontobecorrect(e.g.,validtypeIDs,validstringoffsets,etc)
*/ staticint btf_sanity_check(conststruct btf *btf)
{ conststruct btf_type *t;
__u32 i, n = btf__type_cnt(btf); int err;
for (i = btf->start_id; i < n; i++) {
t = btf_type_by_id(btf, i);
err = btf_validate_type(btf, t, i); if (err) return err;
} return0;
}
/* Return pointer size this BTF instance assumes. The size is heuristically *determinedbylookingfor'long'or'unsignedlong'integertypeand *recordingitssizeinbytes.IfBTFtypeinformationdoesn'thaveanysuch *type,thisfunctionreturns0.Inthelattercase,nativearchitecture's *pointersizeisassumed,sowillbeeither4or8,dependingon *architecturethatlibbpfwascompiledfor.It'spossibletooverride *guessedvaluebyusingbtf__set_pointer_size()API.
*/
size_t btf__pointer_size(conststruct btf *btf)
{ if (!btf->ptr_sz)
((struct btf *)btf)->ptr_sz = determine_ptr_size(btf);
if (btf->ptr_sz < 0) /* not enough BTF type info to guess */ return0;
return btf->ptr_sz;
}
/* Override or set pointer size in bytes. Only values of 4 and 8 are *supported.
*/ int btf__set_pointer_size(struct btf *btf, size_t ptr_sz)
{ if (ptr_sz != 4 && ptr_sz != 8) return libbpf_err(-EINVAL);
btf->ptr_sz = ptr_sz; return0;
}
t = btf__type_by_id(btf, type_id); for (i = 0; i < MAX_RESOLVE_DEPTH && !btf_type_is_void_or_null(t); i++) { switch (btf_kind(t)) { case BTF_KIND_INT: 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; goto done; case BTF_KIND_PTR:
size = btf_ptr_sz(btf); goto done; case BTF_KIND_TYPEDEF: case BTF_KIND_VOLATILE: case BTF_KIND_CONST: case BTF_KIND_RESTRICT: case BTF_KIND_VAR: case BTF_KIND_DECL_TAG: case BTF_KIND_TYPE_TAG:
type_id = t->type; break; case BTF_KIND_ARRAY:
array = btf_array(t); if (nelems && array->nelems > UINT32_MAX / nelems) return libbpf_err(-E2BIG);
nelems *= array->nelems;
type_id = array->type; break; default: return libbpf_err(-EINVAL);
}
t = btf__type_by_id(btf, type_id);
}
done: if (size < 0) return libbpf_err(-EINVAL); if (nelems && size > UINT32_MAX / nelems) return libbpf_err(-E2BIG);
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: return btf_ptr_sz(btf); case BTF_KIND_TYPEDEF: case BTF_KIND_VOLATILE: case BTF_KIND_CONST: case BTF_KIND_RESTRICT: case BTF_KIND_TYPE_TAG: return btf__align_of(btf, t->type); case BTF_KIND_ARRAY: return btf__align_of(btf, btf_array(t)->type); case BTF_KIND_STRUCT: case BTF_KIND_UNION: { conststruct btf_member *m = btf_members(t);
__u16 vlen = btf_vlen(t); int i, max_align = 1, align;
for (i = 0; i < vlen; i++, m++) {
align = btf__align_of(btf, m->type); if (align <= 0) return libbpf_err(align);
max_align = max(max_align, align);
/* if field offset isn't aligned according to field *type'salignment,thenstructmustbepacked
*/ if (btf_member_bitfield_size(t, i) == 0 &&
(m->offset % (8 * align)) != 0) return1;
}
/* if struct/union size isn't a multiple of its alignment, *thenstructmustbepacked
*/ if ((t->size % max_align) != 0) return1;
void btf__free(struct btf *btf)
{ if (IS_ERR_OR_NULL(btf)) return;
if (btf->fd >= 0)
close(btf->fd);
if (btf_is_modifiable(btf)) { /* if BTF was modified after loading, it will have a split *in-memoryrepresentationforheader,types,andstrings *sections,soweneedtofreeallofthemindividually.It *mightstillhaveacachedcontiguousrawdatapresent, *whichwillbeunconditionallyfreedbelow.
*/
free(btf->hdr);
free(btf->types_data);
strset__free(btf->strs_set);
}
btf_free_raw_data(btf);
free(btf->raw_data_swapped);
free(btf->type_offs); if (btf->owns_base)
btf__free(btf->base_btf);
free(btf);
}
idx++; if (gelf_getshdr(scn, &sh) != &sh) {
pr_warn("failed to get section(%d) header from %s\n",
idx, path); goto err;
}
name = elf_strptr(elf, shstrndx, sh.sh_name); if (!name) {
pr_warn("failed to get section(%d) name from %s\n",
idx, path); goto err;
}
if (strcmp(name, BTF_ELF_SEC) == 0)
field = &secs->btf_data; elseif (strcmp(name, BTF_EXT_ELF_SEC) == 0)
field = &secs->btf_ext_data; elseif (strcmp(name, BTF_BASE_ELF_SEC) == 0)
field = &secs->btf_base_data; else continue;
if (sh.sh_type != SHT_PROGBITS) {
pr_warn("unexpected section type (%d) of section(%d, %s) from %s\n",
sh.sh_type, idx, name, path); goto err;
}
data = elf_getdata(scn, 0); if (!data) {
pr_warn("failed to get section(%d, %s) data from %s\n",
idx, name, path); goto err;
}
*field = data;
}
switch (gelf_getclass(elf)) { case ELFCLASS32:
btf__set_pointer_size(btf, 4); break; case ELFCLASS64:
btf__set_pointer_size(btf, 8); break; default:
pr_warn("failed to get ELF class (bitness) for %s\n", path); break;
}
opts.token_fd = token_fd; if (token_fd)
opts.btf_flags |= BPF_F_TOKEN_FD;
btf->fd = bpf_btf_load(raw_data, raw_size, &opts); if (btf->fd < 0) { /* time to turn on verbose mode and try again */ if (log_level == 0) {
log_level = 1; goto retry_load;
} /* only retry if caller didn't provide custom log_buf, but *makesurewecanneveroverflowbuf_sz
*/ if (!log_buf && errno == ENOSPC && buf_sz <= UINT_MAX / 2) goto retry_load;
err = -errno;
pr_warn("BTF loading error: %s\n", errstr(err)); /* don't print out contents of custom log_buf */ if (!log_buf && buf[0])
pr_warn("-- BEGIN BTF LOAD LOG ---\n%s\n-- END BTF LOAD LOG --\n", buf);
}
data = swap_endian ? btf->raw_data_swapped : btf->raw_data; if (data) {
*size = btf->raw_size; return data;
}
data_sz = hdr->hdr_len + hdr->type_len + hdr->str_len;
data = calloc(1, data_sz); if (!data) return NULL;
p = data;
memcpy(p, hdr, hdr->hdr_len); if (swap_endian)
btf_bswap_hdr(p);
p += hdr->hdr_len;
memcpy(p, btf->types_data, hdr->type_len); if (swap_endian) { for (i = 0; i < btf->nr_types; i++) {
t = p + btf->type_offs[i]; /* btf_bswap_type_rest() relies on native t->info, so *weswapbasetypeinfoafterweswappedallthe *additionalinformation
*/ if (btf_bswap_type_rest(t)) goto err_out;
btf_bswap_type_base(t);
}
}
p += hdr->type_len;
memcpy(p, btf_strs_data(btf), hdr->str_len);
p += hdr->str_len;
/* we won't know btf_size until we call bpf_btf_get_info_by_fd(). so *let'sstartwithasanedefault-4KiBhere-andresizeitonlyif *bpf_btf_get_info_by_fd()needsabiggerbuffer.
*/
last_size = 4096;
ptr = malloc(last_size); if (!ptr) return ERR_PTR(-ENOMEM);
staticvoid btf_invalidate_raw_data(struct btf *btf)
{ if (btf->raw_data)
btf_free_raw_data(btf); if (btf->raw_data_swapped) {
free(btf->raw_data_swapped);
btf->raw_data_swapped = NULL;
}
}
/* Ensure BTF is ready to be modified (by splitting into a three memory *regionsforheader,types,andstrings).Alsoinvalidatecached *raw_data,ifany.
*/ staticint btf_ensure_modifiable(struct btf *btf)
{ void *hdr, *types; struct strset *set = NULL; int err = -ENOMEM;
if (btf_is_modifiable(btf)) { /* any BTF modification invalidates raw_data */
btf_invalidate_raw_data(btf); return0;
}
/* split raw data into three memory regions */
hdr = malloc(btf->hdr->hdr_len);
types = malloc(btf->hdr->type_len); if (!hdr || !types) goto err_out;
/* build lookup index for all strings */
set = strset__new(BTF_MAX_STR_OFFSET, btf->strs_data, btf->hdr->str_len); if (IS_ERR(set)) {
err = PTR_ERR(set); goto err_out;
}
/* only when everything was successful, update internal state */
btf->hdr = hdr;
btf->types_data = types;
btf->types_data_cap = btf->hdr->type_len;
btf->strs_data = NULL;
btf->strs_set = set; /* if BTF was created from scratch, all strings are guaranteed to be *uniqueanddeduplicated
*/ if (btf->hdr->str_len == 0)
btf->strs_deduped = true; if (!btf->base_btf && btf->hdr->str_len == 1)
btf->strs_deduped = true;
/* Find an offset in BTF string section that corresponds to a given string *s*. *Returns: *->0offsetintostringsection,ifstringisfound; *--ENOENT,ifstringisnotinthestringsection; *-<0,onanyothererror.
*/ int btf__find_str(struct btf *btf, constchar *s)
{ int off;
if (btf->base_btf) {
off = btf__find_str(btf->base_btf, s); if (off != -ENOENT) return off;
}
/* BTF needs to be in a modifiable state to build string lookup index */ if (btf_ensure_modifiable(btf)) return libbpf_err(-ENOMEM);
off = strset__find_str(btf->strs_set, s); if (off < 0) return libbpf_err(off);
return btf->start_str_off + off;
}
/* Add a string s to the BTF string section. *Returns: *->0offsetintostringsection,onsuccess; *-<0,onerror.
*/ int btf__add_str(struct btf *btf, constchar *s)
{ int off;
if (btf->base_btf) {
off = btf__find_str(btf->base_btf, s); if (off != -ENOENT) return off;
}
if (btf_ensure_modifiable(btf)) return libbpf_err(-ENOMEM);
off = strset__add_str(btf->strs_set, s); if (off < 0) return libbpf_err(off);
off = btf__add_str(p->dst, btf__str_by_offset(p->src, *str_off)); if (off < 0) return off;
/* Remember string mapping from src to dst. It avoids *performingexpensivestringcomparisons.
*/ if (p->str_off_map) {
err = hashmap__append(p->str_off_map, *str_off, off); if (err) return err;
}
/* pre-allocate enough memory for new types */
t = btf_add_type_mem(btf, data_sz); if (!t) return libbpf_err(-ENOMEM);
/* pre-allocate enough memory for type offset index for new types */
off = btf_add_type_offs_mem(btf, cnt); if (!off) return libbpf_err(-ENOMEM);
/* Map the string offsets from src_btf to the offsets from btf to improve performance */
p.str_off_map = hashmap__new(btf_dedup_identity_hash_fn, btf_dedup_equal_fn, NULL); if (IS_ERR(p.str_off_map)) return libbpf_err(-ENOMEM);
/* bulk copy types data for all types from src_btf */
memcpy(t, src_btf->types_data, data_sz);
for (i = 0; i < cnt; i++) { struct btf_field_iter it;
__u32 *type_id, *str_off;
sz = btf_type_size(t); if (sz < 0) { /* unlikely, has to be corrupted src_btf */
err = sz; goto err_out;
}
/* fill out type ID to type offset mapping for lookups by type ID */
*off = t - btf->types_data;
/* add, dedup, and remap strings referenced by this BTF type */
err = btf_field_iter_init(&it, t, BTF_FIELD_ITER_STRS); if (err) goto err_out; while ((str_off = btf_field_iter_next(&it))) {
err = btf_rewrite_str(&p, str_off); if (err) goto err_out;
}
/* remap all type IDs referenced from this BTF type */
err = btf_field_iter_init(&it, t, BTF_FIELD_ITER_IDS); if (err) goto err_out;
while ((type_id = btf_field_iter_next(&it))) { if (!*type_id) /* nothing to do for VOID references */ continue;
/* we haven't updated btf's type count yet, so *btf->start_id+btf->nr_types-1isthetypeIDoffsetweshould *addtoallnewlyaddedBTFtypes
*/
*type_id += btf->start_id + btf->nr_types - 1;
}
/* go to next type data and type offset index entry */
t += sz;
off++;
}
/* Up until now any of the copied type data was effectively invisible, *soifweexitedearlybeforethispointduetoerror,BTFwouldbe *effectivelyunmodified.Therewouldbeextrainternalmemory *pre-allocated,butitwouldnotbeavailableforquerying.Butnow *thatwe'vecopiedandrewrittenallthedatasuccessfully,wecan *updatetypecountandvariousinternaloffsetsandsizesto *"commit"thechangesandmadethemvisibletotheoutsideworld.
*/
btf->hdr->type_len += data_sz;
btf->hdr->str_off += data_sz;
btf->nr_types += cnt;
hashmap__free(p.str_off_map);
/* return type ID of the first added BTF type */ return btf->start_id + btf->nr_types - cnt;
err_out: /* zero out preallocated memory as if it was just allocated with *libbpf_add_mem()
*/
memset(btf->types_data + btf->hdr->type_len, 0, data_sz);
memset(btf->strs_data + old_strs_len, 0, btf->hdr->str_len - old_strs_len);
/* and now restore original strings section size; types data size *wasn'tmodified,sodoesn'tneedrestoring,seebigcommentabove
*/
btf->hdr->str_len = old_strs_len;
hashmap__free(p.str_off_map);
return libbpf_err(err);
}
/* *AppendnewBTF_KIND_INTtypewith: *-*name*-non-empty,non-NULLtypename; *-*sz*-power-of-2(1,2,4,..)sizeofthetype,inbytes; *-encodingisacombinationofBTF_INT_SIGNED,BTF_INT_CHAR,BTF_INT_BOOL. *Returns: *->0,typeIDofnewlyaddedBTFtype; *-<0,onerror.
*/ int btf__add_int(struct btf *btf, constchar *name, size_t byte_sz, int encoding)
{ struct btf_type *t; int sz, name_off;
/* non-empty name */ if (!name || !name[0]) return libbpf_err(-EINVAL); /* byte_sz must be power of 2 */ if (!byte_sz || (byte_sz & (byte_sz - 1)) || byte_sz > 16) return libbpf_err(-EINVAL); if (encoding & ~(BTF_INT_SIGNED | BTF_INT_CHAR | BTF_INT_BOOL)) return libbpf_err(-EINVAL);
/* deconstruct BTF, if necessary, and invalidate raw_data */ if (btf_ensure_modifiable(btf)) return libbpf_err(-ENOMEM);
sz = sizeof(struct btf_type) + sizeof(int);
t = btf_add_type_mem(btf, sz); if (!t) return libbpf_err(-ENOMEM);
/* if something goes wrong later, we might end up with an extra string, *butthatshouldn'tbeaproblem,becauseBTFcan'tbeconstructed *completelyanywayandwillmostprobablybejustdiscarded
*/
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_INT, 0, 0);
t->size = byte_sz; /* set INT info, we don't allow setting legacy bit offset/size */
*(__u32 *)(t + 1) = (encoding << 24) | (byte_sz * 8);
/* it's completely legal to append BTF types with type IDs pointing forward to *typesthathaven'tbeenappendedyet,soweonlymakesurethatidlooks *sane,wecan'tguaranteethatIDwillalwaysbevalid
*/ staticint validate_type_id(int id)
{ if (id < 0 || id > BTF_MAX_NR_TYPES) return -EINVAL; return0;
}
/* generic append function for PTR, TYPEDEF, CONST/VOLATILE/RESTRICT */ staticint btf_add_ref_kind(struct btf *btf, int kind, constchar *name, int ref_type_id, int kflag)
{ struct btf_type *t; int sz, name_off = 0;
if (validate_type_id(ref_type_id)) return libbpf_err(-EINVAL);
if (btf_ensure_modifiable(btf)) return libbpf_err(-ENOMEM);
sz = sizeof(struct btf_type);
t = btf_add_type_mem(btf, sz); if (!t) return libbpf_err(-ENOMEM);
if (name && name[0]) {
name_off = btf__add_str(btf, name); if (name_off < 0) return name_off;
}
/* generic STRUCT/UNION append function */ staticint btf_add_composite(struct btf *btf, int kind, constchar *name, __u32 bytes_sz)
{ struct btf_type *t; int sz, name_off = 0;
if (btf_ensure_modifiable(btf)) return libbpf_err(-ENOMEM);
sz = sizeof(struct btf_type);
t = btf_add_type_mem(btf, sz); if (!t) return libbpf_err(-ENOMEM);
if (name && name[0]) {
name_off = btf__add_str(btf, name); if (name_off < 0) return name_off;
}
/* start out with vlen=0 and no kflag; this will be adjusted when *addingeachmember
*/
t->name_off = name_off;
t->info = btf_type_info(kind, 0, 0);
t->size = bytes_sz;
/* last type should be union/struct */ if (btf->nr_types == 0) return libbpf_err(-EINVAL);
t = btf_last_type(btf); if (!btf_is_composite(t)) return libbpf_err(-EINVAL);
if (validate_type_id(type_id)) return libbpf_err(-EINVAL); /* best-effort bit field offset/size enforcement */
is_bitfield = bit_size || (bit_offset % 8 != 0); if (is_bitfield && (bit_size == 0 || bit_size > 255 || bit_offset > 0xffffff)) return libbpf_err(-EINVAL);
/* only offset 0 is allowed for unions */ if (btf_is_union(t) && bit_offset) return libbpf_err(-EINVAL);
/* decompose and invalidate raw data */ if (btf_ensure_modifiable(btf)) return libbpf_err(-ENOMEM);
sz = sizeof(struct btf_member);
m = btf_add_type_mem(btf, sz); if (!m) return libbpf_err(-ENOMEM);
if (name && name[0]) {
name_off = btf__add_str(btf, name); if (name_off < 0) return name_off;
}
/* byte_sz must be power of 2 */ if (!byte_sz || (byte_sz & (byte_sz - 1)) || byte_sz > 8) return libbpf_err(-EINVAL);
if (btf_ensure_modifiable(btf)) return libbpf_err(-ENOMEM);
sz = sizeof(struct btf_type);
t = btf_add_type_mem(btf, sz); if (!t) return libbpf_err(-ENOMEM);
if (name && name[0]) {
name_off = btf__add_str(btf, name); if (name_off < 0) return name_off;
}
/* start out with vlen=0; it will be adjusted when adding enum values */
t->name_off = name_off;
t->info = btf_type_info(kind, 0, is_signed);
t->size = byte_sz;
/* last type should be BTF_KIND_ENUM */ if (btf->nr_types == 0) return libbpf_err(-EINVAL);
t = btf_last_type(btf); if (!btf_is_enum(t)) return libbpf_err(-EINVAL);
/* non-empty name */ if (!name || !name[0]) return libbpf_err(-EINVAL); if (value < INT_MIN || value > UINT_MAX) return libbpf_err(-E2BIG);
/* decompose and invalidate raw data */ if (btf_ensure_modifiable(btf)) return libbpf_err(-ENOMEM);
sz = sizeof(struct btf_enum);
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 = value;
/* update parent type's vlen */
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);
/* 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;
switch (fwd_kind) { case BTF_FWD_STRUCT: case BTF_FWD_UNION: { struct btf_type *t; int id;
id = btf_add_ref_kind(btf, BTF_KIND_FWD, name, 0, 0); if (id <= 0) return id;
t = btf_type_by_id(btf, id);
t->info = btf_type_info(BTF_KIND_FWD, 0, fwd_kind == BTF_FWD_UNION); return id;
} case BTF_FWD_ENUM: /* enum forward in BTF currently is just an enum with no enum *values;wealsoassumeastandard4-bytesizeforit
*/ return btf__add_enum(btf, name, sizeof(int)); default: return libbpf_err(-EINVAL);
}
}
/* *AppendnewBTF_KING_TYPEDEFtypewith: *-*name*,non-empty/non-NULLname; *-*ref_type_id*-referencedtypeID,itmightnotexistyet; *Returns: *->0,typeIDofnewlyaddedBTFtype; *-<0,onerror.
*/ int btf__add_typedef(struct btf *btf, constchar *name, int ref_type_id)
{ if (!name || !name[0]) return libbpf_err(-EINVAL);
/* *AppendnewBTF_KIND_FUNC_PROTOwith: *-*ret_type_id*-typeIDforreturnresultofafunction. * *Functionprototypeinitiallyhasnoarguments,buttheycanbeaddedby *btf__add_func_param()onebyone,immediatelyafter *btf__add_func_proto()succeeded. * *Returns: *->0,typeIDofnewlyaddedBTFtype; *-<0,onerror.
*/ int btf__add_func_proto(struct btf *btf, int ret_type_id)
{ struct btf_type *t; int sz;
if (validate_type_id(ret_type_id)) return libbpf_err(-EINVAL);
if (btf_ensure_modifiable(btf)) return libbpf_err(-ENOMEM);
sz = sizeof(struct btf_type);
t = btf_add_type_mem(btf, sz); if (!t) return libbpf_err(-ENOMEM);
/* start out with vlen=0; this will be adjusted when adding enum *values,ifnecessary
*/
t->name_off = 0;
t->info = btf_type_info(BTF_KIND_FUNC_PROTO, 0, 0);
t->type = ret_type_id;
return btf_commit_type(btf, sz);
}
/* *AppendnewfunctionparameterforcurrentFUNC_PROTOtypewith: *-*name*-parametername,canbeNULLorempty; *-*type_id*-typeIDdescribingthetypeoftheparameter. *Returns: *-0,onsuccess; *-<0,onerror.
*/ int btf__add_func_param(struct btf *btf, constchar *name, int type_id)
{ struct btf_type *t; struct btf_param *p; int sz, name_off = 0;
if (validate_type_id(type_id)) return libbpf_err(-EINVAL);
/* last type should be BTF_KIND_FUNC_PROTO */ if (btf->nr_types == 0) return libbpf_err(-EINVAL);
t = btf_last_type(btf); if (!btf_is_func_proto(t)) return libbpf_err(-EINVAL);
/* decompose and invalidate raw data */ if (btf_ensure_modifiable(btf)) return libbpf_err(-ENOMEM);
sz = sizeof(struct btf_param);
p = btf_add_type_mem(btf, sz); if (!p) return libbpf_err(-ENOMEM);
if (name && name[0]) {
name_off = btf__add_str(btf, name); if (name_off < 0) return name_off;
}
p->name_off = name_off;
p->type = type_id;
/* update parent type's vlen */
t = btf_last_type(btf);
btf_type_inc_vlen(t);
/* non-empty name */ if (!name || !name[0]) return libbpf_err(-EINVAL);
if (btf_ensure_modifiable(btf)) return libbpf_err(-ENOMEM);
sz = sizeof(struct btf_type);
t = btf_add_type_mem(btf, sz); if (!t) return libbpf_err(-ENOMEM);
name_off = btf__add_str(btf, name); if (name_off < 0) return name_off;
/* start with vlen=0, which will be update as var_secinfos are added */
t->name_off = name_off;
t->info = btf_type_info(BTF_KIND_DATASEC, 0, 0);
t->size = byte_sz;
return btf_commit_type(btf, sz);
}
/* *AppendnewdatasectionvariableinformationentryforcurrentDATASECtype: *-*var_type_id*-typeID,describingtypeofthevariable; *-*offset*-variableoffsetwithindatasection,inbytes; *-*byte_sz*-variablesize,inbytes. * *Returns: *-0,onsuccess; *-<0,onerror.
*/ int btf__add_datasec_var_info(struct btf *btf, int var_type_id, __u32 offset, __u32 byte_sz)
{ struct btf_type *t; struct btf_var_secinfo *v; int sz;
/* last type should be BTF_KIND_DATASEC */ if (btf->nr_types == 0) return libbpf_err(-EINVAL);
t = btf_last_type(btf); if (!btf_is_datasec(t)) return libbpf_err(-EINVAL);
if (validate_type_id(var_type_id)) return libbpf_err(-EINVAL);
/* decompose and invalidate raw data */ if (btf_ensure_modifiable(btf)) return libbpf_err(-ENOMEM);
sz = sizeof(struct btf_var_secinfo);
v = btf_add_type_mem(btf, sz); if (!v) return libbpf_err(-ENOMEM);
if (ext_sec->off & 0x03) {
pr_debug(".BTF.ext %s section is not aligned to 4 bytes\n",
ext_sec->desc); return -EINVAL;
}
/* The start of the info sec (including the __u32 record_size). */
info = btf_ext->data + btf_ext->hdr->hdr_len + ext_sec->off;
info_left = ext_sec->len;
if (btf_ext->data + btf_ext->data_size < info + ext_sec->len) {
pr_debug("%s section (off:%u len:%u) is beyond the end of the ELF section .BTF.ext\n",
ext_sec->desc, ext_sec->off, ext_sec->len); return -EINVAL;
}
/* At least a record size */ if (info_left < sizeof(__u32)) {
pr_debug(".BTF.ext %s record size not found\n", ext_sec->desc); return -EINVAL;
}
/* The record size needs to meet either the minimum standard or, when *handlingnon-nativeendiannessdata,theexactstandardsoas *toallowsafebyte-swapping.
*/
record_size = is_native ? *(__u32 *)info : bswap_32(*(__u32 *)info); if (record_size < ext_sec->min_rec_size ||
(!is_native && record_size != ext_sec->min_rec_size) ||
record_size & 0x03) {
pr_debug("%s section in .BTF.ext has invalid record size %u\n",
ext_sec->desc, record_size); return -EINVAL;
}
sinfo = info + sizeof(__u32);
info_left -= sizeof(__u32);
/* If no records, return failure now so .BTF.ext won't be used. */ if (!info_left) {
pr_debug("%s section in .BTF.ext has no records\n", ext_sec->desc); return -EINVAL;
}
/* Ensure known version of structs, current BTF_VERSION == 1 */ if (hdr->version != 1) {
pr_debug("Unsupported BTF.ext version:%u\n", hdr->version); return -ENOTSUP;
}
if (hdr->flags) {
pr_debug("Unsupported BTF.ext flags:%x\n", hdr->flags); return -ENOTSUP;
}
if (data_size < hdr_len) {
pr_debug("BTF.ext header not found\n"); return -EINVAL;
} elseif (data_size == hdr_len) {
pr_debug("BTF.ext has no data\n"); return -EINVAL;
}
/* Return native data (always present) or swapped data if present */ if (!swap_endian) return btf_ext->data; elseif (btf_ext->data_swapped) return btf_ext->data_swapped;
/* Recreate missing swapped data, then cache and return */
data = calloc(1, data_sz); if (!data) return NULL;
memcpy(data, btf_ext->data, data_sz);
type_cnt = btf__type_cnt(btf);
d->map = malloc(sizeof(__u32) * type_cnt); if (!d->map) {
err = -ENOMEM; goto done;
} /* special BTF "void" type is made canonical immediately */
d->map[0] = 0; 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))
d->map[i] = i; else
d->map[i] = BTF_UNPROCESSED_ID;
}
d->hypot_map = malloc(sizeof(__u32) * type_cnt); if (!d->hypot_map) {
err = -ENOMEM; goto done;
} for (i = 0; i < type_cnt; i++)
d->hypot_map[i] = BTF_UNPROCESSED_ID;
done: if (err) {
btf_dedup_free(d); return ERR_PTR(err);
}
return d;
}
/* *Iterateoverallpossibleplacesin.BTFand.BTF.extthatcanreference *stringandpasspointertoittoaprovidedcallback`fn`.
*/ staticint btf_for_each_str_off(struct btf_dedup *d, str_off_visit_fn fn, void *ctx)
{ int i, r;
for (i = 0; i < d->btf->nr_types; i++) { struct btf_field_iter it; struct btf_type *t = btf_type_by_id(d->btf, d->btf->start_id + i);
__u32 *str_off;
r = btf_field_iter_init(&it, t, BTF_FIELD_ITER_STRS); if (r) return r;
while ((str_off = btf_field_iter_next(&it))) {
r = fn(str_off, ctx); if (r) return r;
}
}
if (!d->btf_ext) return0;
r = btf_ext_visit_str_offs(d->btf_ext, fn, ctx); if (r) return r;
if (!d->btf->base_btf) { /* insert empty string; we won't be looking it up during strings *dedup,butit'sgoodtohaveitforgenericBTFstringlookups
*/
err = strset__add_str(d->strs_set, ""); if (err < 0) goto err_out;
}
staticbool btf_compat_enum(struct btf_type *t1, struct btf_type *t2)
{ if (!btf_is_enum_fwd(t1) && !btf_is_enum_fwd(t2)) return btf_equal_enum(t1, t2); /* At this point either t1 or t2 or both are forward declarations, thus: *-skipcomparingvlenbecauseitiszeroforforwarddeclarations; *-skipcomparingsizetoallowenumforwarddeclarations *tobecompatiblewithenum64fulldeclarations; *-skipcomparingkindforthesamereason.
*/ return t1->name_off == t2->name_off &&
btf_is_any_enum(t1) && btf_is_any_enum(t2);
}
/* *CalculatetypesignaturehashofSTRUCT/UNION,ignoringreferencedtypeIDs, *asreferencedtypeIDsequivalenceisestablishedseparatelyduringtype *graphequivalencecheckalgorithm.
*/ staticlong btf_hash_struct(struct btf_type *t)
{ conststruct btf_member *member = btf_members(t);
__u32 vlen = btf_vlen(t); long h = btf_hash_common(t); int i;
for (i = 0; i < vlen; i++) {
h = hash_combine(h, member->name_off);
h = hash_combine(h, member->offset); /* no hashing of referenced type ID, it can be unresolved yet */
member++;
} return h;
}
/* skip return type ID */ if (t1->name_off != t2->name_off || t1->info != t2->info) returnfalse;
vlen = btf_vlen(t1);
m1 = btf_params(t1);
m2 = btf_params(t2); for (i = 0; i < vlen; i++) { if (m1->name_off != m2->name_off) returnfalse;
m1++;
m2++;
} returntrue;
}
/* Prepare split BTF for deduplication by calculating hashes of base BTF's *typesandinitializingtherestofthestate(canonicaltypemapping)for *thefixedbaseBTFpart.
*/ staticint btf_dedup_prep(struct btf_dedup *d)
{ struct btf_type *t; int type_id; long h;
if (!d->btf->base_btf) return0;
for (type_id = 1; type_id < d->btf->start_id; type_id++) {
t = btf_type_by_id(d->btf, type_id);
/* all base BTF types are self-canonical by definition */
d->map[type_id] = type_id;
switch (btf_kind(t)) { case BTF_KIND_VAR: case BTF_KIND_DATASEC: /* VAR and DATASEC are never hash/deduplicated */ continue; case BTF_KIND_CONST: case BTF_KIND_VOLATILE: case BTF_KIND_RESTRICT: case BTF_KIND_PTR: case BTF_KIND_FWD: case BTF_KIND_TYPEDEF: case BTF_KIND_FUNC: case BTF_KIND_FLOAT: case BTF_KIND_TYPE_TAG:
h = btf_hash_common(t); break; case BTF_KIND_INT: case BTF_KIND_DECL_TAG:
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: case BTF_KIND_UNION:
h = btf_hash_struct(t); break; case BTF_KIND_ARRAY:
h = btf_hash_array(t); break; case BTF_KIND_FUNC_PROTO:
h = btf_hash_fnproto(t); break; default:
pr_debug("unknown kind %d for type [%d]\n", btf_kind(t), type_id); return -EINVAL;
} if (btf_dedup_table_add(d, h, type_id)) return -ENOMEM;
}
return0;
}
/* *Deduplicateprimitivetypes,thatcan'treferenceothertypes,bycalculating *theirtypesignaturehashandcomparingthemwithanypossiblecanonical *candidate.Ifnocanonicalcandidatematches,typeitselfismarkedas *canonicalandisaddedinto`btf_dedup->dedup_table`asanothercandidate.
*/ staticint btf_dedup_prim_type(struct btf_dedup *d, __u32 type_id)
{ struct btf_type *t = btf_type_by_id(d->btf, type_id); struct hashmap_entry *hash_entry; struct btf_type *cand; /* if we don't find equivalent type, then we are canonical */
__u32 new_id = type_id;
__u32 cand_id; long h;
switch (btf_kind(t)) { case BTF_KIND_CONST: case BTF_KIND_VOLATILE: case BTF_KIND_RESTRICT: case BTF_KIND_PTR: case BTF_KIND_TYPEDEF: case BTF_KIND_ARRAY: case BTF_KIND_STRUCT: case BTF_KIND_UNION: case BTF_KIND_FUNC: case BTF_KIND_FUNC_PROTO: case BTF_KIND_VAR: case BTF_KIND_DATASEC: case BTF_KIND_DECL_TAG: case BTF_KIND_TYPE_TAG: return0;
case BTF_KIND_INT:
h = btf_hash_int_decl_tag(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_int_tag(t, cand)) {
new_id = cand_id; break;
}
} break;
case BTF_KIND_ENUM: case BTF_KIND_ENUM64:
h = btf_hash_enum(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_enum(t, cand)) {
new_id = cand_id; break;
} if (btf_compat_enum(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);
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)) {
new_id = cand_id; break;
}
} break;
switch (k1) { case BTF_KIND_UNKN: /* VOID */ returntrue; case BTF_KIND_INT: return btf_equal_int_tag(t1, t2); case BTF_KIND_ENUM: case BTF_KIND_ENUM64: return btf_compat_enum(t1, t2); case BTF_KIND_FWD: case BTF_KIND_FLOAT: return btf_equal_common(t1, t2); case BTF_KIND_CONST: case BTF_KIND_VOLATILE: case BTF_KIND_RESTRICT: case BTF_KIND_PTR: case BTF_KIND_TYPEDEF: case BTF_KIND_FUNC: case BTF_KIND_TYPE_TAG: if (t1->info != t2->info || t1->name_off != t2->name_off) returnfalse;
id1 = t1->type;
id2 = t2->type; goto recur; case BTF_KIND_ARRAY: { struct btf_array *a1, *a2;
if (cand_kind == BTF_KIND_FWD) {
real_kind = canon_kind;
fwd_kind = btf_fwd_kind(cand_type);
} else {
real_kind = cand_kind;
fwd_kind = btf_fwd_kind(canon_type); /* we'd need to resolve base FWD to STRUCT/UNION */ if (fwd_kind == real_kind && canon_id < d->btf->start_id)
d->hypot_adjust_canon = true;
} return fwd_kind == real_kind;
}
if (cand_kind != canon_kind) return0;
switch (cand_kind) { case BTF_KIND_INT: return btf_equal_int_tag(cand_type, canon_type);
case BTF_KIND_ENUM: case BTF_KIND_ENUM64: return btf_compat_enum(cand_type, canon_type);
case BTF_KIND_FWD: case BTF_KIND_FLOAT: return btf_equal_common(cand_type, canon_type);
case BTF_KIND_CONST: case BTF_KIND_VOLATILE: case BTF_KIND_RESTRICT: case BTF_KIND_PTR: case BTF_KIND_TYPEDEF: case BTF_KIND_FUNC: case BTF_KIND_TYPE_TAG: if (cand_type->info != canon_type->info) return0; return btf_dedup_is_equiv(d, cand_type->type, canon_type->type);
case BTF_KIND_ARRAY: { conststruct btf_array *cand_arr, *canon_arr;
/* if it's the split BTF case, we still need to point base FWD *toSTRUCT/UNIONinasplitBTF,becauseFWDsfromsplitBTF *willberesolvedagainstbaseFWD.Ifwedon'tpointbase *canonicalFWDtotheresolvedSTRUCT/UNION,thenallthe *FWDsinsplitBTFwon'tbecorrectlyresolvedtoaproper *STRUCT/UNION.
*/ if (t_kind != BTF_KIND_FWD && c_kind == BTF_KIND_FWD)
d->map[c_id] = t_id;
/* if graph equivalence determined that we'd need to adjust *basecanonicaltypes,thenweneedtoonlypointbaseFWDs *toSTRUCTs/UNIONsanddonomoremodifications.Forall *otherpurposesthetypegraphswerenotequivalent.
*/ if (d->hypot_adjust_canon) continue;
staticint btf_dedup_struct_types(struct btf_dedup *d)
{ int i, err;
for (i = 0; i < d->btf->nr_types; i++) {
err = btf_dedup_struct_type(d, d->btf->start_id + i); if (err) return err;
} return0;
}
/* *Deduplicatereferencetype. * *Onceallprimitiveandstruct/uniontypesgotdeduplicated,wecaneasily *deduplicateallother(reference)BTFtypes.Thisisdoneintwosteps: * *1.ResolveallreferencedtypeIDsintotheircanonicaltypeIDs.This *resolutioncanbedoneeitherimmediatelyforprimitiveorstruct/uniontypes *(becausetheywerededupedinprevioustwophases)orrecursivelyfor *referencetypes.Recursionwillalwaysterminateateitherprimitiveor *struct/uniontype,atwhichpointwecan"unwind"chainofreferencetypes *onebyone.ThereisnodangerofencounteringcyclesbecauseinCtype *systemtheonlywaytoformtypecycleisthroughstruct/union,soanychain *ofreferencetypes,eventhosetakingpartinatypecycle,willinevitably *reachstruct/unionatsomepoint. * *2.OnceallreferencedtypeIDsareresolvedintocanonicalones,BTFtype *becomes"stable",inthesensethatnofurtherdeduplicationwillcause *anychangestoit.Withthat,it'snowpossibletocalculatetype'ssignature *hash(thistimetakingintoaccountreferencedtypeIDs)andloopoverall *potentialcanonicalrepresentatives.Ifnomatchwasfound,currenttype *willbecomecanonicalrepresentativeofitselfandwillbeaddedinto *btf_dedup->dedup_tableasanotherpossiblecanonicalrepresentative.
*/ staticint btf_dedup_ref_type(struct btf_dedup *d, __u32 type_id)
{ struct hashmap_entry *hash_entry;
__u32 new_id = type_id, cand_id; struct btf_type *t, *cand; /* if we don't find equivalent type, then we are representative type */ int ref_type_id; long h;
if (d->map[type_id] == BTF_IN_PROGRESS_ID) return -ELOOP; if (d->map[type_id] <= BTF_MAX_NR_TYPES) return resolve_type_id(d, type_id);
t = btf_type_by_id(d->btf, type_id);
d->map[type_id] = BTF_IN_PROGRESS_ID;
switch (btf_kind(t)) { case BTF_KIND_CONST: case BTF_KIND_VOLATILE: case BTF_KIND_RESTRICT: case BTF_KIND_PTR: case BTF_KIND_TYPEDEF: case BTF_KIND_FUNC: case BTF_KIND_TYPE_TAG:
ref_type_id = btf_dedup_ref_type(d, t->type); if (ref_type_id < 0) return ref_type_id;
t->type = ref_type_id;
/* we are going to reuse hypot_map to store compaction remapping */
d->hypot_map[0] = 0; /* base BTF types are not renumbered */ for (id = 1; id < d->btf->start_id; id++)
d->hypot_map[id] = id; for (i = 0, id = d->btf->start_id; i < d->btf->nr_types; i++, id++)
d->hypot_map[id] = BTF_UNPROCESSED_ID;
p = d->btf->types_data;
for (i = 0, id = d->btf->start_id; i < d->btf->nr_types; i++, id++) { if (d->map[id] != id) continue;
t = btf__type_by_id(d->btf, id);
len = btf_type_size(t); if (len < 0) return len;
memmove(p, t, len);
d->hypot_map[id] = next_type_id;
d->btf->type_offs[next_type_id - d->btf->start_id] = p - d->btf->types_data;
p += len;
next_type_id++;
}
err = btf_field_iter_init(&it, split_t, BTF_FIELD_ITER_IDS); if (err) return err; while ((id = btf_field_iter_next(&it))) { struct btf_type *base_t;
if (!*id) continue; /* split BTF id, not needed */ if (*id >= dist->split_start_id) continue; /* already added ? */ if (dist->id_map[*id] > 0) continue;
/* only a subset of base BTF types should be referenced from *splitBTF;ensurenothingunexpectedisreferenced.
*/
base_t = btf_type_by_id(dist->pipe.src, *id); switch (btf_kind(base_t)) { case BTF_KIND_INT: case BTF_KIND_FLOAT: case BTF_KIND_FWD: case BTF_KIND_ARRAY: case BTF_KIND_STRUCT: case BTF_KIND_UNION: case BTF_KIND_TYPEDEF: case BTF_KIND_ENUM: case BTF_KIND_ENUM64: case BTF_KIND_PTR: case BTF_KIND_CONST: case BTF_KIND_RESTRICT: case BTF_KIND_VOLATILE: case BTF_KIND_FUNC_PROTO: case BTF_KIND_TYPE_TAG:
dist->id_map[*id] = *id; break; default:
pr_warn("unexpected reference to base type[%u] of kind [%u] when creating distilled base BTF.\n",
*id, btf_kind(base_t)); return -EINVAL;
} /* If a base type is used, ensure types it refers to are *markedasusedalso;soforexampleifwefindaPTRtoINT *weneedboththePTRandINT. * *Theonlyexceptionisnamedstruct/unions,sincedistilled *baseBTFcompositetypeshavenomembers.
*/ if (btf_is_composite(base_t) && base_t->name_off) continue;
err = btf_add_distilled_type_ids(dist, *id); if (err) return err;
} return0;
}
staticint btf_add_distilled_types(struct btf_distill *dist)
{ bool adding_to_base = dist->pipe.dst->start_id == 1; int id = btf__type_cnt(dist->pipe.dst); struct btf_type *t; int i, err = 0;
/* Add types for each of the required references to either distilled *baseorsplitBTF,dependingontypecharacteristics.
*/ for (i = 1; i < dist->split_start_id; i++) { constchar *name; int kind;
if (!dist->id_map[i]) continue;
t = btf_type_by_id(dist->pipe.src, i);
kind = btf_kind(t);
name = btf__name_by_offset(dist->pipe.src, t->name_off);
switch (kind) { case BTF_KIND_INT: case BTF_KIND_FLOAT: case BTF_KIND_FWD: /* Named int, float, fwd are added to base. */ if (!adding_to_base) continue;
err = btf_add_type(&dist->pipe, t); break; case BTF_KIND_STRUCT: case BTF_KIND_UNION: /* Named struct/union are added to base as 0-vlen *struct/unionofsamesize.Anonymousstruct/unions *areaddedtosplitBTFas-is.
*/ if (adding_to_base) { if (!t->name_off) continue;
err = btf_add_composite(dist->pipe.dst, kind, name, t->size);
} else { if (t->name_off) continue;
err = btf_add_type(&dist->pipe, t);
} break; case BTF_KIND_ENUM: case BTF_KIND_ENUM64: /* Named enum[64]s are added to base as a sized *enum;relocationwillmatchwithappropriately-named *andsizedenumorenum64. * *AnonymousenumsareaddedtosplitBTFas-is.
*/ if (adding_to_base) { if (!t->name_off) continue;
err = btf__add_enum(dist->pipe.dst, name, t->size);
} else { if (t->name_off) continue;
err = btf_add_type(&dist->pipe, t);
} break; case BTF_KIND_ARRAY: case BTF_KIND_TYPEDEF: case BTF_KIND_PTR: case BTF_KIND_CONST: case BTF_KIND_RESTRICT: case BTF_KIND_VOLATILE: case BTF_KIND_FUNC_PROTO: case BTF_KIND_TYPE_TAG: /* All other types are added to split BTF. */ if (adding_to_base) continue;
err = btf_add_type(&dist->pipe, t); break; default:
pr_warn("unexpected kind when adding base type '%s'[%u] of kind [%u] to distilled base BTF.\n",
name, i, kind); return -EINVAL;
/* Pass over src split BTF; generate the list of base BTF type ids it *references;thesewillconstituteourdistilledBTFsettobe *distributedoverbaseandsplitBTFasappropriate.
*/ for (i = src_btf->start_id; i < n; i++) {
err = btf_add_distilled_type_ids(&dist, i); if (err < 0) goto done;
} /* Next add types for each of the required references to base BTF and split BTF *inturn.
*/
err = btf_add_distilled_types(&dist); if (err < 0) goto done;
/* Create new split BTF with distilled base BTF as its base; the final *stateissplitBTFwithdistilledbaseBTFthatrepresentsenough *aboutitsbasereferencestoallowittoberelocatedwiththebase *BTFavailable.
*/
new_split = btf__new_empty_split(new_base); if (!new_split) {
err = -errno; goto done;
}
dist.pipe.dst = new_split; /* First add all split types */ for (i = src_btf->start_id; i < n; i++) {
t = btf_type_by_id(src_btf, i);
err = btf_add_type(&dist.pipe, t); if (err < 0) goto done;
} /* Now add distilled types to split BTF that are not added to base. */
err = btf_add_distilled_types(&dist); if (err < 0) goto done;
/* All split BTF ids will be shifted downwards since there are less base *BTFidsindistilledbaseBTF.
*/
dist.diff_id = dist.split_start_id - btf__type_cnt(new_base);
n = btf__type_cnt(new_split); /* Now update base/split BTF ids. */ for (i = 1; i < n; i++) {
err = btf_update_distilled_type_ids(&dist, i); if (err < 0) break;
}
done:
free(dist.id_map);
hashmap__free(dist.pipe.str_off_map); if (err) {
btf__free(new_split);
btf__free(new_base); return libbpf_err(err);
}
*new_base_btf = new_base;
*new_split_btf = new_split;
int btf__relocate(struct btf *btf, conststruct btf *base_btf)
{ int err = btf_relocate(btf, base_btf, NULL);
if (!err)
btf->owns_base = false; return libbpf_err(err);
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.381Bemerkung:
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.