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 20 static 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 39 struct btf_attach_table { 40 DECLARE_HASHTABLE(table, 16); 41 }; 42 43 struct btf_attach_point { 44 __u32 obj_id; 45 __u32 btf_id; 46 struct hlist_node hash; 47 }; 48 49 static 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 65 static 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 77 static 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 84 static 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 321 static 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 354 static void __printf(2, 0) btf_dump_printf(void *ctx, 355 const char *fmt, va_list args) 356 { 357 vfprintf(stdout, fmt, args); 358 } 359 360 static 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 386 done: 387 btf_dump__free(d); 388 return err; 389 } 390 391 static 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 530 done: 531 close(fd); 532 btf__free(btf); 533 return err; 534 } 535 536 static 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 563 static 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 576 static int 577 build_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 672 err_free: 673 delete_btf_table(tab); 674 return err; 675 } 676 677 static int 678 build_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 702 static void 703 show_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 730 static void 731 show_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 761 static int 762 show_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 783 static 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 853 exit_free: 854 delete_btf_table(&btf_prog_table); 855 delete_btf_table(&btf_map_table); 856 857 return err; 858 } 859 860 static 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 883 static const struct cmd cmds[] = { 884 { "show", do_show }, 885 { "list", do_show }, 886 { "help", do_help }, 887 { "dump", do_dump }, 888 { 0 } 889 }; 890 891 int do_btf(int argc, char **argv) 892 { 893 return cmd_select(cmds, argc, argv, do_help); 894 } 895