| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471 |
- // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
- /* Copyright (C) 2019 Facebook */
- #ifndef _GNU_SOURCE
- #define _GNU_SOURCE
- #endif
- #include <errno.h>
- #include <fcntl.h>
- #include <linux/err.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- #include <linux/btf.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <bpf/bpf.h>
- #include <bpf/btf.h>
- #include <bpf/hashmap.h>
- #include <bpf/libbpf.h>
- #include "json_writer.h"
- #include "main.h"
- #define KFUNC_DECL_TAG "bpf_kfunc"
- #define FASTCALL_DECL_TAG "bpf_fastcall"
- #define MAX_ROOT_IDS 16
- static const char * const btf_kind_str[NR_BTF_KINDS] = {
- [BTF_KIND_UNKN] = "UNKNOWN",
- [BTF_KIND_INT] = "INT",
- [BTF_KIND_PTR] = "PTR",
- [BTF_KIND_ARRAY] = "ARRAY",
- [BTF_KIND_STRUCT] = "STRUCT",
- [BTF_KIND_UNION] = "UNION",
- [BTF_KIND_ENUM] = "ENUM",
- [BTF_KIND_FWD] = "FWD",
- [BTF_KIND_TYPEDEF] = "TYPEDEF",
- [BTF_KIND_VOLATILE] = "VOLATILE",
- [BTF_KIND_CONST] = "CONST",
- [BTF_KIND_RESTRICT] = "RESTRICT",
- [BTF_KIND_FUNC] = "FUNC",
- [BTF_KIND_FUNC_PROTO] = "FUNC_PROTO",
- [BTF_KIND_VAR] = "VAR",
- [BTF_KIND_DATASEC] = "DATASEC",
- [BTF_KIND_FLOAT] = "FLOAT",
- [BTF_KIND_DECL_TAG] = "DECL_TAG",
- [BTF_KIND_TYPE_TAG] = "TYPE_TAG",
- [BTF_KIND_ENUM64] = "ENUM64",
- };
- struct sort_datum {
- int index;
- int type_rank;
- const char *sort_name;
- const char *own_name;
- __u64 disambig_hash;
- };
- static const char *btf_int_enc_str(__u8 encoding)
- {
- switch (encoding) {
- case 0:
- return "(none)";
- case BTF_INT_SIGNED:
- return "SIGNED";
- case BTF_INT_CHAR:
- return "CHAR";
- case BTF_INT_BOOL:
- return "BOOL";
- default:
- return "UNKN";
- }
- }
- static const char *btf_var_linkage_str(__u32 linkage)
- {
- switch (linkage) {
- case BTF_VAR_STATIC:
- return "static";
- case BTF_VAR_GLOBAL_ALLOCATED:
- return "global";
- case BTF_VAR_GLOBAL_EXTERN:
- return "extern";
- default:
- return "(unknown)";
- }
- }
- static const char *btf_func_linkage_str(const struct btf_type *t)
- {
- switch (btf_vlen(t)) {
- case BTF_FUNC_STATIC:
- return "static";
- case BTF_FUNC_GLOBAL:
- return "global";
- case BTF_FUNC_EXTERN:
- return "extern";
- default:
- return "(unknown)";
- }
- }
- static const char *btf_str(const struct btf *btf, __u32 off)
- {
- if (!off)
- return "(anon)";
- return btf__name_by_offset(btf, off) ? : "(invalid)";
- }
- static int btf_kind_safe(int kind)
- {
- return kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
- }
- static int dump_btf_type(const struct btf *btf, __u32 id,
- const struct btf_type *t)
- {
- json_writer_t *w = json_wtr;
- int kind = btf_kind(t);
- if (json_output) {
- jsonw_start_object(w);
- jsonw_uint_field(w, "id", id);
- jsonw_string_field(w, "kind", btf_kind_str[btf_kind_safe(kind)]);
- jsonw_string_field(w, "name", btf_str(btf, t->name_off));
- } else {
- printf("[%u] %s '%s'", id, btf_kind_str[btf_kind_safe(kind)],
- btf_str(btf, t->name_off));
- }
- switch (kind) {
- case BTF_KIND_INT: {
- __u32 v = *(__u32 *)(t + 1);
- const char *enc;
- enc = btf_int_enc_str(BTF_INT_ENCODING(v));
- if (json_output) {
- jsonw_uint_field(w, "size", t->size);
- jsonw_uint_field(w, "bits_offset", BTF_INT_OFFSET(v));
- jsonw_uint_field(w, "nr_bits", BTF_INT_BITS(v));
- jsonw_string_field(w, "encoding", enc);
- } else {
- printf(" size=%u bits_offset=%u nr_bits=%u encoding=%s",
- t->size, BTF_INT_OFFSET(v), BTF_INT_BITS(v),
- enc);
- }
- break;
- }
- case BTF_KIND_PTR:
- case BTF_KIND_CONST:
- case BTF_KIND_VOLATILE:
- case BTF_KIND_RESTRICT:
- case BTF_KIND_TYPEDEF:
- case BTF_KIND_TYPE_TAG:
- if (json_output)
- jsonw_uint_field(w, "type_id", t->type);
- else
- printf(" type_id=%u", t->type);
- break;
- case BTF_KIND_ARRAY: {
- const struct btf_array *arr = (const void *)(t + 1);
- if (json_output) {
- jsonw_uint_field(w, "type_id", arr->type);
- jsonw_uint_field(w, "index_type_id", arr->index_type);
- jsonw_uint_field(w, "nr_elems", arr->nelems);
- } else {
- printf(" type_id=%u index_type_id=%u nr_elems=%u",
- arr->type, arr->index_type, arr->nelems);
- }
- break;
- }
- case BTF_KIND_STRUCT:
- case BTF_KIND_UNION: {
- const struct btf_member *m = (const void *)(t + 1);
- __u16 vlen = BTF_INFO_VLEN(t->info);
- int i;
- if (json_output) {
- jsonw_uint_field(w, "size", t->size);
- jsonw_uint_field(w, "vlen", vlen);
- jsonw_name(w, "members");
- jsonw_start_array(w);
- } else {
- printf(" size=%u vlen=%u", t->size, vlen);
- }
- for (i = 0; i < vlen; i++, m++) {
- const char *name = btf_str(btf, m->name_off);
- __u32 bit_off, bit_sz;
- if (BTF_INFO_KFLAG(t->info)) {
- bit_off = BTF_MEMBER_BIT_OFFSET(m->offset);
- bit_sz = BTF_MEMBER_BITFIELD_SIZE(m->offset);
- } else {
- bit_off = m->offset;
- bit_sz = 0;
- }
- if (json_output) {
- jsonw_start_object(w);
- jsonw_string_field(w, "name", name);
- jsonw_uint_field(w, "type_id", m->type);
- jsonw_uint_field(w, "bits_offset", bit_off);
- if (bit_sz) {
- jsonw_uint_field(w, "bitfield_size",
- bit_sz);
- }
- jsonw_end_object(w);
- } else {
- printf("\n\t'%s' type_id=%u bits_offset=%u",
- name, m->type, bit_off);
- if (bit_sz)
- printf(" bitfield_size=%u", bit_sz);
- }
- }
- if (json_output)
- jsonw_end_array(w);
- break;
- }
- 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(" 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);
- if (json_output) {
- jsonw_start_object(w);
- jsonw_string_field(w, "name", name);
- if (btf_kflag(t))
- jsonw_int_field(w, "val", v->val);
- else
- jsonw_uint_field(w, "val", v->val);
- jsonw_end_object(w);
- } else {
- if (btf_kflag(t))
- printf("\n\t'%s' val=%d", name, v->val);
- else
- printf("\n\t'%s' val=%u", name, (__u32)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,
- (long long)val);
- else
- printf("\n\t'%s' val=%lluULL", name,
- (unsigned long long)val);
- }
- }
- if (json_output)
- jsonw_end_array(w);
- break;
- }
- case BTF_KIND_FWD: {
- const char *fwd_kind = BTF_INFO_KFLAG(t->info) ? "union"
- : "struct";
- if (json_output)
- jsonw_string_field(w, "fwd_kind", fwd_kind);
- else
- printf(" fwd_kind=%s", fwd_kind);
- break;
- }
- case BTF_KIND_FUNC: {
- const char *linkage = btf_func_linkage_str(t);
- if (json_output) {
- jsonw_uint_field(w, "type_id", t->type);
- jsonw_string_field(w, "linkage", linkage);
- } else {
- printf(" type_id=%u linkage=%s", t->type, linkage);
- }
- break;
- }
- case BTF_KIND_FUNC_PROTO: {
- const struct btf_param *p = (const void *)(t + 1);
- __u16 vlen = BTF_INFO_VLEN(t->info);
- int i;
- if (json_output) {
- jsonw_uint_field(w, "ret_type_id", t->type);
- jsonw_uint_field(w, "vlen", vlen);
- jsonw_name(w, "params");
- jsonw_start_array(w);
- } else {
- printf(" ret_type_id=%u vlen=%u", t->type, vlen);
- }
- for (i = 0; i < vlen; i++, p++) {
- const char *name = btf_str(btf, p->name_off);
- if (json_output) {
- jsonw_start_object(w);
- jsonw_string_field(w, "name", name);
- jsonw_uint_field(w, "type_id", p->type);
- jsonw_end_object(w);
- } else {
- printf("\n\t'%s' type_id=%u", name, p->type);
- }
- }
- if (json_output)
- jsonw_end_array(w);
- break;
- }
- case BTF_KIND_VAR: {
- const struct btf_var *v = (const void *)(t + 1);
- const char *linkage;
- linkage = btf_var_linkage_str(v->linkage);
- if (json_output) {
- jsonw_uint_field(w, "type_id", t->type);
- jsonw_string_field(w, "linkage", linkage);
- } else {
- printf(" type_id=%u, linkage=%s", t->type, linkage);
- }
- break;
- }
- case BTF_KIND_DATASEC: {
- const struct btf_var_secinfo *v = (const void *)(t + 1);
- const struct btf_type *vt;
- __u16 vlen = BTF_INFO_VLEN(t->info);
- int i;
- if (json_output) {
- jsonw_uint_field(w, "size", t->size);
- jsonw_uint_field(w, "vlen", vlen);
- jsonw_name(w, "vars");
- jsonw_start_array(w);
- } else {
- printf(" size=%u vlen=%u", t->size, vlen);
- }
- for (i = 0; i < vlen; i++, v++) {
- if (json_output) {
- jsonw_start_object(w);
- jsonw_uint_field(w, "type_id", v->type);
- jsonw_uint_field(w, "offset", v->offset);
- jsonw_uint_field(w, "size", v->size);
- jsonw_end_object(w);
- } else {
- printf("\n\ttype_id=%u offset=%u size=%u",
- v->type, v->offset, v->size);
- if (v->type < btf__type_cnt(btf)) {
- vt = btf__type_by_id(btf, v->type);
- printf(" (%s '%s')",
- btf_kind_str[btf_kind_safe(btf_kind(vt))],
- btf_str(btf, vt->name_off));
- }
- }
- }
- if (json_output)
- jsonw_end_array(w);
- break;
- }
- case BTF_KIND_FLOAT: {
- if (json_output)
- jsonw_uint_field(w, "size", t->size);
- else
- printf(" size=%u", t->size);
- break;
- }
- case BTF_KIND_DECL_TAG: {
- const struct btf_decl_tag *tag = (const void *)(t + 1);
- if (json_output) {
- jsonw_uint_field(w, "type_id", t->type);
- jsonw_int_field(w, "component_idx", tag->component_idx);
- } else {
- printf(" type_id=%u component_idx=%d", t->type, tag->component_idx);
- }
- break;
- }
- default:
- break;
- }
- if (json_output)
- jsonw_end_object(json_wtr);
- else
- printf("\n");
- return 0;
- }
- static int dump_btf_raw(const struct btf *btf,
- __u32 *root_type_ids, int root_type_cnt)
- {
- const struct btf_type *t;
- int i;
- if (json_output) {
- jsonw_start_object(json_wtr);
- jsonw_name(json_wtr, "types");
- jsonw_start_array(json_wtr);
- }
- if (root_type_cnt) {
- for (i = 0; i < root_type_cnt; i++) {
- t = btf__type_by_id(btf, root_type_ids[i]);
- dump_btf_type(btf, root_type_ids[i], t);
- }
- } else {
- const struct btf *base;
- int cnt = btf__type_cnt(btf);
- int start_id = 1;
- base = btf__base_btf(btf);
- if (base)
- start_id = btf__type_cnt(base);
- for (i = start_id; i < cnt; i++) {
- t = btf__type_by_id(btf, i);
- dump_btf_type(btf, i, t);
- }
- }
- if (json_output) {
- jsonw_end_array(json_wtr);
- jsonw_end_object(json_wtr);
- }
- return 0;
- }
- struct ptr_array {
- __u32 cnt;
- __u32 cap;
- const void **elems;
- };
- static int ptr_array_push(const void *ptr, struct ptr_array *arr)
- {
- __u32 new_cap;
- void *tmp;
- if (arr->cnt == arr->cap) {
- new_cap = (arr->cap ?: 16) * 2;
- tmp = realloc(arr->elems, sizeof(*arr->elems) * new_cap);
- if (!tmp)
- return -ENOMEM;
- arr->elems = tmp;
- arr->cap = new_cap;
- }
- arr->elems[arr->cnt++] = ptr;
- return 0;
- }
- static void ptr_array_free(struct ptr_array *arr)
- {
- free(arr->elems);
- }
- static int cmp_kfuncs(const void *pa, const void *pb, void *ctx)
- {
- struct btf *btf = ctx;
- const struct btf_type *a = *(void **)pa;
- const struct btf_type *b = *(void **)pb;
- return strcmp(btf__str_by_offset(btf, a->name_off),
- btf__str_by_offset(btf, b->name_off));
- }
- static int dump_btf_kfuncs(struct btf_dump *d, const struct btf *btf)
- {
- LIBBPF_OPTS(btf_dump_emit_type_decl_opts, opts);
- __u32 cnt = btf__type_cnt(btf), i, j;
- struct ptr_array fastcalls = {};
- struct ptr_array kfuncs = {};
- int err = 0;
- printf("\n/* BPF kfuncs */\n");
- printf("#ifndef BPF_NO_KFUNC_PROTOTYPES\n");
- for (i = 1; i < cnt; i++) {
- const struct btf_type *t = btf__type_by_id(btf, i);
- const struct btf_type *ft;
- const char *name;
- if (!btf_is_decl_tag(t))
- continue;
- if (btf_decl_tag(t)->component_idx != -1)
- continue;
- ft = btf__type_by_id(btf, t->type);
- if (!btf_is_func(ft))
- continue;
- name = btf__name_by_offset(btf, t->name_off);
- if (strncmp(name, KFUNC_DECL_TAG, sizeof(KFUNC_DECL_TAG)) == 0) {
- err = ptr_array_push(ft, &kfuncs);
- if (err)
- goto out;
- }
- if (strncmp(name, FASTCALL_DECL_TAG, sizeof(FASTCALL_DECL_TAG)) == 0) {
- err = ptr_array_push(ft, &fastcalls);
- if (err)
- goto out;
- }
- }
- /* Sort kfuncs by name for improved vmlinux.h stability */
- qsort_r(kfuncs.elems, kfuncs.cnt, sizeof(*kfuncs.elems), cmp_kfuncs, (void *)btf);
- for (i = 0; i < kfuncs.cnt; i++) {
- const struct btf_type *t = kfuncs.elems[i];
- printf("extern ");
- /* Assume small amount of fastcall kfuncs */
- for (j = 0; j < fastcalls.cnt; j++) {
- if (fastcalls.elems[j] == t) {
- printf("__bpf_fastcall ");
- break;
- }
- }
- opts.field_name = btf__name_by_offset(btf, t->name_off);
- err = btf_dump__emit_type_decl(d, t->type, &opts);
- if (err)
- goto out;
- printf(" __weak __ksym;\n");
- }
- printf("#endif\n\n");
- out:
- ptr_array_free(&fastcalls);
- ptr_array_free(&kfuncs);
- return err;
- }
- static void __printf(2, 0) btf_dump_printf(void *ctx,
- const char *fmt, va_list args)
- {
- vfprintf(stdout, fmt, args);
- }
- static int btf_type_rank(const struct btf *btf, __u32 index, bool has_name)
- {
- const struct btf_type *t = btf__type_by_id(btf, index);
- const int kind = btf_kind(t);
- const int max_rank = 10;
- if (t->name_off)
- has_name = true;
- switch (kind) {
- case BTF_KIND_ENUM:
- case BTF_KIND_ENUM64:
- return has_name ? 1 : 0;
- case BTF_KIND_INT:
- case BTF_KIND_FLOAT:
- return 2;
- case BTF_KIND_STRUCT:
- case BTF_KIND_UNION:
- return has_name ? 3 : max_rank;
- case BTF_KIND_FUNC_PROTO:
- return has_name ? 4 : max_rank;
- case BTF_KIND_ARRAY:
- if (has_name)
- return btf_type_rank(btf, btf_array(t)->type, has_name);
- return max_rank;
- case BTF_KIND_TYPE_TAG:
- case BTF_KIND_CONST:
- case BTF_KIND_PTR:
- case BTF_KIND_VOLATILE:
- case BTF_KIND_RESTRICT:
- case BTF_KIND_TYPEDEF:
- case BTF_KIND_DECL_TAG:
- if (has_name)
- return btf_type_rank(btf, t->type, has_name);
- return max_rank;
- default:
- return max_rank;
- }
- }
- static const char *btf_type_sort_name(const struct btf *btf, __u32 index, bool from_ref)
- {
- const struct btf_type *t = btf__type_by_id(btf, index);
- switch (btf_kind(t)) {
- case BTF_KIND_ENUM:
- case BTF_KIND_ENUM64: {
- int name_off = t->name_off;
- if (!from_ref && !name_off && btf_vlen(t))
- name_off = btf_kind(t) == BTF_KIND_ENUM64 ?
- btf_enum64(t)->name_off :
- btf_enum(t)->name_off;
- return btf__name_by_offset(btf, name_off);
- }
- case BTF_KIND_ARRAY:
- return btf_type_sort_name(btf, btf_array(t)->type, true);
- case BTF_KIND_TYPE_TAG:
- case BTF_KIND_CONST:
- case BTF_KIND_PTR:
- case BTF_KIND_VOLATILE:
- case BTF_KIND_RESTRICT:
- case BTF_KIND_TYPEDEF:
- case BTF_KIND_DECL_TAG:
- return btf_type_sort_name(btf, t->type, true);
- default:
- return btf__name_by_offset(btf, t->name_off);
- }
- return NULL;
- }
- static __u64 hasher(__u64 hash, __u64 val)
- {
- return hash * 31 + val;
- }
- static __u64 btf_name_hasher(__u64 hash, const struct btf *btf, __u32 name_off)
- {
- if (!name_off)
- return hash;
- return hasher(hash, str_hash(btf__name_by_offset(btf, name_off)));
- }
- static __u64 btf_type_disambig_hash(const struct btf *btf, __u32 id, bool include_members)
- {
- const struct btf_type *t = btf__type_by_id(btf, id);
- int i;
- size_t hash = 0;
- hash = btf_name_hasher(hash, btf, t->name_off);
- switch (btf_kind(t)) {
- case BTF_KIND_ENUM:
- case BTF_KIND_ENUM64:
- for (i = 0; i < btf_vlen(t); i++) {
- __u32 name_off = btf_is_enum(t) ?
- btf_enum(t)[i].name_off :
- btf_enum64(t)[i].name_off;
- hash = btf_name_hasher(hash, btf, name_off);
- }
- break;
- case BTF_KIND_STRUCT:
- case BTF_KIND_UNION:
- if (!include_members)
- break;
- for (i = 0; i < btf_vlen(t); i++) {
- const struct btf_member *m = btf_members(t) + i;
- hash = btf_name_hasher(hash, btf, m->name_off);
- /* resolve field type's name and hash it as well */
- hash = hasher(hash, btf_type_disambig_hash(btf, m->type, false));
- }
- break;
- case BTF_KIND_TYPE_TAG:
- case BTF_KIND_CONST:
- case BTF_KIND_PTR:
- case BTF_KIND_VOLATILE:
- case BTF_KIND_RESTRICT:
- case BTF_KIND_TYPEDEF:
- case BTF_KIND_DECL_TAG:
- hash = hasher(hash, btf_type_disambig_hash(btf, t->type, include_members));
- break;
- case BTF_KIND_ARRAY: {
- struct btf_array *arr = btf_array(t);
- hash = hasher(hash, arr->nelems);
- hash = hasher(hash, btf_type_disambig_hash(btf, arr->type, include_members));
- break;
- }
- default:
- break;
- }
- return hash;
- }
- static int btf_type_compare(const void *left, const void *right)
- {
- const struct sort_datum *d1 = (const struct sort_datum *)left;
- const struct sort_datum *d2 = (const struct sort_datum *)right;
- int r;
- r = d1->type_rank - d2->type_rank;
- r = r ?: strcmp(d1->sort_name, d2->sort_name);
- r = r ?: strcmp(d1->own_name, d2->own_name);
- if (r)
- return r;
- if (d1->disambig_hash != d2->disambig_hash)
- return d1->disambig_hash < d2->disambig_hash ? -1 : 1;
- return d1->index - d2->index;
- }
- static struct sort_datum *sort_btf_c(const struct btf *btf)
- {
- struct sort_datum *datums;
- int n;
- n = btf__type_cnt(btf);
- datums = malloc(sizeof(struct sort_datum) * n);
- if (!datums)
- return NULL;
- for (int i = 0; i < n; ++i) {
- struct sort_datum *d = datums + i;
- const struct btf_type *t = btf__type_by_id(btf, i);
- d->index = i;
- d->type_rank = btf_type_rank(btf, i, false);
- d->sort_name = btf_type_sort_name(btf, i, false);
- d->own_name = btf__name_by_offset(btf, t->name_off);
- d->disambig_hash = btf_type_disambig_hash(btf, i, true);
- }
- qsort(datums, n, sizeof(struct sort_datum), btf_type_compare);
- return datums;
- }
- static int dump_btf_c(const struct btf *btf,
- __u32 *root_type_ids, int root_type_cnt, bool sort_dump)
- {
- struct sort_datum *datums = NULL;
- struct btf_dump *d;
- int err = 0, i;
- d = btf_dump__new(btf, btf_dump_printf, NULL, NULL);
- if (!d)
- return -errno;
- printf("#ifndef __VMLINUX_H__\n");
- printf("#define __VMLINUX_H__\n");
- printf("\n");
- printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n");
- printf("#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)\n");
- printf("#endif\n\n");
- printf("#ifndef __ksym\n");
- printf("#define __ksym __attribute__((section(\".ksyms\")))\n");
- printf("#endif\n\n");
- printf("#ifndef __weak\n");
- printf("#define __weak __attribute__((weak))\n");
- printf("#endif\n\n");
- printf("#ifndef __bpf_fastcall\n");
- printf("#if __has_attribute(bpf_fastcall)\n");
- printf("#define __bpf_fastcall __attribute__((bpf_fastcall))\n");
- printf("#else\n");
- printf("#define __bpf_fastcall\n");
- printf("#endif\n");
- printf("#endif\n\n");
- if (root_type_cnt) {
- for (i = 0; i < root_type_cnt; i++) {
- err = btf_dump__dump_type(d, root_type_ids[i]);
- if (err)
- goto done;
- }
- } else {
- int cnt = btf__type_cnt(btf);
- if (sort_dump)
- datums = sort_btf_c(btf);
- for (i = 1; i < cnt; i++) {
- int idx = datums ? datums[i].index : i;
- err = btf_dump__dump_type(d, idx);
- if (err)
- goto done;
- }
- err = dump_btf_kfuncs(d, btf);
- if (err)
- goto done;
- }
- printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n");
- printf("#pragma clang attribute pop\n");
- printf("#endif\n");
- printf("\n");
- printf("#endif /* __VMLINUX_H__ */\n");
- done:
- free(datums);
- btf_dump__free(d);
- return err;
- }
- static const char sysfs_vmlinux[] = "/sys/kernel/btf/vmlinux";
- static struct btf *get_vmlinux_btf_from_sysfs(void)
- {
- struct btf *base;
- base = btf__parse(sysfs_vmlinux, NULL);
- if (!base)
- p_err("failed to parse vmlinux BTF at '%s': %d\n",
- sysfs_vmlinux, -errno);
- return base;
- }
- #define BTF_NAME_BUFF_LEN 64
- static bool btf_is_kernel_module(__u32 btf_id)
- {
- struct bpf_btf_info btf_info = {};
- char btf_name[BTF_NAME_BUFF_LEN];
- int btf_fd;
- __u32 len;
- int err;
- btf_fd = bpf_btf_get_fd_by_id(btf_id);
- if (btf_fd < 0) {
- p_err("can't get BTF object by id (%u): %s", btf_id, strerror(errno));
- return false;
- }
- len = sizeof(btf_info);
- btf_info.name = ptr_to_u64(btf_name);
- btf_info.name_len = sizeof(btf_name);
- err = bpf_btf_get_info_by_fd(btf_fd, &btf_info, &len);
- close(btf_fd);
- if (err) {
- p_err("can't get BTF (ID %u) object info: %s", btf_id, strerror(errno));
- return false;
- }
- return btf_info.kernel_btf && strncmp(btf_name, "vmlinux", sizeof(btf_name)) != 0;
- }
- static int do_dump(int argc, char **argv)
- {
- bool dump_c = false, sort_dump_c = true;
- struct btf *btf = NULL, *base = NULL;
- __u32 root_type_ids[MAX_ROOT_IDS];
- bool have_id_filtering;
- int root_type_cnt = 0;
- __u32 btf_id = -1;
- const char *src;
- int fd = -1;
- int err = 0;
- int i;
- if (!REQ_ARGS(2)) {
- usage();
- return -1;
- }
- src = GET_ARG();
- if (is_prefix(src, "map")) {
- struct bpf_map_info info = {};
- __u32 len = sizeof(info);
- if (!REQ_ARGS(2)) {
- usage();
- return -1;
- }
- fd = map_parse_fd_and_info(&argc, &argv, &info, &len,
- BPF_F_RDONLY);
- if (fd < 0)
- return -1;
- btf_id = info.btf_id;
- if (argc && is_prefix(*argv, "key")) {
- root_type_ids[root_type_cnt++] = info.btf_key_type_id;
- NEXT_ARG();
- } else if (argc && is_prefix(*argv, "value")) {
- root_type_ids[root_type_cnt++] = info.btf_value_type_id;
- NEXT_ARG();
- } else if (argc && is_prefix(*argv, "all")) {
- NEXT_ARG();
- } else if (argc && is_prefix(*argv, "kv")) {
- root_type_ids[root_type_cnt++] = info.btf_key_type_id;
- root_type_ids[root_type_cnt++] = info.btf_value_type_id;
- NEXT_ARG();
- } else {
- root_type_ids[root_type_cnt++] = info.btf_key_type_id;
- root_type_ids[root_type_cnt++] = info.btf_value_type_id;
- }
- } else if (is_prefix(src, "prog")) {
- struct bpf_prog_info info = {};
- __u32 len = sizeof(info);
- if (!REQ_ARGS(2)) {
- usage();
- return -1;
- }
- fd = prog_parse_fd(&argc, &argv);
- if (fd < 0)
- return -1;
- err = bpf_prog_get_info_by_fd(fd, &info, &len);
- if (err) {
- p_err("can't get prog info: %s", strerror(errno));
- goto done;
- }
- btf_id = info.btf_id;
- } else if (is_prefix(src, "id")) {
- char *endptr;
- btf_id = strtoul(*argv, &endptr, 0);
- if (*endptr) {
- p_err("can't parse %s as ID", *argv);
- return -1;
- }
- NEXT_ARG();
- } else if (is_prefix(src, "file")) {
- const char sysfs_prefix[] = "/sys/kernel/btf/";
- if (!base_btf &&
- strncmp(*argv, sysfs_prefix, sizeof(sysfs_prefix) - 1) == 0 &&
- strcmp(*argv, sysfs_vmlinux) != 0)
- base = get_vmlinux_btf_from_sysfs();
- btf = btf__parse_split(*argv, base ?: base_btf);
- if (!btf) {
- err = -errno;
- p_err("failed to load BTF from %s: %s",
- *argv, strerror(errno));
- goto done;
- }
- NEXT_ARG();
- } else {
- err = -1;
- p_err("unrecognized BTF source specifier: '%s'", src);
- goto done;
- }
- have_id_filtering = !!root_type_cnt;
- while (argc) {
- if (is_prefix(*argv, "format")) {
- NEXT_ARG();
- if (argc < 1) {
- p_err("expecting value for 'format' option\n");
- err = -EINVAL;
- goto done;
- }
- if (strcmp(*argv, "c") == 0) {
- dump_c = true;
- } else if (strcmp(*argv, "raw") == 0) {
- dump_c = false;
- } else {
- p_err("unrecognized format specifier: '%s', possible values: raw, c",
- *argv);
- err = -EINVAL;
- goto done;
- }
- NEXT_ARG();
- } else if (is_prefix(*argv, "root_id")) {
- __u32 root_id;
- char *end;
- if (have_id_filtering) {
- p_err("cannot use root_id with other type filtering");
- err = -EINVAL;
- goto done;
- } else if (root_type_cnt == MAX_ROOT_IDS) {
- p_err("only %d root_id are supported", MAX_ROOT_IDS);
- err = -E2BIG;
- goto done;
- }
- NEXT_ARG();
- root_id = strtoul(*argv, &end, 0);
- if (*end) {
- err = -1;
- p_err("can't parse %s as root ID", *argv);
- goto done;
- }
- for (i = 0; i < root_type_cnt; i++) {
- if (root_type_ids[i] == root_id) {
- err = -EINVAL;
- p_err("duplicate root_id %u supplied", root_id);
- goto done;
- }
- }
- root_type_ids[root_type_cnt++] = root_id;
- NEXT_ARG();
- } else if (is_prefix(*argv, "unsorted")) {
- sort_dump_c = false;
- NEXT_ARG();
- } else {
- p_err("unrecognized option: '%s'", *argv);
- err = -EINVAL;
- goto done;
- }
- }
- if (!btf) {
- if (!base_btf && btf_is_kernel_module(btf_id)) {
- p_info("Warning: valid base BTF was not specified with -B option, falling back to standard base BTF (%s)",
- sysfs_vmlinux);
- base_btf = get_vmlinux_btf_from_sysfs();
- }
- btf = btf__load_from_kernel_by_id_split(btf_id, base_btf);
- if (!btf) {
- err = -errno;
- p_err("get btf by id (%u): %s", btf_id, strerror(errno));
- goto done;
- }
- }
- /* Invalid root IDs causes half emitted boilerplate and then unclean
- * exit. It's an ugly user experience, so handle common error here.
- */
- for (i = 0; i < root_type_cnt; i++) {
- if (root_type_ids[i] >= btf__type_cnt(btf)) {
- err = -EINVAL;
- p_err("invalid root ID: %u", root_type_ids[i]);
- goto done;
- }
- }
- if (dump_c) {
- if (json_output) {
- p_err("JSON output for C-syntax dump is not supported");
- err = -ENOTSUP;
- goto done;
- }
- err = dump_btf_c(btf, root_type_ids, root_type_cnt, sort_dump_c);
- } else {
- err = dump_btf_raw(btf, root_type_ids, root_type_cnt);
- }
- done:
- close(fd);
- btf__free(btf);
- btf__free(base);
- return err;
- }
- static int btf_parse_fd(int *argc, char ***argv)
- {
- unsigned int id;
- char *endptr;
- int fd;
- if (!is_prefix(*argv[0], "id")) {
- p_err("expected 'id', got: '%s'?", **argv);
- return -1;
- }
- NEXT_ARGP();
- id = strtoul(**argv, &endptr, 0);
- if (*endptr) {
- p_err("can't parse %s as ID", **argv);
- return -1;
- }
- NEXT_ARGP();
- fd = bpf_btf_get_fd_by_id(id);
- if (fd < 0)
- p_err("can't get BTF object by id (%u): %s",
- id, strerror(errno));
- return fd;
- }
- static int
- build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type,
- void *info, __u32 *len)
- {
- static const char * const names[] = {
- [BPF_OBJ_UNKNOWN] = "unknown",
- [BPF_OBJ_PROG] = "prog",
- [BPF_OBJ_MAP] = "map",
- };
- LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts_ro);
- __u32 btf_id, id = 0;
- int err;
- int fd;
- opts_ro.open_flags = BPF_F_RDONLY;
- while (true) {
- switch (type) {
- case BPF_OBJ_PROG:
- err = bpf_prog_get_next_id(id, &id);
- break;
- case BPF_OBJ_MAP:
- err = bpf_map_get_next_id(id, &id);
- break;
- default:
- err = -1;
- p_err("unexpected object type: %u", type);
- goto err_free;
- }
- if (err) {
- if (errno == ENOENT) {
- err = 0;
- break;
- }
- p_err("can't get next %s: %s%s", names[type],
- strerror(errno),
- errno == EINVAL ? " -- kernel too old?" : "");
- goto err_free;
- }
- switch (type) {
- case BPF_OBJ_PROG:
- fd = bpf_prog_get_fd_by_id(id);
- break;
- case BPF_OBJ_MAP:
- fd = bpf_map_get_fd_by_id_opts(id, &opts_ro);
- break;
- default:
- err = -1;
- p_err("unexpected object type: %u", type);
- goto err_free;
- }
- if (fd < 0) {
- if (errno == ENOENT)
- continue;
- p_err("can't get %s by id (%u): %s", names[type], id,
- strerror(errno));
- err = -1;
- goto err_free;
- }
- memset(info, 0, *len);
- if (type == BPF_OBJ_PROG)
- err = bpf_prog_get_info_by_fd(fd, info, len);
- else
- err = bpf_map_get_info_by_fd(fd, info, len);
- close(fd);
- if (err) {
- p_err("can't get %s info: %s", names[type],
- strerror(errno));
- goto err_free;
- }
- switch (type) {
- case BPF_OBJ_PROG:
- btf_id = ((struct bpf_prog_info *)info)->btf_id;
- break;
- case BPF_OBJ_MAP:
- btf_id = ((struct bpf_map_info *)info)->btf_id;
- break;
- default:
- err = -1;
- p_err("unexpected object type: %u", type);
- goto err_free;
- }
- if (!btf_id)
- continue;
- err = hashmap__append(tab, btf_id, id);
- if (err) {
- p_err("failed to append entry to hashmap for BTF ID %u, object ID %u: %s",
- btf_id, id, strerror(-err));
- goto err_free;
- }
- }
- return 0;
- err_free:
- hashmap__free(tab);
- return err;
- }
- static int
- build_btf_tables(struct hashmap *btf_prog_table,
- struct hashmap *btf_map_table)
- {
- struct bpf_prog_info prog_info;
- __u32 prog_len = sizeof(prog_info);
- struct bpf_map_info map_info;
- __u32 map_len = sizeof(map_info);
- int err = 0;
- err = build_btf_type_table(btf_prog_table, BPF_OBJ_PROG, &prog_info,
- &prog_len);
- if (err)
- return err;
- err = build_btf_type_table(btf_map_table, BPF_OBJ_MAP, &map_info,
- &map_len);
- if (err) {
- hashmap__free(btf_prog_table);
- return err;
- }
- return 0;
- }
- static void
- show_btf_plain(struct bpf_btf_info *info, int fd,
- struct hashmap *btf_prog_table,
- struct hashmap *btf_map_table)
- {
- struct hashmap_entry *entry;
- const char *name = u64_to_ptr(info->name);
- int n;
- printf("%u: ", info->id);
- if (info->kernel_btf)
- printf("name [%s] ", name);
- else if (name && name[0])
- printf("name %s ", name);
- else
- printf("name <anon> ");
- printf("size %uB", info->btf_size);
- n = 0;
- hashmap__for_each_key_entry(btf_prog_table, entry, info->id) {
- printf("%s%lu", n++ == 0 ? " prog_ids " : ",", (unsigned long)entry->value);
- }
- n = 0;
- hashmap__for_each_key_entry(btf_map_table, entry, info->id) {
- printf("%s%lu", n++ == 0 ? " map_ids " : ",", (unsigned long)entry->value);
- }
- emit_obj_refs_plain(refs_table, info->id, "\n\tpids ");
- printf("\n");
- }
- static void
- show_btf_json(struct bpf_btf_info *info, int fd,
- struct hashmap *btf_prog_table,
- struct hashmap *btf_map_table)
- {
- struct hashmap_entry *entry;
- const char *name = u64_to_ptr(info->name);
- jsonw_start_object(json_wtr); /* btf object */
- jsonw_uint_field(json_wtr, "id", info->id);
- jsonw_uint_field(json_wtr, "size", info->btf_size);
- jsonw_name(json_wtr, "prog_ids");
- jsonw_start_array(json_wtr); /* prog_ids */
- hashmap__for_each_key_entry(btf_prog_table, entry, info->id) {
- jsonw_uint(json_wtr, entry->value);
- }
- jsonw_end_array(json_wtr); /* prog_ids */
- jsonw_name(json_wtr, "map_ids");
- jsonw_start_array(json_wtr); /* map_ids */
- hashmap__for_each_key_entry(btf_map_table, entry, info->id) {
- jsonw_uint(json_wtr, entry->value);
- }
- jsonw_end_array(json_wtr); /* map_ids */
- emit_obj_refs_json(refs_table, info->id, json_wtr); /* pids */
- jsonw_bool_field(json_wtr, "kernel", info->kernel_btf);
- if (name && name[0])
- jsonw_string_field(json_wtr, "name", name);
- jsonw_end_object(json_wtr); /* btf object */
- }
- static int
- show_btf(int fd, struct hashmap *btf_prog_table,
- struct hashmap *btf_map_table)
- {
- struct bpf_btf_info info;
- __u32 len = sizeof(info);
- char name[64];
- int err;
- memset(&info, 0, sizeof(info));
- err = bpf_btf_get_info_by_fd(fd, &info, &len);
- if (err) {
- p_err("can't get BTF object info: %s", strerror(errno));
- return -1;
- }
- /* if kernel support emitting BTF object name, pass name pointer */
- if (info.name_len) {
- memset(&info, 0, sizeof(info));
- info.name_len = sizeof(name);
- info.name = ptr_to_u64(name);
- len = sizeof(info);
- err = bpf_btf_get_info_by_fd(fd, &info, &len);
- if (err) {
- p_err("can't get BTF object info: %s", strerror(errno));
- return -1;
- }
- }
- if (json_output)
- show_btf_json(&info, fd, btf_prog_table, btf_map_table);
- else
- show_btf_plain(&info, fd, btf_prog_table, btf_map_table);
- return 0;
- }
- static int do_show(int argc, char **argv)
- {
- struct hashmap *btf_prog_table;
- struct hashmap *btf_map_table;
- int err, fd = -1;
- __u32 id = 0;
- if (argc == 2) {
- fd = btf_parse_fd(&argc, &argv);
- if (fd < 0)
- return -1;
- }
- if (argc) {
- if (fd >= 0)
- close(fd);
- return BAD_ARG();
- }
- btf_prog_table = hashmap__new(hash_fn_for_key_as_id,
- equal_fn_for_key_as_id, NULL);
- btf_map_table = hashmap__new(hash_fn_for_key_as_id,
- equal_fn_for_key_as_id, NULL);
- if (IS_ERR(btf_prog_table) || IS_ERR(btf_map_table)) {
- hashmap__free(btf_prog_table);
- hashmap__free(btf_map_table);
- if (fd >= 0)
- close(fd);
- p_err("failed to create hashmap for object references");
- return -1;
- }
- err = build_btf_tables(btf_prog_table, btf_map_table);
- if (err) {
- if (fd >= 0)
- close(fd);
- return err;
- }
- build_obj_refs_table(&refs_table, BPF_OBJ_BTF);
- if (fd >= 0) {
- err = show_btf(fd, btf_prog_table, btf_map_table);
- close(fd);
- goto exit_free;
- }
- if (json_output)
- jsonw_start_array(json_wtr); /* root array */
- while (true) {
- err = bpf_btf_get_next_id(id, &id);
- if (err) {
- if (errno == ENOENT) {
- err = 0;
- break;
- }
- p_err("can't get next BTF object: %s%s",
- strerror(errno),
- errno == EINVAL ? " -- kernel too old?" : "");
- err = -1;
- break;
- }
- fd = bpf_btf_get_fd_by_id(id);
- if (fd < 0) {
- if (errno == ENOENT)
- continue;
- p_err("can't get BTF object by id (%u): %s",
- id, strerror(errno));
- err = -1;
- break;
- }
- err = show_btf(fd, btf_prog_table, btf_map_table);
- close(fd);
- if (err)
- break;
- }
- if (json_output)
- jsonw_end_array(json_wtr); /* root array */
- exit_free:
- hashmap__free(btf_prog_table);
- hashmap__free(btf_map_table);
- delete_obj_refs_table(refs_table);
- return err;
- }
- static int do_help(int argc, char **argv)
- {
- if (json_output) {
- jsonw_null(json_wtr);
- return 0;
- }
- fprintf(stderr,
- "Usage: %1$s %2$s { show | list } [id BTF_ID]\n"
- " %1$s %2$s dump BTF_SRC [format FORMAT] [root_id ROOT_ID]\n"
- " %1$s %2$s help\n"
- "\n"
- " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
- " FORMAT := { raw | c [unsorted] }\n"
- " " HELP_SPEC_MAP "\n"
- " " HELP_SPEC_PROGRAM "\n"
- " " HELP_SPEC_OPTIONS " |\n"
- " {-B|--base-btf} }\n"
- "",
- bin_name, "btf");
- return 0;
- }
- static const struct cmd cmds[] = {
- { "show", do_show },
- { "list", do_show },
- { "help", do_help },
- { "dump", do_dump },
- { 0 }
- };
- int do_btf(int argc, char **argv)
- {
- return cmd_select(cmds, argc, argv, do_help);
- }
|