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