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
  15#include "btf.h"
  16#include "json_writer.h"
  17#include "main.h"
  18
  19static const char * const btf_kind_str[NR_BTF_KINDS] = {
  20        [BTF_KIND_UNKN]         = "UNKNOWN",
  21        [BTF_KIND_INT]          = "INT",
  22        [BTF_KIND_PTR]          = "PTR",
  23        [BTF_KIND_ARRAY]        = "ARRAY",
  24        [BTF_KIND_STRUCT]       = "STRUCT",
  25        [BTF_KIND_UNION]        = "UNION",
  26        [BTF_KIND_ENUM]         = "ENUM",
  27        [BTF_KIND_FWD]          = "FWD",
  28        [BTF_KIND_TYPEDEF]      = "TYPEDEF",
  29        [BTF_KIND_VOLATILE]     = "VOLATILE",
  30        [BTF_KIND_CONST]        = "CONST",
  31        [BTF_KIND_RESTRICT]     = "RESTRICT",
  32        [BTF_KIND_FUNC]         = "FUNC",
  33        [BTF_KIND_FUNC_PROTO]   = "FUNC_PROTO",
  34        [BTF_KIND_VAR]          = "VAR",
  35        [BTF_KIND_DATASEC]      = "DATASEC",
  36};
  37
  38static const char *btf_int_enc_str(__u8 encoding)
  39{
  40        switch (encoding) {
  41        case 0:
  42                return "(none)";
  43        case BTF_INT_SIGNED:
  44                return "SIGNED";
  45        case BTF_INT_CHAR:
  46                return "CHAR";
  47        case BTF_INT_BOOL:
  48                return "BOOL";
  49        default:
  50                return "UNKN";
  51        }
  52}
  53
  54static const char *btf_var_linkage_str(__u32 linkage)
  55{
  56        switch (linkage) {
  57        case BTF_VAR_STATIC:
  58                return "static";
  59        case BTF_VAR_GLOBAL_ALLOCATED:
  60                return "global-alloc";
  61        default:
  62                return "(unknown)";
  63        }
  64}
  65
  66static const char *btf_str(const struct btf *btf, __u32 off)
  67{
  68        if (!off)
  69                return "(anon)";
  70        return btf__name_by_offset(btf, off) ? : "(invalid)";
  71}
  72
  73static int dump_btf_type(const struct btf *btf, __u32 id,
  74                         const struct btf_type *t)
  75{
  76        json_writer_t *w = json_wtr;
  77        int kind, safe_kind;
  78
  79        kind = BTF_INFO_KIND(t->info);
  80        safe_kind = kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN;
  81
  82        if (json_output) {
  83                jsonw_start_object(w);
  84                jsonw_uint_field(w, "id", id);
  85                jsonw_string_field(w, "kind", btf_kind_str[safe_kind]);
  86                jsonw_string_field(w, "name", btf_str(btf, t->name_off));
  87        } else {
  88                printf("[%u] %s '%s'", id, btf_kind_str[safe_kind],
  89                       btf_str(btf, t->name_off));
  90        }
  91
  92        switch (BTF_INFO_KIND(t->info)) {
  93        case BTF_KIND_INT: {
  94                __u32 v = *(__u32 *)(t + 1);
  95                const char *enc;
  96
  97                enc = btf_int_enc_str(BTF_INT_ENCODING(v));
  98
  99                if (json_output) {
 100                        jsonw_uint_field(w, "size", t->size);
 101                        jsonw_uint_field(w, "bits_offset", BTF_INT_OFFSET(v));
 102                        jsonw_uint_field(w, "nr_bits", BTF_INT_BITS(v));
 103                        jsonw_string_field(w, "encoding", enc);
 104                } else {
 105                        printf(" size=%u bits_offset=%u nr_bits=%u encoding=%s",
 106                               t->size, BTF_INT_OFFSET(v), BTF_INT_BITS(v),
 107                               enc);
 108                }
 109                break;
 110        }
 111        case BTF_KIND_PTR:
 112        case BTF_KIND_CONST:
 113        case BTF_KIND_VOLATILE:
 114        case BTF_KIND_RESTRICT:
 115        case BTF_KIND_TYPEDEF:
 116                if (json_output)
 117                        jsonw_uint_field(w, "type_id", t->type);
 118                else
 119                        printf(" type_id=%u", t->type);
 120                break;
 121        case BTF_KIND_ARRAY: {
 122                const struct btf_array *arr = (const void *)(t + 1);
 123
 124                if (json_output) {
 125                        jsonw_uint_field(w, "type_id", arr->type);
 126                        jsonw_uint_field(w, "index_type_id", arr->index_type);
 127                        jsonw_uint_field(w, "nr_elems", arr->nelems);
 128                } else {
 129                        printf(" type_id=%u index_type_id=%u nr_elems=%u",
 130                               arr->type, arr->index_type, arr->nelems);
 131                }
 132                break;
 133        }
 134        case BTF_KIND_STRUCT:
 135        case BTF_KIND_UNION: {
 136                const struct btf_member *m = (const void *)(t + 1);
 137                __u16 vlen = BTF_INFO_VLEN(t->info);
 138                int i;
 139
 140                if (json_output) {
 141                        jsonw_uint_field(w, "size", t->size);
 142                        jsonw_uint_field(w, "vlen", vlen);
 143                        jsonw_name(w, "members");
 144                        jsonw_start_array(w);
 145                } else {
 146                        printf(" size=%u vlen=%u", t->size, vlen);
 147                }
 148                for (i = 0; i < vlen; i++, m++) {
 149                        const char *name = btf_str(btf, m->name_off);
 150                        __u32 bit_off, bit_sz;
 151
 152                        if (BTF_INFO_KFLAG(t->info)) {
 153                                bit_off = BTF_MEMBER_BIT_OFFSET(m->offset);
 154                                bit_sz = BTF_MEMBER_BITFIELD_SIZE(m->offset);
 155                        } else {
 156                                bit_off = m->offset;
 157                                bit_sz = 0;
 158                        }
 159
 160                        if (json_output) {
 161                                jsonw_start_object(w);
 162                                jsonw_string_field(w, "name", name);
 163                                jsonw_uint_field(w, "type_id", m->type);
 164                                jsonw_uint_field(w, "bits_offset", bit_off);
 165                                if (bit_sz) {
 166                                        jsonw_uint_field(w, "bitfield_size",
 167                                                         bit_sz);
 168                                }
 169                                jsonw_end_object(w);
 170                        } else {
 171                                printf("\n\t'%s' type_id=%u bits_offset=%u",
 172                                       name, m->type, bit_off);
 173                                if (bit_sz)
 174                                        printf(" bitfield_size=%u", bit_sz);
 175                        }
 176                }
 177                if (json_output)
 178                        jsonw_end_array(w);
 179                break;
 180        }
 181        case BTF_KIND_ENUM: {
 182                const struct btf_enum *v = (const void *)(t + 1);
 183                __u16 vlen = BTF_INFO_VLEN(t->info);
 184                int i;
 185
 186                if (json_output) {
 187                        jsonw_uint_field(w, "size", t->size);
 188                        jsonw_uint_field(w, "vlen", vlen);
 189                        jsonw_name(w, "values");
 190                        jsonw_start_array(w);
 191                } else {
 192                        printf(" size=%u vlen=%u", t->size, vlen);
 193                }
 194                for (i = 0; i < vlen; i++, v++) {
 195                        const char *name = btf_str(btf, v->name_off);
 196
 197                        if (json_output) {
 198                                jsonw_start_object(w);
 199                                jsonw_string_field(w, "name", name);
 200                                jsonw_uint_field(w, "val", v->val);
 201                                jsonw_end_object(w);
 202                        } else {
 203                                printf("\n\t'%s' val=%u", name, v->val);
 204                        }
 205                }
 206                if (json_output)
 207                        jsonw_end_array(w);
 208                break;
 209        }
 210        case BTF_KIND_FWD: {
 211                const char *fwd_kind = BTF_INFO_KFLAG(t->info) ? "union"
 212                                                               : "struct";
 213
 214                if (json_output)
 215                        jsonw_string_field(w, "fwd_kind", fwd_kind);
 216                else
 217                        printf(" fwd_kind=%s", fwd_kind);
 218                break;
 219        }
 220        case BTF_KIND_FUNC:
 221                if (json_output)
 222                        jsonw_uint_field(w, "type_id", t->type);
 223                else
 224                        printf(" type_id=%u", t->type);
 225                break;
 226        case BTF_KIND_FUNC_PROTO: {
 227                const struct btf_param *p = (const void *)(t + 1);
 228                __u16 vlen = BTF_INFO_VLEN(t->info);
 229                int i;
 230
 231                if (json_output) {
 232                        jsonw_uint_field(w, "ret_type_id", t->type);
 233                        jsonw_uint_field(w, "vlen", vlen);
 234                        jsonw_name(w, "params");
 235                        jsonw_start_array(w);
 236                } else {
 237                        printf(" ret_type_id=%u vlen=%u", t->type, vlen);
 238                }
 239                for (i = 0; i < vlen; i++, p++) {
 240                        const char *name = btf_str(btf, p->name_off);
 241
 242                        if (json_output) {
 243                                jsonw_start_object(w);
 244                                jsonw_string_field(w, "name", name);
 245                                jsonw_uint_field(w, "type_id", p->type);
 246                                jsonw_end_object(w);
 247                        } else {
 248                                printf("\n\t'%s' type_id=%u", name, p->type);
 249                        }
 250                }
 251                if (json_output)
 252                        jsonw_end_array(w);
 253                break;
 254        }
 255        case BTF_KIND_VAR: {
 256                const struct btf_var *v = (const void *)(t + 1);
 257                const char *linkage;
 258
 259                linkage = btf_var_linkage_str(v->linkage);
 260
 261                if (json_output) {
 262                        jsonw_uint_field(w, "type_id", t->type);
 263                        jsonw_string_field(w, "linkage", linkage);
 264                } else {
 265                        printf(" type_id=%u, linkage=%s", t->type, linkage);
 266                }
 267                break;
 268        }
 269        case BTF_KIND_DATASEC: {
 270                const struct btf_var_secinfo *v = (const void *)(t+1);
 271                __u16 vlen = BTF_INFO_VLEN(t->info);
 272                int i;
 273
 274                if (json_output) {
 275                        jsonw_uint_field(w, "size", t->size);
 276                        jsonw_uint_field(w, "vlen", vlen);
 277                        jsonw_name(w, "vars");
 278                        jsonw_start_array(w);
 279                } else {
 280                        printf(" size=%u vlen=%u", t->size, vlen);
 281                }
 282                for (i = 0; i < vlen; i++, v++) {
 283                        if (json_output) {
 284                                jsonw_start_object(w);
 285                                jsonw_uint_field(w, "type_id", v->type);
 286                                jsonw_uint_field(w, "offset", v->offset);
 287                                jsonw_uint_field(w, "size", v->size);
 288                                jsonw_end_object(w);
 289                        } else {
 290                                printf("\n\ttype_id=%u offset=%u size=%u",
 291                                       v->type, v->offset, v->size);
 292                        }
 293                }
 294                if (json_output)
 295                        jsonw_end_array(w);
 296                break;
 297        }
 298        default:
 299                break;
 300        }
 301
 302        if (json_output)
 303                jsonw_end_object(json_wtr);
 304        else
 305                printf("\n");
 306
 307        return 0;
 308}
 309
 310static int dump_btf_raw(const struct btf *btf,
 311                        __u32 *root_type_ids, int root_type_cnt)
 312{
 313        const struct btf_type *t;
 314        int i;
 315
 316        if (json_output) {
 317                jsonw_start_object(json_wtr);
 318                jsonw_name(json_wtr, "types");
 319                jsonw_start_array(json_wtr);
 320        }
 321
 322        if (root_type_cnt) {
 323                for (i = 0; i < root_type_cnt; i++) {
 324                        t = btf__type_by_id(btf, root_type_ids[i]);
 325                        dump_btf_type(btf, root_type_ids[i], t);
 326                }
 327        } else {
 328                int cnt = btf__get_nr_types(btf);
 329
 330                for (i = 1; i <= cnt; i++) {
 331                        t = btf__type_by_id(btf, i);
 332                        dump_btf_type(btf, i, t);
 333                }
 334        }
 335
 336        if (json_output) {
 337                jsonw_end_array(json_wtr);
 338                jsonw_end_object(json_wtr);
 339        }
 340        return 0;
 341}
 342
 343static void __printf(2, 0) btf_dump_printf(void *ctx,
 344                                           const char *fmt, va_list args)
 345{
 346        vfprintf(stdout, fmt, args);
 347}
 348
 349static int dump_btf_c(const struct btf *btf,
 350                      __u32 *root_type_ids, int root_type_cnt)
 351{
 352        struct btf_dump *d;
 353        int err = 0, i;
 354
 355        d = btf_dump__new(btf, NULL, NULL, btf_dump_printf);
 356        if (IS_ERR(d))
 357                return PTR_ERR(d);
 358
 359        if (root_type_cnt) {
 360                for (i = 0; i < root_type_cnt; i++) {
 361                        err = btf_dump__dump_type(d, root_type_ids[i]);
 362                        if (err)
 363                                goto done;
 364                }
 365        } else {
 366                int cnt = btf__get_nr_types(btf);
 367
 368                for (i = 1; i <= cnt; i++) {
 369                        err = btf_dump__dump_type(d, i);
 370                        if (err)
 371                                goto done;
 372                }
 373        }
 374
 375done:
 376        btf_dump__free(d);
 377        return err;
 378}
 379
 380static int do_dump(int argc, char **argv)
 381{
 382        struct btf *btf = NULL;
 383        __u32 root_type_ids[2];
 384        int root_type_cnt = 0;
 385        bool dump_c = false;
 386        __u32 btf_id = -1;
 387        const char *src;
 388        int fd = -1;
 389        int err;
 390
 391        if (!REQ_ARGS(2)) {
 392                usage();
 393                return -1;
 394        }
 395        src = GET_ARG();
 396
 397        if (is_prefix(src, "map")) {
 398                struct bpf_map_info info = {};
 399                __u32 len = sizeof(info);
 400
 401                if (!REQ_ARGS(2)) {
 402                        usage();
 403                        return -1;
 404                }
 405
 406                fd = map_parse_fd_and_info(&argc, &argv, &info, &len);
 407                if (fd < 0)
 408                        return -1;
 409
 410                btf_id = info.btf_id;
 411                if (argc && is_prefix(*argv, "key")) {
 412                        root_type_ids[root_type_cnt++] = info.btf_key_type_id;
 413                        NEXT_ARG();
 414                } else if (argc && is_prefix(*argv, "value")) {
 415                        root_type_ids[root_type_cnt++] = info.btf_value_type_id;
 416                        NEXT_ARG();
 417                } else if (argc && is_prefix(*argv, "all")) {
 418                        NEXT_ARG();
 419                } else if (argc && is_prefix(*argv, "kv")) {
 420                        root_type_ids[root_type_cnt++] = info.btf_key_type_id;
 421                        root_type_ids[root_type_cnt++] = info.btf_value_type_id;
 422                        NEXT_ARG();
 423                } else {
 424                        root_type_ids[root_type_cnt++] = info.btf_key_type_id;
 425                        root_type_ids[root_type_cnt++] = info.btf_value_type_id;
 426                }
 427        } else if (is_prefix(src, "prog")) {
 428                struct bpf_prog_info info = {};
 429                __u32 len = sizeof(info);
 430
 431                if (!REQ_ARGS(2)) {
 432                        usage();
 433                        return -1;
 434                }
 435
 436                fd = prog_parse_fd(&argc, &argv);
 437                if (fd < 0)
 438                        return -1;
 439
 440                err = bpf_obj_get_info_by_fd(fd, &info, &len);
 441                if (err) {
 442                        p_err("can't get prog info: %s", strerror(errno));
 443                        goto done;
 444                }
 445
 446                btf_id = info.btf_id;
 447        } else if (is_prefix(src, "id")) {
 448                char *endptr;
 449
 450                btf_id = strtoul(*argv, &endptr, 0);
 451                if (*endptr) {
 452                        p_err("can't parse %s as ID", **argv);
 453                        return -1;
 454                }
 455                NEXT_ARG();
 456        } else if (is_prefix(src, "file")) {
 457                btf = btf__parse_elf(*argv, NULL);
 458                if (IS_ERR(btf)) {
 459                        err = PTR_ERR(btf);
 460                        btf = NULL;
 461                        p_err("failed to load BTF from %s: %s", 
 462                              *argv, strerror(err));
 463                        goto done;
 464                }
 465                NEXT_ARG();
 466        } else {
 467                err = -1;
 468                p_err("unrecognized BTF source specifier: '%s'", src);
 469                goto done;
 470        }
 471
 472        while (argc) {
 473                if (is_prefix(*argv, "format")) {
 474                        NEXT_ARG();
 475                        if (argc < 1) {
 476                                p_err("expecting value for 'format' option\n");
 477                                goto done;
 478                        }
 479                        if (strcmp(*argv, "c") == 0) {
 480                                dump_c = true;
 481                        } else if (strcmp(*argv, "raw") == 0) {
 482                                dump_c = false;
 483                        } else {
 484                                p_err("unrecognized format specifier: '%s', possible values: raw, c",
 485                                      *argv);
 486                                goto done;
 487                        }
 488                        NEXT_ARG();
 489                } else {
 490                        p_err("unrecognized option: '%s'", *argv);
 491                        goto done;
 492                }
 493        }
 494
 495        if (!btf) {
 496                err = btf__get_from_id(btf_id, &btf);
 497                if (err) {
 498                        p_err("get btf by id (%u): %s", btf_id, strerror(err));
 499                        goto done;
 500                }
 501                if (!btf) {
 502                        err = ENOENT;
 503                        p_err("can't find btf with ID (%u)", btf_id);
 504                        goto done;
 505                }
 506        }
 507
 508        if (dump_c) {
 509                if (json_output) {
 510                        p_err("JSON output for C-syntax dump is not supported");
 511                        err = -ENOTSUP;
 512                        goto done;
 513                }
 514                err = dump_btf_c(btf, root_type_ids, root_type_cnt);
 515        } else {
 516                err = dump_btf_raw(btf, root_type_ids, root_type_cnt);
 517        }
 518
 519done:
 520        close(fd);
 521        btf__free(btf);
 522        return err;
 523}
 524
 525static int do_help(int argc, char **argv)
 526{
 527        if (json_output) {
 528                jsonw_null(json_wtr);
 529                return 0;
 530        }
 531
 532        fprintf(stderr,
 533                "Usage: %s btf dump BTF_SRC [format FORMAT]\n"
 534                "       %s btf help\n"
 535                "\n"
 536                "       BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n"
 537                "       FORMAT  := { raw | c }\n"
 538                "       " HELP_SPEC_MAP "\n"
 539                "       " HELP_SPEC_PROGRAM "\n"
 540                "       " HELP_SPEC_OPTIONS "\n"
 541                "",
 542                bin_name, bin_name);
 543
 544        return 0;
 545}
 546
 547static const struct cmd cmds[] = {
 548        { "help",       do_help },
 549        { "dump",       do_dump },
 550        { 0 }
 551};
 552
 553int do_btf(int argc, char **argv)
 554{
 555        return cmd_select(cmds, argc, argv, do_help);
 556}
 557