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