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