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