linux/tools/bpf/bpftool/btf.c
<<
>>
Prefs
   1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
   2/* Copyright (C) 2019 Facebook */
   3
   4#include <errno.h>
   5#include <fcntl.h>
   6#include <linux/err.h>
   7#include <stdbool.h>
   8#include <stdio.h>
   9#include <string.h>
  10#include <unistd.h>
  11#include <bpf.h>
  12#include <libbpf.h>
  13#include <linux/btf.h>
  14#include <linux/hashtable.h>
  15#include <sys/types.h>
  16#include <sys/stat.h>
  17#include <unistd.h>
  18
  19#include "btf.h"
  20#include "json_writer.h"
  21#include "main.h"
  22
  23static const char * const btf_kind_str[NR_BTF_KINDS] = {
  24        [BTF_KIND_UNKN]         = "UNKNOWN",
  25        [BTF_KIND_INT]          = "INT",
  26        [BTF_KIND_PTR]          = "PTR",
  27        [BTF_KIND_ARRAY]        = "ARRAY",
  28        [BTF_KIND_STRUCT]       = "STRUCT",
  29        [BTF_KIND_UNION]        = "UNION",
  30        [BTF_KIND_ENUM]         = "ENUM",
  31        [BTF_KIND_FWD]          = "FWD",
  32        [BTF_KIND_TYPEDEF]      = "TYPEDEF",
  33        [BTF_KIND_VOLATILE]     = "VOLATILE",
  34        [BTF_KIND_CONST]        = "CONST",
  35        [BTF_KIND_RESTRICT]     = "RESTRICT",
  36        [BTF_KIND_FUNC]         = "FUNC",
  37        [BTF_KIND_FUNC_PROTO]   = "FUNC_PROTO",
  38        [BTF_KIND_VAR]          = "VAR",
  39        [BTF_KIND_DATASEC]      = "DATASEC",
  40};
  41
  42struct btf_attach_table {
  43        DECLARE_HASHTABLE(table, 16);
  44};
  45
  46struct btf_attach_point {
  47        __u32 obj_id;
  48        __u32 btf_id;
  49        struct hlist_node hash;
  50};
  51
  52static const char *btf_int_enc_str(__u8 encoding)
  53{
  54        switch (encoding) {
  55        case 0:
  56                return "(none)";
  57        case BTF_INT_SIGNED:
  58                return "SIGNED";
  59        case BTF_INT_CHAR:
  60                return "CHAR";
  61        case BTF_INT_BOOL:
  62                return "BOOL";
  63        default:
  64                return "UNKN";
  65        }
  66}
  67
  68static const char *btf_var_linkage_str(__u32 linkage)
  69{
  70        switch (linkage) {
  71        case BTF_VAR_STATIC:
  72                return "static";
  73        case BTF_VAR_GLOBAL_ALLOCATED:
  74                return "global-alloc";
  75        default:
  76                return "(unknown)";
  77        }
  78}
  79
  80static const char *btf_str(const struct btf *btf, __u32 off)
  81{
  82        if (!off)
  83                return "(anon)";
  84        return btf__name_by_offset(btf, off) ? : "(invalid)";
  85}
  86
  87static int dump_btf_type(const struct btf *btf, __u32 id,
  88                         const struct btf_type *t)
  89{
  90        json_writer_t *w = json_wtr;
  91        int kind, safe_kind;
  92
  93        kind = BTF_INFO_KIND(t->info);
  94        safe_kind = kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
  95
  96        if (json_output) {
  97                jsonw_start_object(w);
  98                jsonw_uint_field(w, "id", id);
  99                jsonw_string_field(w, "kind", btf_kind_str[safe_kind]);
 100                jsonw_string_field(w, "name", btf_str(btf, t->name_off));
 101        } else {
 102                printf("[%u] %s '%s'", id, btf_kind_str[safe_kind],
 103                       btf_str(btf, t->name_off));
 104        }
 105
 106        switch (BTF_INFO_KIND(t->info)) {
 107        case BTF_KIND_INT: {
 108                __u32 v = *(__u32 *)(t + 1);
 109                const char *enc;
 110
 111                enc = btf_int_enc_str(BTF_INT_ENCODING(v));
 112
 113                if (json_output) {
 114                        jsonw_uint_field(w, "size", t->size);
 115                        jsonw_uint_field(w, "bits_offset", BTF_INT_OFFSET(v));
 116                        jsonw_uint_field(w, "nr_bits", BTF_INT_BITS(v));
 117                        jsonw_string_field(w, "encoding", enc);
 118                } else {
 119                        printf(" size=%u bits_offset=%u nr_bits=%u encoding=%s",
 120                               t->size, BTF_INT_OFFSET(v), BTF_INT_BITS(v),
 121                               enc);
 122                }
 123                break;
 124        }
 125        case BTF_KIND_PTR:
 126        case BTF_KIND_CONST:
 127        case BTF_KIND_VOLATILE:
 128        case BTF_KIND_RESTRICT:
 129        case BTF_KIND_TYPEDEF:
 130                if (json_output)
 131                        jsonw_uint_field(w, "type_id", t->type);
 132                else
 133                        printf(" type_id=%u", t->type);
 134                break;
 135        case BTF_KIND_ARRAY: {
 136                const struct btf_array *arr = (const void *)(t + 1);
 137
 138                if (json_output) {
 139                        jsonw_uint_field(w, "type_id", arr->type);
 140                        jsonw_uint_field(w, "index_type_id", arr->index_type);
 141                        jsonw_uint_field(w, "nr_elems", arr->nelems);
 142                } else {
 143                        printf(" type_id=%u index_type_id=%u nr_elems=%u",
 144                               arr->type, arr->index_type, arr->nelems);
 145                }
 146                break;
 147        }
 148        case BTF_KIND_STRUCT:
 149        case BTF_KIND_UNION: {
 150                const struct btf_member *m = (const void *)(t + 1);
 151                __u16 vlen = BTF_INFO_VLEN(t->info);
 152                int i;
 153
 154                if (json_output) {
 155                        jsonw_uint_field(w, "size", t->size);
 156                        jsonw_uint_field(w, "vlen", vlen);
 157                        jsonw_name(w, "members");
 158                        jsonw_start_array(w);
 159                } else {
 160                        printf(" size=%u vlen=%u", t->size, vlen);
 161                }
 162                for (i = 0; i < vlen; i++, m++) {
 163                        const char *name = btf_str(btf, m->name_off);
 164                        __u32 bit_off, bit_sz;
 165
 166                        if (BTF_INFO_KFLAG(t->info)) {
 167                                bit_off = BTF_MEMBER_BIT_OFFSET(m->offset);
 168                                bit_sz = BTF_MEMBER_BITFIELD_SIZE(m->offset);
 169                        } else {
 170                                bit_off = m->offset;
 171                                bit_sz = 0;
 172                        }
 173
 174                        if (json_output) {
 175                                jsonw_start_object(w);
 176                                jsonw_string_field(w, "name", name);
 177                                jsonw_uint_field(w, "type_id", m->type);
 178                                jsonw_uint_field(w, "bits_offset", bit_off);
 179                                if (bit_sz) {
 180                                        jsonw_uint_field(w, "bitfield_size",
 181                                                         bit_sz);
 182                                }
 183                                jsonw_end_object(w);
 184                        } else {
 185                                printf("\n\t'%s' type_id=%u bits_offset=%u",
 186                                       name, m->type, bit_off);
 187                                if (bit_sz)
 188                                        printf(" bitfield_size=%u", bit_sz);
 189                        }
 190                }
 191                if (json_output)
 192                        jsonw_end_array(w);
 193                break;
 194        }
 195        case BTF_KIND_ENUM: {
 196                const struct btf_enum *v = (const void *)(t + 1);
 197                __u16 vlen = BTF_INFO_VLEN(t->info);
 198                int i;
 199
 200                if (json_output) {
 201                        jsonw_uint_field(w, "size", t->size);
 202                        jsonw_uint_field(w, "vlen", vlen);
 203                        jsonw_name(w, "values");
 204                        jsonw_start_array(w);
 205                } else {
 206                        printf(" size=%u vlen=%u", t->size, vlen);
 207                }
 208                for (i = 0; i < vlen; i++, v++) {
 209                        const char *name = btf_str(btf, v->name_off);
 210
 211                        if (json_output) {
 212                                jsonw_start_object(w);
 213                                jsonw_string_field(w, "name", name);
 214                                jsonw_uint_field(w, "val", v->val);
 215                                jsonw_end_object(w);
 216                        } else {
 217                                printf("\n\t'%s' val=%u", name, v->val);
 218                        }
 219                }
 220                if (json_output)
 221                        jsonw_end_array(w);
 222                break;
 223        }
 224        case BTF_KIND_FWD: {
 225                const char *fwd_kind = BTF_INFO_KFLAG(t->info) ? "union"
 226                                                               : "struct";
 227
 228                if (json_output)
 229                        jsonw_string_field(w, "fwd_kind", fwd_kind);
 230                else
 231                        printf(" fwd_kind=%s", fwd_kind);
 232                break;
 233        }
 234        case BTF_KIND_FUNC:
 235                if (json_output)
 236                        jsonw_uint_field(w, "type_id", t->type);
 237                else
 238                        printf(" type_id=%u", t->type);
 239                break;
 240        case BTF_KIND_FUNC_PROTO: {
 241                const struct btf_param *p = (const void *)(t + 1);
 242                __u16 vlen = BTF_INFO_VLEN(t->info);
 243                int i;
 244
 245                if (json_output) {
 246                        jsonw_uint_field(w, "ret_type_id", t->type);
 247                        jsonw_uint_field(w, "vlen", vlen);
 248                        jsonw_name(w, "params");
 249                        jsonw_start_array(w);
 250                } else {
 251                        printf(" ret_type_id=%u vlen=%u", t->type, vlen);
 252                }
 253                for (i = 0; i < vlen; i++, p++) {
 254                        const char *name = btf_str(btf, p->name_off);
 255
 256                        if (json_output) {
 257                                jsonw_start_object(w);
 258                                jsonw_string_field(w, "name", name);
 259                                jsonw_uint_field(w, "type_id", p->type);
 260                                jsonw_end_object(w);
 261                        } else {
 262                                printf("\n\t'%s' type_id=%u", name, p->type);
 263                        }
 264                }
 265                if (json_output)
 266                        jsonw_end_array(w);
 267                break;
 268        }
 269        case BTF_KIND_VAR: {
 270                const struct btf_var *v = (const void *)(t + 1);
 271                const char *linkage;
 272
 273                linkage = btf_var_linkage_str(v->linkage);
 274
 275                if (json_output) {
 276                        jsonw_uint_field(w, "type_id", t->type);
 277                        jsonw_string_field(w, "linkage", linkage);
 278                } else {
 279                        printf(" type_id=%u, linkage=%s", t->type, linkage);
 280                }
 281                break;
 282        }
 283        case BTF_KIND_DATASEC: {
 284                const struct btf_var_secinfo *v = (const void *)(t+1);
 285                __u16 vlen = BTF_INFO_VLEN(t->info);
 286                int i;
 287
 288                if (json_output) {
 289                        jsonw_uint_field(w, "size", t->size);
 290                        jsonw_uint_field(w, "vlen", vlen);
 291                        jsonw_name(w, "vars");
 292                        jsonw_start_array(w);
 293                } else {
 294                        printf(" size=%u vlen=%u", t->size, vlen);
 295                }
 296                for (i = 0; i < vlen; i++, v++) {
 297                        if (json_output) {
 298                                jsonw_start_object(w);
 299                                jsonw_uint_field(w, "type_id", v->type);
 300                                jsonw_uint_field(w, "offset", v->offset);
 301                                jsonw_uint_field(w, "size", v->size);
 302                                jsonw_end_object(w);
 303                        } else {
 304                                printf("\n\ttype_id=%u offset=%u size=%u",
 305                                       v->type, v->offset, v->size);
 306                        }
 307                }
 308                if (json_output)
 309                        jsonw_end_array(w);
 310                break;
 311        }
 312        default:
 313                break;
 314        }
 315
 316        if (json_output)
 317                jsonw_end_object(json_wtr);
 318        else
 319                printf("\n");
 320
 321        return 0;
 322}
 323
 324static int dump_btf_raw(const struct btf *btf,
 325                        __u32 *root_type_ids, int root_type_cnt)
 326{
 327        const struct btf_type *t;
 328        int i;
 329
 330        if (json_output) {
 331                jsonw_start_object(json_wtr);
 332                jsonw_name(json_wtr, "types");
 333                jsonw_start_array(json_wtr);
 334        }
 335
 336        if (root_type_cnt) {
 337                for (i = 0; i < root_type_cnt; i++) {
 338                        t = btf__type_by_id(btf, root_type_ids[i]);
 339                        dump_btf_type(btf, root_type_ids[i], t);
 340                }
 341        } else {
 342                int cnt = btf__get_nr_types(btf);
 343
 344                for (i = 1; i <= cnt; i++) {
 345                        t = btf__type_by_id(btf, i);
 346                        dump_btf_type(btf, i, t);
 347                }
 348        }
 349
 350        if (json_output) {
 351                jsonw_end_array(json_wtr);
 352                jsonw_end_object(json_wtr);
 353        }
 354        return 0;
 355}
 356
 357static void __printf(2, 0) btf_dump_printf(void *ctx,
 358                                           const char *fmt, va_list args)
 359{
 360        vfprintf(stdout, fmt, args);
 361}
 362
 363static int dump_btf_c(const struct btf *btf,
 364                      __u32 *root_type_ids, int root_type_cnt)
 365{
 366        struct btf_dump *d;
 367        int err = 0, i;
 368
 369        d = btf_dump__new(btf, NULL, NULL, btf_dump_printf);
 370        if (IS_ERR(d))
 371                return PTR_ERR(d);
 372
 373        if (root_type_cnt) {
 374                for (i = 0; i < root_type_cnt; i++) {
 375                        err = btf_dump__dump_type(d, root_type_ids[i]);
 376                        if (err)
 377                                goto done;
 378                }
 379        } else {
 380                int cnt = btf__get_nr_types(btf);
 381
 382                for (i = 1; i <= cnt; i++) {
 383                        err = btf_dump__dump_type(d, i);
 384                        if (err)
 385                                goto done;
 386                }
 387        }
 388
 389done:
 390        btf_dump__free(d);
 391        return err;
 392}
 393
 394static struct btf *btf__parse_raw(const char *file)
 395{
 396        struct btf *btf;
 397        struct stat st;
 398        __u8 *buf;
 399        FILE *f;
 400
 401        if (stat(file, &st))
 402                return NULL;
 403
 404        f = fopen(file, "rb");
 405        if (!f)
 406                return NULL;
 407
 408        buf = malloc(st.st_size);
 409        if (!buf) {
 410                btf = ERR_PTR(-ENOMEM);
 411                goto exit_close;
 412        }
 413
 414        if ((size_t) st.st_size != fread(buf, 1, st.st_size, f)) {
 415                btf = ERR_PTR(-EINVAL);
 416                goto exit_free;
 417        }
 418
 419        btf = btf__new(buf, st.st_size);
 420
 421exit_free:
 422        free(buf);
 423exit_close:
 424        fclose(f);
 425        return btf;
 426}
 427
 428static bool is_btf_raw(const char *file)
 429{
 430        __u16 magic = 0;
 431        int fd, nb_read;
 432
 433        fd = open(file, O_RDONLY);
 434        if (fd < 0)
 435                return false;
 436
 437        nb_read = read(fd, &magic, sizeof(magic));
 438        close(fd);
 439        return nb_read == sizeof(magic) && magic == BTF_MAGIC;
 440}
 441
 442static int do_dump(int argc, char **argv)
 443{
 444        struct btf *btf = NULL;
 445        __u32 root_type_ids[2];
 446        int root_type_cnt = 0;
 447        bool dump_c = false;
 448        __u32 btf_id = -1;
 449        const char *src;
 450        int fd = -1;
 451        int err;
 452
 453        if (!REQ_ARGS(2)) {
 454                usage();
 455                return -1;
 456        }
 457        src = GET_ARG();
 458
 459        if (is_prefix(src, "map")) {
 460                struct bpf_map_info info = {};
 461                __u32 len = sizeof(info);
 462
 463                if (!REQ_ARGS(2)) {
 464                        usage();
 465                        return -1;
 466                }
 467
 468                fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
 469                if (fd < 0)
 470                        return -1;
 471
 472                btf_id = info.btf_id;
 473                if (argc && is_prefix(*argv, "key")) {
 474                        root_type_ids[root_type_cnt++] = info.btf_key_type_id;
 475                        NEXT_ARG();
 476                } else if (argc && is_prefix(*argv, "value")) {
 477                        root_type_ids[root_type_cnt++] = info.btf_value_type_id;
 478                        NEXT_ARG();
 479                } else if (argc && is_prefix(*argv, "all")) {
 480                        NEXT_ARG();
 481                } else if (argc && is_prefix(*argv, "kv")) {
 482                        root_type_ids[root_type_cnt++] = info.btf_key_type_id;
 483                        root_type_ids[root_type_cnt++] = info.btf_value_type_id;
 484                        NEXT_ARG();
 485                } else {
 486                        root_type_ids[root_type_cnt++] = info.btf_key_type_id;
 487                        root_type_ids[root_type_cnt++] = info.btf_value_type_id;
 488                }
 489        } else if (is_prefix(src, "prog")) {
 490                struct bpf_prog_info info = {};
 491                __u32 len = sizeof(info);
 492
 493                if (!REQ_ARGS(2)) {
 494                        usage();
 495                        return -1;
 496                }
 497
 498                fd = prog_parse_fd(&argc, &argv);
 499                if (fd < 0)
 500                        return -1;
 501
 502                err = bpf_obj_get_info_by_fd(fd, &info, &len);
 503                if (err) {
 504                        p_err("can't get prog info: %s", strerror(errno));
 505                        goto done;
 506                }
 507
 508                btf_id = info.btf_id;
 509        } else if (is_prefix(src, "id")) {
 510                char *endptr;
 511
 512                btf_id = strtoul(*argv, &endptr, 0);
 513                if (*endptr) {
 514                        p_err("can't parse %s as ID", *argv);
 515                        return -1;
 516                }
 517                NEXT_ARG();
 518        } else if (is_prefix(src, "file")) {
 519                if (is_btf_raw(*argv))
 520                        btf = btf__parse_raw(*argv);
 521                else
 522                        btf = btf__parse_elf(*argv, NULL);
 523
 524                if (IS_ERR(btf)) {
 525                        err = PTR_ERR(btf);
 526                        btf = NULL;
 527                        p_err("failed to load BTF from %s: %s", 
 528                              *argv, strerror(err));
 529                        goto done;
 530                }
 531                NEXT_ARG();
 532        } else {
 533                err = -1;
 534                p_err("unrecognized BTF source specifier: '%s'", src);
 535                goto done;
 536        }
 537
 538        while (argc) {
 539                if (is_prefix(*argv, "format")) {
 540                        NEXT_ARG();
 541                        if (argc < 1) {
 542                                p_err("expecting value for 'format' option\n");
 543                                goto done;
 544                        }
 545                        if (strcmp(*argv, "c") == 0) {
 546                                dump_c = true;
 547                        } else if (strcmp(*argv, "raw") == 0) {
 548                                dump_c = false;
 549                        } else {
 550                                p_err("unrecognized format specifier: '%s', possible values: raw, c",
 551                                      *argv);
 552                                goto done;
 553                        }
 554                        NEXT_ARG();
 555                } else {
 556                        p_err("unrecognized option: '%s'", *argv);
 557                        goto done;
 558                }
 559        }
 560
 561        if (!btf) {
 562                err = btf__get_from_id(btf_id, &btf);
 563                if (err) {
 564                        p_err("get btf by id (%u): %s", btf_id, strerror(err));
 565                        goto done;
 566                }
 567                if (!btf) {
 568                        err = ENOENT;
 569                        p_err("can't find btf with ID (%u)", btf_id);
 570                        goto done;
 571                }
 572        }
 573
 574        if (dump_c) {
 575                if (json_output) {
 576                        p_err("JSON output for C-syntax dump is not supported");
 577                        err = -ENOTSUP;
 578                        goto done;
 579                }
 580                err = dump_btf_c(btf, root_type_ids, root_type_cnt);
 581        } else {
 582                err = dump_btf_raw(btf, root_type_ids, root_type_cnt);
 583        }
 584
 585done:
 586        close(fd);
 587        btf__free(btf);
 588        return err;
 589}
 590
 591static int btf_parse_fd(int *argc, char ***argv)
 592{
 593        unsigned int id;
 594        char *endptr;
 595        int fd;
 596
 597        if (!is_prefix(*argv[0], "id")) {
 598                p_err("expected 'id', got: '%s'?", **argv);
 599                return -1;
 600        }
 601        NEXT_ARGP();
 602
 603        id = strtoul(**argv, &endptr, 0);
 604        if (*endptr) {
 605                p_err("can't parse %s as ID", **argv);
 606                return -1;
 607        }
 608        NEXT_ARGP();
 609
 610        fd = bpf_btf_get_fd_by_id(id);
 611        if (fd < 0)
 612                p_err("can't get BTF object by id (%u): %s",
 613                      id, strerror(errno));
 614
 615        return fd;
 616}
 617
 618static void delete_btf_table(struct btf_attach_table *tab)
 619{
 620        struct btf_attach_point *obj;
 621        struct hlist_node *tmp;
 622
 623        unsigned int bkt;
 624
 625        hash_for_each_safe(tab->table, bkt, tmp, obj, hash) {
 626                hash_del(&obj->hash);
 627                free(obj);
 628        }
 629}
 630
 631static int
 632build_btf_type_table(struct btf_attach_table *tab, enum bpf_obj_type type,
 633                     void *info, __u32 *len)
 634{
 635        static const char * const names[] = {
 636                [BPF_OBJ_UNKNOWN]       = "unknown",
 637                [BPF_OBJ_PROG]          = "prog",
 638                [BPF_OBJ_MAP]           = "map",
 639        };
 640        struct btf_attach_point *obj_node;
 641        __u32 btf_id, id = 0;
 642        int err;
 643        int fd;
 644
 645        while (true) {
 646                switch (type) {
 647                case BPF_OBJ_PROG:
 648                        err = bpf_prog_get_next_id(id, &id);
 649                        break;
 650                case BPF_OBJ_MAP:
 651                        err = bpf_map_get_next_id(id, &id);
 652                        break;
 653                default:
 654                        err = -1;
 655                        p_err("unexpected object type: %d", type);
 656                        goto err_free;
 657                }
 658                if (err) {
 659                        if (errno == ENOENT) {
 660                                err = 0;
 661                                break;
 662                        }
 663                        p_err("can't get next %s: %s%s", names[type],
 664                              strerror(errno),
 665                              errno == EINVAL ? " -- kernel too old?" : "");
 666                        goto err_free;
 667                }
 668
 669                switch (type) {
 670                case BPF_OBJ_PROG:
 671                        fd = bpf_prog_get_fd_by_id(id);
 672                        break;
 673                case BPF_OBJ_MAP:
 674                        fd = bpf_map_get_fd_by_id(id);
 675                        break;
 676                default:
 677                        err = -1;
 678                        p_err("unexpected object type: %d", type);
 679                        goto err_free;
 680                }
 681                if (fd < 0) {
 682                        if (errno == ENOENT)
 683                                continue;
 684                        p_err("can't get %s by id (%u): %s", names[type], id,
 685                              strerror(errno));
 686                        err = -1;
 687                        goto err_free;
 688                }
 689
 690                memset(info, 0, *len);
 691                err = bpf_obj_get_info_by_fd(fd, info, len);
 692                close(fd);
 693                if (err) {
 694                        p_err("can't get %s info: %s", names[type],
 695                              strerror(errno));
 696                        goto err_free;
 697                }
 698
 699                switch (type) {
 700                case BPF_OBJ_PROG:
 701                        btf_id = ((struct bpf_prog_info *)info)->btf_id;
 702                        break;
 703                case BPF_OBJ_MAP:
 704                        btf_id = ((struct bpf_map_info *)info)->btf_id;
 705                        break;
 706                default:
 707                        err = -1;
 708                        p_err("unexpected object type: %d", type);
 709                        goto err_free;
 710                }
 711                if (!btf_id)
 712                        continue;
 713
 714                obj_node = calloc(1, sizeof(*obj_node));
 715                if (!obj_node) {
 716                        p_err("failed to allocate memory: %s", strerror(errno));
 717                        goto err_free;
 718                }
 719
 720                obj_node->obj_id = id;
 721                obj_node->btf_id = btf_id;
 722                hash_add(tab->table, &obj_node->hash, obj_node->btf_id);
 723        }
 724
 725        return 0;
 726
 727err_free:
 728        delete_btf_table(tab);
 729        return err;
 730}
 731
 732static int
 733build_btf_tables(struct btf_attach_table *btf_prog_table,
 734                 struct btf_attach_table *btf_map_table)
 735{
 736        struct bpf_prog_info prog_info;
 737        __u32 prog_len = sizeof(prog_info);
 738        struct bpf_map_info map_info;
 739        __u32 map_len = sizeof(map_info);
 740        int err = 0;
 741
 742        err = build_btf_type_table(btf_prog_table, BPF_OBJ_PROG, &prog_info,
 743                                   &prog_len);
 744        if (err)
 745                return err;
 746
 747        err = build_btf_type_table(btf_map_table, BPF_OBJ_MAP, &map_info,
 748                                   &map_len);
 749        if (err) {
 750                delete_btf_table(btf_prog_table);
 751                return err;
 752        }
 753
 754        return 0;
 755}
 756
 757static void
 758show_btf_plain(struct bpf_btf_info *info, int fd,
 759               struct btf_attach_table *btf_prog_table,
 760               struct btf_attach_table *btf_map_table)
 761{
 762        struct btf_attach_point *obj;
 763        int n;
 764
 765        printf("%u: ", info->id);
 766        printf("size %uB", info->btf_size);
 767
 768        n = 0;
 769        hash_for_each_possible(btf_prog_table->table, obj, hash, info->id) {
 770                if (obj->btf_id == info->id)
 771                        printf("%s%u", n++ == 0 ? "  prog_ids " : ",",
 772                               obj->obj_id);
 773        }
 774
 775        n = 0;
 776        hash_for_each_possible(btf_map_table->table, obj, hash, info->id) {
 777                if (obj->btf_id == info->id)
 778                        printf("%s%u", n++ == 0 ? "  map_ids " : ",",
 779                               obj->obj_id);
 780        }
 781
 782        printf("\n");
 783}
 784
 785static void
 786show_btf_json(struct bpf_btf_info *info, int fd,
 787              struct btf_attach_table *btf_prog_table,
 788              struct btf_attach_table *btf_map_table)
 789{
 790        struct btf_attach_point *obj;
 791
 792        jsonw_start_object(json_wtr);   /* btf object */
 793        jsonw_uint_field(json_wtr, "id", info->id);
 794        jsonw_uint_field(json_wtr, "size", info->btf_size);
 795
 796        jsonw_name(json_wtr, "prog_ids");
 797        jsonw_start_array(json_wtr);    /* prog_ids */
 798        hash_for_each_possible(btf_prog_table->table, obj, hash,
 799                               info->id) {
 800                if (obj->btf_id == info->id)
 801                        jsonw_uint(json_wtr, obj->obj_id);
 802        }
 803        jsonw_end_array(json_wtr);      /* prog_ids */
 804
 805        jsonw_name(json_wtr, "map_ids");
 806        jsonw_start_array(json_wtr);    /* map_ids */
 807        hash_for_each_possible(btf_map_table->table, obj, hash,
 808                               info->id) {
 809                if (obj->btf_id == info->id)
 810                        jsonw_uint(json_wtr, obj->obj_id);
 811        }
 812        jsonw_end_array(json_wtr);      /* map_ids */
 813        jsonw_end_object(json_wtr);     /* btf object */
 814}
 815
 816static int
 817show_btf(int fd, struct btf_attach_table *btf_prog_table,
 818         struct btf_attach_table *btf_map_table)
 819{
 820        struct bpf_btf_info info = {};
 821        __u32 len = sizeof(info);
 822        int err;
 823
 824        err = bpf_obj_get_info_by_fd(fd, &info, &len);
 825        if (err) {
 826                p_err("can't get BTF object info: %s", strerror(errno));
 827                return -1;
 828        }
 829
 830        if (json_output)
 831                show_btf_json(&info, fd, btf_prog_table, btf_map_table);
 832        else
 833                show_btf_plain(&info, fd, btf_prog_table, btf_map_table);
 834
 835        return 0;
 836}
 837
 838static int do_show(int argc, char **argv)
 839{
 840        struct btf_attach_table btf_prog_table;
 841        struct btf_attach_table btf_map_table;
 842        int err, fd = -1;
 843        __u32 id = 0;
 844
 845        if (argc == 2) {
 846                fd = btf_parse_fd(&argc, &argv);
 847                if (fd < 0)
 848                        return -1;
 849        }
 850
 851        if (argc) {
 852                if (fd >= 0)
 853                        close(fd);
 854                return BAD_ARG();
 855        }
 856
 857        hash_init(btf_prog_table.table);
 858        hash_init(btf_map_table.table);
 859        err = build_btf_tables(&btf_prog_table, &btf_map_table);
 860        if (err) {
 861                if (fd >= 0)
 862                        close(fd);
 863                return err;
 864        }
 865
 866        if (fd >= 0) {
 867                err = show_btf(fd, &btf_prog_table, &btf_map_table);
 868                close(fd);
 869                goto exit_free;
 870        }
 871
 872        if (json_output)
 873                jsonw_start_array(json_wtr);    /* root array */
 874
 875        while (true) {
 876                err = bpf_btf_get_next_id(id, &id);
 877                if (err) {
 878                        if (errno == ENOENT) {
 879                                err = 0;
 880                                break;
 881                        }
 882                        p_err("can't get next BTF object: %s%s",
 883                              strerror(errno),
 884                              errno == EINVAL ? " -- kernel too old?" : "");
 885                        err = -1;
 886                        break;
 887                }
 888
 889                fd = bpf_btf_get_fd_by_id(id);
 890                if (fd < 0) {
 891                        if (errno == ENOENT)
 892                                continue;
 893                        p_err("can't get BTF object by id (%u): %s",
 894                              id, strerror(errno));
 895                        err = -1;
 896                        break;
 897                }
 898
 899                err = show_btf(fd, &btf_prog_table, &btf_map_table);
 900                close(fd);
 901                if (err)
 902                        break;
 903        }
 904
 905        if (json_output)
 906                jsonw_end_array(json_wtr);      /* root array */
 907
 908exit_free:
 909        delete_btf_table(&btf_prog_table);
 910        delete_btf_table(&btf_map_table);
 911
 912        return err;
 913}
 914
 915static int do_help(int argc, char **argv)
 916{
 917        if (json_output) {
 918                jsonw_null(json_wtr);
 919                return 0;
 920        }
 921
 922        fprintf(stderr,
 923                "Usage: %s btf { show | list } [id BTF_ID]\n"
 924                "       %s btf dump BTF_SRC [format FORMAT]\n"
 925                "       %s btf help\n"
 926                "\n"
 927                "       BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
 928                "       FORMAT  := { raw | c }\n"
 929                "       " HELP_SPEC_MAP "\n"
 930                "       " HELP_SPEC_PROGRAM "\n"
 931                "       " HELP_SPEC_OPTIONS "\n"
 932                "",
 933                bin_name, bin_name, bin_name);
 934
 935        return 0;
 936}
 937
 938static const struct cmd cmds[] = {
 939        { "show",       do_show },
 940        { "list",       do_show },
 941        { "help",       do_help },
 942        { "dump",       do_dump },
 943        { 0 }
 944};
 945
 946int do_btf(int argc, char **argv)
 947{
 948        return cmd_select(cmds, argc, argv, do_help);
 949}
 950