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 int do_dump(int argc, char **argv)
 426{
 427        struct btf *btf = NULL;
 428        __u32 root_type_ids[2];
 429        int root_type_cnt = 0;
 430        bool dump_c = false;
 431        __u32 btf_id = -1;
 432        const char *src;
 433        int fd = -1;
 434        int err;
 435
 436        if (!REQ_ARGS(2)) {
 437                usage();
 438                return -1;
 439        }
 440        src = GET_ARG();
 441
 442        if (is_prefix(src, "map")) {
 443                struct bpf_map_info info = {};
 444                __u32 len = sizeof(info);
 445
 446                if (!REQ_ARGS(2)) {
 447                        usage();
 448                        return -1;
 449                }
 450
 451                fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
 452                if (fd < 0)
 453                        return -1;
 454
 455                btf_id = info.btf_id;
 456                if (argc && is_prefix(*argv, "key")) {
 457                        root_type_ids[root_type_cnt++] = info.btf_key_type_id;
 458                        NEXT_ARG();
 459                } else if (argc && is_prefix(*argv, "value")) {
 460                        root_type_ids[root_type_cnt++] = info.btf_value_type_id;
 461                        NEXT_ARG();
 462                } else if (argc && is_prefix(*argv, "all")) {
 463                        NEXT_ARG();
 464                } else if (argc && is_prefix(*argv, "kv")) {
 465                        root_type_ids[root_type_cnt++] = info.btf_key_type_id;
 466                        root_type_ids[root_type_cnt++] = info.btf_value_type_id;
 467                        NEXT_ARG();
 468                } else {
 469                        root_type_ids[root_type_cnt++] = info.btf_key_type_id;
 470                        root_type_ids[root_type_cnt++] = info.btf_value_type_id;
 471                }
 472        } else if (is_prefix(src, "prog")) {
 473                struct bpf_prog_info info = {};
 474                __u32 len = sizeof(info);
 475
 476                if (!REQ_ARGS(2)) {
 477                        usage();
 478                        return -1;
 479                }
 480
 481                fd = prog_parse_fd(&argc, &argv);
 482                if (fd < 0)
 483                        return -1;
 484
 485                err = bpf_obj_get_info_by_fd(fd, &info, &len);
 486                if (err) {
 487                        p_err("can't get prog info: %s", strerror(errno));
 488                        goto done;
 489                }
 490
 491                btf_id = info.btf_id;
 492        } else if (is_prefix(src, "id")) {
 493                char *endptr;
 494
 495                btf_id = strtoul(*argv, &endptr, 0);
 496                if (*endptr) {
 497                        p_err("can't parse %s as ID", *argv);
 498                        return -1;
 499                }
 500                NEXT_ARG();
 501        } else if (is_prefix(src, "file")) {
 502                btf = btf__parse(*argv, NULL);
 503                if (IS_ERR(btf)) {
 504                        err = -PTR_ERR(btf);
 505                        btf = NULL;
 506                        p_err("failed to load BTF from %s: %s",
 507                              *argv, strerror(err));
 508                        goto done;
 509                }
 510                NEXT_ARG();
 511        } else {
 512                err = -1;
 513                p_err("unrecognized BTF source specifier: '%s'", src);
 514                goto done;
 515        }
 516
 517        while (argc) {
 518                if (is_prefix(*argv, "format")) {
 519                        NEXT_ARG();
 520                        if (argc < 1) {
 521                                p_err("expecting value for 'format' option\n");
 522                                goto done;
 523                        }
 524                        if (strcmp(*argv, "c") == 0) {
 525                                dump_c = true;
 526                        } else if (strcmp(*argv, "raw") == 0) {
 527                                dump_c = false;
 528                        } else {
 529                                p_err("unrecognized format specifier: '%s', possible values: raw, c",
 530                                      *argv);
 531                                goto done;
 532                        }
 533                        NEXT_ARG();
 534                } else {
 535                        p_err("unrecognized option: '%s'", *argv);
 536                        goto done;
 537                }
 538        }
 539
 540        if (!btf) {
 541                err = btf__get_from_id(btf_id, &btf);
 542                if (err) {
 543                        p_err("get btf by id (%u): %s", btf_id, strerror(err));
 544                        goto done;
 545                }
 546                if (!btf) {
 547                        err = -ENOENT;
 548                        p_err("can't find btf with ID (%u)", btf_id);
 549                        goto done;
 550                }
 551        }
 552
 553        if (dump_c) {
 554                if (json_output) {
 555                        p_err("JSON output for C-syntax dump is not supported");
 556                        err = -ENOTSUP;
 557                        goto done;
 558                }
 559                err = dump_btf_c(btf, root_type_ids, root_type_cnt);
 560        } else {
 561                err = dump_btf_raw(btf, root_type_ids, root_type_cnt);
 562        }
 563
 564done:
 565        close(fd);
 566        btf__free(btf);
 567        return err;
 568}
 569
 570static int btf_parse_fd(int *argc, char ***argv)
 571{
 572        unsigned int id;
 573        char *endptr;
 574        int fd;
 575
 576        if (!is_prefix(*argv[0], "id")) {
 577                p_err("expected 'id', got: '%s'?", **argv);
 578                return -1;
 579        }
 580        NEXT_ARGP();
 581
 582        id = strtoul(**argv, &endptr, 0);
 583        if (*endptr) {
 584                p_err("can't parse %s as ID", **argv);
 585                return -1;
 586        }
 587        NEXT_ARGP();
 588
 589        fd = bpf_btf_get_fd_by_id(id);
 590        if (fd < 0)
 591                p_err("can't get BTF object by id (%u): %s",
 592                      id, strerror(errno));
 593
 594        return fd;
 595}
 596
 597static void delete_btf_table(struct btf_attach_table *tab)
 598{
 599        struct btf_attach_point *obj;
 600        struct hlist_node *tmp;
 601
 602        unsigned int bkt;
 603
 604        hash_for_each_safe(tab->table, bkt, tmp, obj, hash) {
 605                hash_del(&obj->hash);
 606                free(obj);
 607        }
 608}
 609
 610static int
 611build_btf_type_table(struct btf_attach_table *tab, enum bpf_obj_type type,
 612                     void *info, __u32 *len)
 613{
 614        static const char * const names[] = {
 615                [BPF_OBJ_UNKNOWN]       = "unknown",
 616                [BPF_OBJ_PROG]          = "prog",
 617                [BPF_OBJ_MAP]           = "map",
 618        };
 619        struct btf_attach_point *obj_node;
 620        __u32 btf_id, id = 0;
 621        int err;
 622        int fd;
 623
 624        while (true) {
 625                switch (type) {
 626                case BPF_OBJ_PROG:
 627                        err = bpf_prog_get_next_id(id, &id);
 628                        break;
 629                case BPF_OBJ_MAP:
 630                        err = bpf_map_get_next_id(id, &id);
 631                        break;
 632                default:
 633                        err = -1;
 634                        p_err("unexpected object type: %d", type);
 635                        goto err_free;
 636                }
 637                if (err) {
 638                        if (errno == ENOENT) {
 639                                err = 0;
 640                                break;
 641                        }
 642                        p_err("can't get next %s: %s%s", names[type],
 643                              strerror(errno),
 644                              errno == EINVAL ? " -- kernel too old?" : "");
 645                        goto err_free;
 646                }
 647
 648                switch (type) {
 649                case BPF_OBJ_PROG:
 650                        fd = bpf_prog_get_fd_by_id(id);
 651                        break;
 652                case BPF_OBJ_MAP:
 653                        fd = bpf_map_get_fd_by_id(id);
 654                        break;
 655                default:
 656                        err = -1;
 657                        p_err("unexpected object type: %d", type);
 658                        goto err_free;
 659                }
 660                if (fd < 0) {
 661                        if (errno == ENOENT)
 662                                continue;
 663                        p_err("can't get %s by id (%u): %s", names[type], id,
 664                              strerror(errno));
 665                        err = -1;
 666                        goto err_free;
 667                }
 668
 669                memset(info, 0, *len);
 670                err = bpf_obj_get_info_by_fd(fd, info, len);
 671                close(fd);
 672                if (err) {
 673                        p_err("can't get %s info: %s", names[type],
 674                              strerror(errno));
 675                        goto err_free;
 676                }
 677
 678                switch (type) {
 679                case BPF_OBJ_PROG:
 680                        btf_id = ((struct bpf_prog_info *)info)->btf_id;
 681                        break;
 682                case BPF_OBJ_MAP:
 683                        btf_id = ((struct bpf_map_info *)info)->btf_id;
 684                        break;
 685                default:
 686                        err = -1;
 687                        p_err("unexpected object type: %d", type);
 688                        goto err_free;
 689                }
 690                if (!btf_id)
 691                        continue;
 692
 693                obj_node = calloc(1, sizeof(*obj_node));
 694                if (!obj_node) {
 695                        p_err("failed to allocate memory: %s", strerror(errno));
 696                        goto err_free;
 697                }
 698
 699                obj_node->obj_id = id;
 700                obj_node->btf_id = btf_id;
 701                hash_add(tab->table, &obj_node->hash, obj_node->btf_id);
 702        }
 703
 704        return 0;
 705
 706err_free:
 707        delete_btf_table(tab);
 708        return err;
 709}
 710
 711static int
 712build_btf_tables(struct btf_attach_table *btf_prog_table,
 713                 struct btf_attach_table *btf_map_table)
 714{
 715        struct bpf_prog_info prog_info;
 716        __u32 prog_len = sizeof(prog_info);
 717        struct bpf_map_info map_info;
 718        __u32 map_len = sizeof(map_info);
 719        int err = 0;
 720
 721        err = build_btf_type_table(btf_prog_table, BPF_OBJ_PROG, &prog_info,
 722                                   &prog_len);
 723        if (err)
 724                return err;
 725
 726        err = build_btf_type_table(btf_map_table, BPF_OBJ_MAP, &map_info,
 727                                   &map_len);
 728        if (err) {
 729                delete_btf_table(btf_prog_table);
 730                return err;
 731        }
 732
 733        return 0;
 734}
 735
 736static void
 737show_btf_plain(struct bpf_btf_info *info, int fd,
 738               struct btf_attach_table *btf_prog_table,
 739               struct btf_attach_table *btf_map_table)
 740{
 741        struct btf_attach_point *obj;
 742        int n;
 743
 744        printf("%u: ", info->id);
 745        printf("size %uB", info->btf_size);
 746
 747        n = 0;
 748        hash_for_each_possible(btf_prog_table->table, obj, hash, info->id) {
 749                if (obj->btf_id == info->id)
 750                        printf("%s%u", n++ == 0 ? "  prog_ids " : ",",
 751                               obj->obj_id);
 752        }
 753
 754        n = 0;
 755        hash_for_each_possible(btf_map_table->table, obj, hash, info->id) {
 756                if (obj->btf_id == info->id)
 757                        printf("%s%u", n++ == 0 ? "  map_ids " : ",",
 758                               obj->obj_id);
 759        }
 760        emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");
 761
 762        printf("\n");
 763}
 764
 765static void
 766show_btf_json(struct bpf_btf_info *info, int fd,
 767              struct btf_attach_table *btf_prog_table,
 768              struct btf_attach_table *btf_map_table)
 769{
 770        struct btf_attach_point *obj;
 771
 772        jsonw_start_object(json_wtr);   /* btf object */
 773        jsonw_uint_field(json_wtr, "id", info->id);
 774        jsonw_uint_field(json_wtr, "size", info->btf_size);
 775
 776        jsonw_name(json_wtr, "prog_ids");
 777        jsonw_start_array(json_wtr);    /* prog_ids */
 778        hash_for_each_possible(btf_prog_table->table, obj, hash,
 779                               info->id) {
 780                if (obj->btf_id == info->id)
 781                        jsonw_uint(json_wtr, obj->obj_id);
 782        }
 783        jsonw_end_array(json_wtr);      /* prog_ids */
 784
 785        jsonw_name(json_wtr, "map_ids");
 786        jsonw_start_array(json_wtr);    /* map_ids */
 787        hash_for_each_possible(btf_map_table->table, obj, hash,
 788                               info->id) {
 789                if (obj->btf_id == info->id)
 790                        jsonw_uint(json_wtr, obj->obj_id);
 791        }
 792        jsonw_end_array(json_wtr);      /* map_ids */
 793
 794        emit_obj_refs_json(&refs_table, info->id, json_wtr); /* pids */
 795
 796        jsonw_end_object(json_wtr);     /* btf object */
 797}
 798
 799static int
 800show_btf(int fd, struct btf_attach_table *btf_prog_table,
 801         struct btf_attach_table *btf_map_table)
 802{
 803        struct bpf_btf_info info = {};
 804        __u32 len = sizeof(info);
 805        int err;
 806
 807        err = bpf_obj_get_info_by_fd(fd, &info, &len);
 808        if (err) {
 809                p_err("can't get BTF object info: %s", strerror(errno));
 810                return -1;
 811        }
 812
 813        if (json_output)
 814                show_btf_json(&info, fd, btf_prog_table, btf_map_table);
 815        else
 816                show_btf_plain(&info, fd, btf_prog_table, btf_map_table);
 817
 818        return 0;
 819}
 820
 821static int do_show(int argc, char **argv)
 822{
 823        struct btf_attach_table btf_prog_table;
 824        struct btf_attach_table btf_map_table;
 825        int err, fd = -1;
 826        __u32 id = 0;
 827
 828        if (argc == 2) {
 829                fd = btf_parse_fd(&argc, &argv);
 830                if (fd < 0)
 831                        return -1;
 832        }
 833
 834        if (argc) {
 835                if (fd >= 0)
 836                        close(fd);
 837                return BAD_ARG();
 838        }
 839
 840        hash_init(btf_prog_table.table);
 841        hash_init(btf_map_table.table);
 842        err = build_btf_tables(&btf_prog_table, &btf_map_table);
 843        if (err) {
 844                if (fd >= 0)
 845                        close(fd);
 846                return err;
 847        }
 848        build_obj_refs_table(&refs_table, BPF_OBJ_BTF);
 849
 850        if (fd >= 0) {
 851                err = show_btf(fd, &btf_prog_table, &btf_map_table);
 852                close(fd);
 853                goto exit_free;
 854        }
 855
 856        if (json_output)
 857                jsonw_start_array(json_wtr);    /* root array */
 858
 859        while (true) {
 860                err = bpf_btf_get_next_id(id, &id);
 861                if (err) {
 862                        if (errno == ENOENT) {
 863                                err = 0;
 864                                break;
 865                        }
 866                        p_err("can't get next BTF object: %s%s",
 867                              strerror(errno),
 868                              errno == EINVAL ? " -- kernel too old?" : "");
 869                        err = -1;
 870                        break;
 871                }
 872
 873                fd = bpf_btf_get_fd_by_id(id);
 874                if (fd < 0) {
 875                        if (errno == ENOENT)
 876                                continue;
 877                        p_err("can't get BTF object by id (%u): %s",
 878                              id, strerror(errno));
 879                        err = -1;
 880                        break;
 881                }
 882
 883                err = show_btf(fd, &btf_prog_table, &btf_map_table);
 884                close(fd);
 885                if (err)
 886                        break;
 887        }
 888
 889        if (json_output)
 890                jsonw_end_array(json_wtr);      /* root array */
 891
 892exit_free:
 893        delete_btf_table(&btf_prog_table);
 894        delete_btf_table(&btf_map_table);
 895        delete_obj_refs_table(&refs_table);
 896
 897        return err;
 898}
 899
 900static int do_help(int argc, char **argv)
 901{
 902        if (json_output) {
 903                jsonw_null(json_wtr);
 904                return 0;
 905        }
 906
 907        fprintf(stderr,
 908                "Usage: %1$s %2$s { show | list } [id BTF_ID]\n"
 909                "       %1$s %2$s dump BTF_SRC [format FORMAT]\n"
 910                "       %1$s %2$s help\n"
 911                "\n"
 912                "       BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
 913                "       FORMAT  := { raw | c }\n"
 914                "       " HELP_SPEC_MAP "\n"
 915                "       " HELP_SPEC_PROGRAM "\n"
 916                "       " HELP_SPEC_OPTIONS "\n"
 917                "",
 918                bin_name, "btf");
 919
 920        return 0;
 921}
 922
 923static const struct cmd cmds[] = {
 924        { "show",       do_show },
 925        { "list",       do_show },
 926        { "help",       do_help },
 927        { "dump",       do_dump },
 928        { 0 }
 929};
 930
 931int do_btf(int argc, char **argv)
 932{
 933        return cmd_select(cmds, argc, argv, do_help);
 934}
 935