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