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