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