1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* Copyright (C) 2019 Facebook */ 3 4 #ifndef _GNU_SOURCE 5 #define _GNU_SOURCE 6 #endif 7 #include <errno.h> 8 #include <fcntl.h> 9 #include <linux/err.h> 10 #include <stdbool.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <unistd.h> 15 #include <linux/btf.h> 16 #include <sys/types.h> 17 #include <sys/stat.h> 18 19 #include <bpf/bpf.h> 20 #include <bpf/btf.h> 21 #include <bpf/hashmap.h> 22 #include <bpf/libbpf.h> 23 24 #include "json_writer.h" 25 #include "main.h" 26 27 #define KFUNC_DECL_TAG "bpf_kfunc" 28 #define FASTCALL_DECL_TAG "bpf_fastcall" 29 30 #define MAX_ROOT_IDS 16 31 32 static const char * const btf_kind_str[NR_BTF_KINDS] = { 33 [BTF_KIND_UNKN] = "UNKNOWN", 34 [BTF_KIND_INT] = "INT", 35 [BTF_KIND_PTR] = "PTR", 36 [BTF_KIND_ARRAY] = "ARRAY", 37 [BTF_KIND_STRUCT] = "STRUCT", 38 [BTF_KIND_UNION] = "UNION", 39 [BTF_KIND_ENUM] = "ENUM", 40 [BTF_KIND_FWD] = "FWD", 41 [BTF_KIND_TYPEDEF] = "TYPEDEF", 42 [BTF_KIND_VOLATILE] = "VOLATILE", 43 [BTF_KIND_CONST] = "CONST", 44 [BTF_KIND_RESTRICT] = "RESTRICT", 45 [BTF_KIND_FUNC] = "FUNC", 46 [BTF_KIND_FUNC_PROTO] = "FUNC_PROTO", 47 [BTF_KIND_VAR] = "VAR", 48 [BTF_KIND_DATASEC] = "DATASEC", 49 [BTF_KIND_FLOAT] = "FLOAT", 50 [BTF_KIND_DECL_TAG] = "DECL_TAG", 51 [BTF_KIND_TYPE_TAG] = "TYPE_TAG", 52 [BTF_KIND_ENUM64] = "ENUM64", 53 }; 54 55 struct sort_datum { 56 int index; 57 int type_rank; 58 const char *sort_name; 59 const char *own_name; 60 __u64 disambig_hash; 61 }; 62 63 static const char *btf_int_enc_str(__u8 encoding) 64 { 65 switch (encoding) { 66 case 0: 67 return "(none)"; 68 case BTF_INT_SIGNED: 69 return "SIGNED"; 70 case BTF_INT_CHAR: 71 return "CHAR"; 72 case BTF_INT_BOOL: 73 return "BOOL"; 74 default: 75 return "UNKN"; 76 } 77 } 78 79 static const char *btf_var_linkage_str(__u32 linkage) 80 { 81 switch (linkage) { 82 case BTF_VAR_STATIC: 83 return "static"; 84 case BTF_VAR_GLOBAL_ALLOCATED: 85 return "global"; 86 case BTF_VAR_GLOBAL_EXTERN: 87 return "extern"; 88 default: 89 return "(unknown)"; 90 } 91 } 92 93 static const char *btf_func_linkage_str(const struct btf_type *t) 94 { 95 switch (btf_vlen(t)) { 96 case BTF_FUNC_STATIC: 97 return "static"; 98 case BTF_FUNC_GLOBAL: 99 return "global"; 100 case BTF_FUNC_EXTERN: 101 return "extern"; 102 default: 103 return "(unknown)"; 104 } 105 } 106 107 static const char *btf_str(const struct btf *btf, __u32 off) 108 { 109 if (!off) 110 return "(anon)"; 111 return btf__name_by_offset(btf, off) ? : "(invalid)"; 112 } 113 114 static int btf_kind_safe(int kind) 115 { 116 return kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN; 117 } 118 119 static int dump_btf_type(const struct btf *btf, __u32 id, 120 const struct btf_type *t) 121 { 122 json_writer_t *w = json_wtr; 123 int kind = btf_kind(t); 124 125 if (json_output) { 126 jsonw_start_object(w); 127 jsonw_uint_field(w, "id", id); 128 jsonw_string_field(w, "kind", btf_kind_str[btf_kind_safe(kind)]); 129 jsonw_string_field(w, "name", btf_str(btf, t->name_off)); 130 } else { 131 printf("[%u] %s '%s'", id, btf_kind_str[btf_kind_safe(kind)], 132 btf_str(btf, t->name_off)); 133 } 134 135 switch (kind) { 136 case BTF_KIND_INT: { 137 __u32 v = *(__u32 *)(t + 1); 138 const char *enc; 139 140 enc = btf_int_enc_str(BTF_INT_ENCODING(v)); 141 142 if (json_output) { 143 jsonw_uint_field(w, "size", t->size); 144 jsonw_uint_field(w, "bits_offset", BTF_INT_OFFSET(v)); 145 jsonw_uint_field(w, "nr_bits", BTF_INT_BITS(v)); 146 jsonw_string_field(w, "encoding", enc); 147 } else { 148 printf(" size=%u bits_offset=%u nr_bits=%u encoding=%s", 149 t->size, BTF_INT_OFFSET(v), BTF_INT_BITS(v), 150 enc); 151 } 152 break; 153 } 154 case BTF_KIND_PTR: 155 case BTF_KIND_CONST: 156 case BTF_KIND_VOLATILE: 157 case BTF_KIND_RESTRICT: 158 case BTF_KIND_TYPEDEF: 159 case BTF_KIND_TYPE_TAG: 160 if (json_output) 161 jsonw_uint_field(w, "type_id", t->type); 162 else 163 printf(" type_id=%u", t->type); 164 break; 165 case BTF_KIND_ARRAY: { 166 const struct btf_array *arr = (const void *)(t + 1); 167 168 if (json_output) { 169 jsonw_uint_field(w, "type_id", arr->type); 170 jsonw_uint_field(w, "index_type_id", arr->index_type); 171 jsonw_uint_field(w, "nr_elems", arr->nelems); 172 } else { 173 printf(" type_id=%u index_type_id=%u nr_elems=%u", 174 arr->type, arr->index_type, arr->nelems); 175 } 176 break; 177 } 178 case BTF_KIND_STRUCT: 179 case BTF_KIND_UNION: { 180 const struct btf_member *m = (const void *)(t + 1); 181 __u16 vlen = BTF_INFO_VLEN(t->info); 182 int i; 183 184 if (json_output) { 185 jsonw_uint_field(w, "size", t->size); 186 jsonw_uint_field(w, "vlen", vlen); 187 jsonw_name(w, "members"); 188 jsonw_start_array(w); 189 } else { 190 printf(" size=%u vlen=%u", t->size, vlen); 191 } 192 for (i = 0; i < vlen; i++, m++) { 193 const char *name = btf_str(btf, m->name_off); 194 __u32 bit_off, bit_sz; 195 196 if (BTF_INFO_KFLAG(t->info)) { 197 bit_off = BTF_MEMBER_BIT_OFFSET(m->offset); 198 bit_sz = BTF_MEMBER_BITFIELD_SIZE(m->offset); 199 } else { 200 bit_off = m->offset; 201 bit_sz = 0; 202 } 203 204 if (json_output) { 205 jsonw_start_object(w); 206 jsonw_string_field(w, "name", name); 207 jsonw_uint_field(w, "type_id", m->type); 208 jsonw_uint_field(w, "bits_offset", bit_off); 209 if (bit_sz) { 210 jsonw_uint_field(w, "bitfield_size", 211 bit_sz); 212 } 213 jsonw_end_object(w); 214 } else { 215 printf("\n\t'%s' type_id=%u bits_offset=%u", 216 name, m->type, bit_off); 217 if (bit_sz) 218 printf(" bitfield_size=%u", bit_sz); 219 } 220 } 221 if (json_output) 222 jsonw_end_array(w); 223 break; 224 } 225 case BTF_KIND_ENUM: { 226 const struct btf_enum *v = (const void *)(t + 1); 227 __u16 vlen = BTF_INFO_VLEN(t->info); 228 const char *encoding; 229 int i; 230 231 encoding = btf_kflag(t) ? "SIGNED" : "UNSIGNED"; 232 if (json_output) { 233 jsonw_string_field(w, "encoding", encoding); 234 jsonw_uint_field(w, "size", t->size); 235 jsonw_uint_field(w, "vlen", vlen); 236 jsonw_name(w, "values"); 237 jsonw_start_array(w); 238 } else { 239 printf(" encoding=%s size=%u vlen=%u", encoding, t->size, vlen); 240 } 241 for (i = 0; i < vlen; i++, v++) { 242 const char *name = btf_str(btf, v->name_off); 243 244 if (json_output) { 245 jsonw_start_object(w); 246 jsonw_string_field(w, "name", name); 247 if (btf_kflag(t)) 248 jsonw_int_field(w, "val", v->val); 249 else 250 jsonw_uint_field(w, "val", v->val); 251 jsonw_end_object(w); 252 } else { 253 if (btf_kflag(t)) 254 printf("\n\t'%s' val=%d", name, v->val); 255 else 256 printf("\n\t'%s' val=%u", name, (__u32)v->val); 257 } 258 } 259 if (json_output) 260 jsonw_end_array(w); 261 break; 262 } 263 case BTF_KIND_ENUM64: { 264 const struct btf_enum64 *v = btf_enum64(t); 265 __u16 vlen = btf_vlen(t); 266 const char *encoding; 267 int i; 268 269 encoding = btf_kflag(t) ? "SIGNED" : "UNSIGNED"; 270 if (json_output) { 271 jsonw_string_field(w, "encoding", encoding); 272 jsonw_uint_field(w, "size", t->size); 273 jsonw_uint_field(w, "vlen", vlen); 274 jsonw_name(w, "values"); 275 jsonw_start_array(w); 276 } else { 277 printf(" encoding=%s size=%u vlen=%u", encoding, t->size, vlen); 278 } 279 for (i = 0; i < vlen; i++, v++) { 280 const char *name = btf_str(btf, v->name_off); 281 __u64 val = ((__u64)v->val_hi32 << 32) | v->val_lo32; 282 283 if (json_output) { 284 jsonw_start_object(w); 285 jsonw_string_field(w, "name", name); 286 if (btf_kflag(t)) 287 jsonw_int_field(w, "val", val); 288 else 289 jsonw_uint_field(w, "val", val); 290 jsonw_end_object(w); 291 } else { 292 if (btf_kflag(t)) 293 printf("\n\t'%s' val=%lldLL", name, 294 (long long)val); 295 else 296 printf("\n\t'%s' val=%lluULL", name, 297 (unsigned long long)val); 298 } 299 } 300 if (json_output) 301 jsonw_end_array(w); 302 break; 303 } 304 case BTF_KIND_FWD: { 305 const char *fwd_kind = BTF_INFO_KFLAG(t->info) ? "union" 306 : "struct"; 307 308 if (json_output) 309 jsonw_string_field(w, "fwd_kind", fwd_kind); 310 else 311 printf(" fwd_kind=%s", fwd_kind); 312 break; 313 } 314 case BTF_KIND_FUNC: { 315 const char *linkage = btf_func_linkage_str(t); 316 317 if (json_output) { 318 jsonw_uint_field(w, "type_id", t->type); 319 jsonw_string_field(w, "linkage", linkage); 320 } else { 321 printf(" type_id=%u linkage=%s", t->type, linkage); 322 } 323 break; 324 } 325 case BTF_KIND_FUNC_PROTO: { 326 const struct btf_param *p = (const void *)(t + 1); 327 __u16 vlen = BTF_INFO_VLEN(t->info); 328 int i; 329 330 if (json_output) { 331 jsonw_uint_field(w, "ret_type_id", t->type); 332 jsonw_uint_field(w, "vlen", vlen); 333 jsonw_name(w, "params"); 334 jsonw_start_array(w); 335 } else { 336 printf(" ret_type_id=%u vlen=%u", t->type, vlen); 337 } 338 for (i = 0; i < vlen; i++, p++) { 339 const char *name = btf_str(btf, p->name_off); 340 341 if (json_output) { 342 jsonw_start_object(w); 343 jsonw_string_field(w, "name", name); 344 jsonw_uint_field(w, "type_id", p->type); 345 jsonw_end_object(w); 346 } else { 347 printf("\n\t'%s' type_id=%u", name, p->type); 348 } 349 } 350 if (json_output) 351 jsonw_end_array(w); 352 break; 353 } 354 case BTF_KIND_VAR: { 355 const struct btf_var *v = (const void *)(t + 1); 356 const char *linkage; 357 358 linkage = btf_var_linkage_str(v->linkage); 359 360 if (json_output) { 361 jsonw_uint_field(w, "type_id", t->type); 362 jsonw_string_field(w, "linkage", linkage); 363 } else { 364 printf(" type_id=%u, linkage=%s", t->type, linkage); 365 } 366 break; 367 } 368 case BTF_KIND_DATASEC: { 369 const struct btf_var_secinfo *v = (const void *)(t + 1); 370 const struct btf_type *vt; 371 __u16 vlen = BTF_INFO_VLEN(t->info); 372 int i; 373 374 if (json_output) { 375 jsonw_uint_field(w, "size", t->size); 376 jsonw_uint_field(w, "vlen", vlen); 377 jsonw_name(w, "vars"); 378 jsonw_start_array(w); 379 } else { 380 printf(" size=%u vlen=%u", t->size, vlen); 381 } 382 for (i = 0; i < vlen; i++, v++) { 383 if (json_output) { 384 jsonw_start_object(w); 385 jsonw_uint_field(w, "type_id", v->type); 386 jsonw_uint_field(w, "offset", v->offset); 387 jsonw_uint_field(w, "size", v->size); 388 jsonw_end_object(w); 389 } else { 390 printf("\n\ttype_id=%u offset=%u size=%u", 391 v->type, v->offset, v->size); 392 393 if (v->type < btf__type_cnt(btf)) { 394 vt = btf__type_by_id(btf, v->type); 395 printf(" (%s '%s')", 396 btf_kind_str[btf_kind_safe(btf_kind(vt))], 397 btf_str(btf, vt->name_off)); 398 } 399 } 400 } 401 if (json_output) 402 jsonw_end_array(w); 403 break; 404 } 405 case BTF_KIND_FLOAT: { 406 if (json_output) 407 jsonw_uint_field(w, "size", t->size); 408 else 409 printf(" size=%u", t->size); 410 break; 411 } 412 case BTF_KIND_DECL_TAG: { 413 const struct btf_decl_tag *tag = (const void *)(t + 1); 414 415 if (json_output) { 416 jsonw_uint_field(w, "type_id", t->type); 417 jsonw_int_field(w, "component_idx", tag->component_idx); 418 } else { 419 printf(" type_id=%u component_idx=%d", t->type, tag->component_idx); 420 } 421 break; 422 } 423 default: 424 break; 425 } 426 427 if (json_output) 428 jsonw_end_object(json_wtr); 429 else 430 printf("\n"); 431 432 return 0; 433 } 434 435 static int dump_btf_raw(const struct btf *btf, 436 __u32 *root_type_ids, int root_type_cnt) 437 { 438 const struct btf_type *t; 439 int i; 440 441 if (json_output) { 442 jsonw_start_object(json_wtr); 443 jsonw_name(json_wtr, "types"); 444 jsonw_start_array(json_wtr); 445 } 446 447 if (root_type_cnt) { 448 for (i = 0; i < root_type_cnt; i++) { 449 t = btf__type_by_id(btf, root_type_ids[i]); 450 dump_btf_type(btf, root_type_ids[i], t); 451 } 452 } else { 453 const struct btf *base; 454 int cnt = btf__type_cnt(btf); 455 int start_id = 1; 456 457 base = btf__base_btf(btf); 458 if (base) 459 start_id = btf__type_cnt(base); 460 461 for (i = start_id; i < cnt; i++) { 462 t = btf__type_by_id(btf, i); 463 dump_btf_type(btf, i, t); 464 } 465 } 466 467 if (json_output) { 468 jsonw_end_array(json_wtr); 469 jsonw_end_object(json_wtr); 470 } 471 return 0; 472 } 473 474 struct ptr_array { 475 __u32 cnt; 476 __u32 cap; 477 const void **elems; 478 }; 479 480 static int ptr_array_push(const void *ptr, struct ptr_array *arr) 481 { 482 __u32 new_cap; 483 void *tmp; 484 485 if (arr->cnt == arr->cap) { 486 new_cap = (arr->cap ?: 16) * 2; 487 tmp = realloc(arr->elems, sizeof(*arr->elems) * new_cap); 488 if (!tmp) 489 return -ENOMEM; 490 arr->elems = tmp; 491 arr->cap = new_cap; 492 } 493 arr->elems[arr->cnt++] = ptr; 494 return 0; 495 } 496 497 static void ptr_array_free(struct ptr_array *arr) 498 { 499 free(arr->elems); 500 } 501 502 static int cmp_kfuncs(const void *pa, const void *pb, void *ctx) 503 { 504 struct btf *btf = ctx; 505 const struct btf_type *a = *(void **)pa; 506 const struct btf_type *b = *(void **)pb; 507 508 return strcmp(btf__str_by_offset(btf, a->name_off), 509 btf__str_by_offset(btf, b->name_off)); 510 } 511 512 static int dump_btf_kfuncs(struct btf_dump *d, const struct btf *btf) 513 { 514 LIBBPF_OPTS(btf_dump_emit_type_decl_opts, opts); 515 __u32 cnt = btf__type_cnt(btf), i, j; 516 struct ptr_array fastcalls = {}; 517 struct ptr_array kfuncs = {}; 518 int err = 0; 519 520 printf("\n/* BPF kfuncs */\n"); 521 printf("#ifndef BPF_NO_KFUNC_PROTOTYPES\n"); 522 523 for (i = 1; i < cnt; i++) { 524 const struct btf_type *t = btf__type_by_id(btf, i); 525 const struct btf_type *ft; 526 const char *name; 527 528 if (!btf_is_decl_tag(t)) 529 continue; 530 531 if (btf_decl_tag(t)->component_idx != -1) 532 continue; 533 534 ft = btf__type_by_id(btf, t->type); 535 if (!btf_is_func(ft)) 536 continue; 537 538 name = btf__name_by_offset(btf, t->name_off); 539 if (strncmp(name, KFUNC_DECL_TAG, sizeof(KFUNC_DECL_TAG)) == 0) { 540 err = ptr_array_push(ft, &kfuncs); 541 if (err) 542 goto out; 543 } 544 545 if (strncmp(name, FASTCALL_DECL_TAG, sizeof(FASTCALL_DECL_TAG)) == 0) { 546 err = ptr_array_push(ft, &fastcalls); 547 if (err) 548 goto out; 549 } 550 } 551 552 /* Sort kfuncs by name for improved vmlinux.h stability */ 553 qsort_r(kfuncs.elems, kfuncs.cnt, sizeof(*kfuncs.elems), cmp_kfuncs, (void *)btf); 554 for (i = 0; i < kfuncs.cnt; i++) { 555 const struct btf_type *t = kfuncs.elems[i]; 556 557 printf("extern "); 558 559 /* Assume small amount of fastcall kfuncs */ 560 for (j = 0; j < fastcalls.cnt; j++) { 561 if (fastcalls.elems[j] == t) { 562 printf("__bpf_fastcall "); 563 break; 564 } 565 } 566 567 opts.field_name = btf__name_by_offset(btf, t->name_off); 568 err = btf_dump__emit_type_decl(d, t->type, &opts); 569 if (err) 570 goto out; 571 572 printf(" __weak __ksym;\n"); 573 } 574 575 printf("#endif\n\n"); 576 577 out: 578 ptr_array_free(&fastcalls); 579 ptr_array_free(&kfuncs); 580 return err; 581 } 582 583 static void __printf(2, 0) btf_dump_printf(void *ctx, 584 const char *fmt, va_list args) 585 { 586 vfprintf(stdout, fmt, args); 587 } 588 589 static int btf_type_rank(const struct btf *btf, __u32 index, bool has_name) 590 { 591 const struct btf_type *t = btf__type_by_id(btf, index); 592 const int kind = btf_kind(t); 593 const int max_rank = 10; 594 595 if (t->name_off) 596 has_name = true; 597 598 switch (kind) { 599 case BTF_KIND_ENUM: 600 case BTF_KIND_ENUM64: 601 return has_name ? 1 : 0; 602 case BTF_KIND_INT: 603 case BTF_KIND_FLOAT: 604 return 2; 605 case BTF_KIND_STRUCT: 606 case BTF_KIND_UNION: 607 return has_name ? 3 : max_rank; 608 case BTF_KIND_FUNC_PROTO: 609 return has_name ? 4 : max_rank; 610 case BTF_KIND_ARRAY: 611 if (has_name) 612 return btf_type_rank(btf, btf_array(t)->type, has_name); 613 return max_rank; 614 case BTF_KIND_TYPE_TAG: 615 case BTF_KIND_CONST: 616 case BTF_KIND_PTR: 617 case BTF_KIND_VOLATILE: 618 case BTF_KIND_RESTRICT: 619 case BTF_KIND_TYPEDEF: 620 case BTF_KIND_DECL_TAG: 621 if (has_name) 622 return btf_type_rank(btf, t->type, has_name); 623 return max_rank; 624 default: 625 return max_rank; 626 } 627 } 628 629 static const char *btf_type_sort_name(const struct btf *btf, __u32 index, bool from_ref) 630 { 631 const struct btf_type *t = btf__type_by_id(btf, index); 632 633 switch (btf_kind(t)) { 634 case BTF_KIND_ENUM: 635 case BTF_KIND_ENUM64: { 636 int name_off = t->name_off; 637 638 if (!from_ref && !name_off && btf_vlen(t)) 639 name_off = btf_kind(t) == BTF_KIND_ENUM64 ? 640 btf_enum64(t)->name_off : 641 btf_enum(t)->name_off; 642 643 return btf__name_by_offset(btf, name_off); 644 } 645 case BTF_KIND_ARRAY: 646 return btf_type_sort_name(btf, btf_array(t)->type, true); 647 case BTF_KIND_TYPE_TAG: 648 case BTF_KIND_CONST: 649 case BTF_KIND_PTR: 650 case BTF_KIND_VOLATILE: 651 case BTF_KIND_RESTRICT: 652 case BTF_KIND_TYPEDEF: 653 case BTF_KIND_DECL_TAG: 654 return btf_type_sort_name(btf, t->type, true); 655 default: 656 return btf__name_by_offset(btf, t->name_off); 657 } 658 return NULL; 659 } 660 661 static __u64 hasher(__u64 hash, __u64 val) 662 { 663 return hash * 31 + val; 664 } 665 666 static __u64 btf_name_hasher(__u64 hash, const struct btf *btf, __u32 name_off) 667 { 668 if (!name_off) 669 return hash; 670 671 return hasher(hash, str_hash(btf__name_by_offset(btf, name_off))); 672 } 673 674 static __u64 btf_type_disambig_hash(const struct btf *btf, __u32 id, bool include_members) 675 { 676 const struct btf_type *t = btf__type_by_id(btf, id); 677 int i; 678 size_t hash = 0; 679 680 hash = btf_name_hasher(hash, btf, t->name_off); 681 682 switch (btf_kind(t)) { 683 case BTF_KIND_ENUM: 684 case BTF_KIND_ENUM64: 685 for (i = 0; i < btf_vlen(t); i++) { 686 __u32 name_off = btf_is_enum(t) ? 687 btf_enum(t)[i].name_off : 688 btf_enum64(t)[i].name_off; 689 690 hash = btf_name_hasher(hash, btf, name_off); 691 } 692 break; 693 case BTF_KIND_STRUCT: 694 case BTF_KIND_UNION: 695 if (!include_members) 696 break; 697 for (i = 0; i < btf_vlen(t); i++) { 698 const struct btf_member *m = btf_members(t) + i; 699 700 hash = btf_name_hasher(hash, btf, m->name_off); 701 /* resolve field type's name and hash it as well */ 702 hash = hasher(hash, btf_type_disambig_hash(btf, m->type, false)); 703 } 704 break; 705 case BTF_KIND_TYPE_TAG: 706 case BTF_KIND_CONST: 707 case BTF_KIND_PTR: 708 case BTF_KIND_VOLATILE: 709 case BTF_KIND_RESTRICT: 710 case BTF_KIND_TYPEDEF: 711 case BTF_KIND_DECL_TAG: 712 hash = hasher(hash, btf_type_disambig_hash(btf, t->type, include_members)); 713 break; 714 case BTF_KIND_ARRAY: { 715 struct btf_array *arr = btf_array(t); 716 717 hash = hasher(hash, arr->nelems); 718 hash = hasher(hash, btf_type_disambig_hash(btf, arr->type, include_members)); 719 break; 720 } 721 default: 722 break; 723 } 724 return hash; 725 } 726 727 static int btf_type_compare(const void *left, const void *right) 728 { 729 const struct sort_datum *d1 = (const struct sort_datum *)left; 730 const struct sort_datum *d2 = (const struct sort_datum *)right; 731 int r; 732 733 r = d1->type_rank - d2->type_rank; 734 r = r ?: strcmp(d1->sort_name, d2->sort_name); 735 r = r ?: strcmp(d1->own_name, d2->own_name); 736 if (r) 737 return r; 738 739 if (d1->disambig_hash != d2->disambig_hash) 740 return d1->disambig_hash < d2->disambig_hash ? -1 : 1; 741 742 return d1->index - d2->index; 743 } 744 745 static struct sort_datum *sort_btf_c(const struct btf *btf) 746 { 747 struct sort_datum *datums; 748 int n; 749 750 n = btf__type_cnt(btf); 751 datums = malloc(sizeof(struct sort_datum) * n); 752 if (!datums) 753 return NULL; 754 755 for (int i = 0; i < n; ++i) { 756 struct sort_datum *d = datums + i; 757 const struct btf_type *t = btf__type_by_id(btf, i); 758 759 d->index = i; 760 d->type_rank = btf_type_rank(btf, i, false); 761 d->sort_name = btf_type_sort_name(btf, i, false); 762 d->own_name = btf__name_by_offset(btf, t->name_off); 763 d->disambig_hash = btf_type_disambig_hash(btf, i, true); 764 } 765 766 qsort(datums, n, sizeof(struct sort_datum), btf_type_compare); 767 768 return datums; 769 } 770 771 static int dump_btf_c(const struct btf *btf, 772 __u32 *root_type_ids, int root_type_cnt, bool sort_dump) 773 { 774 struct sort_datum *datums = NULL; 775 struct btf_dump *d; 776 int err = 0, i; 777 778 d = btf_dump__new(btf, btf_dump_printf, NULL, NULL); 779 if (!d) 780 return -errno; 781 782 printf("#ifndef __VMLINUX_H__\n"); 783 printf("#define __VMLINUX_H__\n"); 784 printf("\n"); 785 printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n"); 786 printf("#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)\n"); 787 printf("#endif\n\n"); 788 printf("#ifndef __ksym\n"); 789 printf("#define __ksym __attribute__((section(\".ksyms\")))\n"); 790 printf("#endif\n\n"); 791 printf("#ifndef __weak\n"); 792 printf("#define __weak __attribute__((weak))\n"); 793 printf("#endif\n\n"); 794 printf("#ifndef __bpf_fastcall\n"); 795 printf("#if __has_attribute(bpf_fastcall)\n"); 796 printf("#define __bpf_fastcall __attribute__((bpf_fastcall))\n"); 797 printf("#else\n"); 798 printf("#define __bpf_fastcall\n"); 799 printf("#endif\n"); 800 printf("#endif\n\n"); 801 802 if (root_type_cnt) { 803 for (i = 0; i < root_type_cnt; i++) { 804 err = btf_dump__dump_type(d, root_type_ids[i]); 805 if (err) 806 goto done; 807 } 808 } else { 809 int cnt = btf__type_cnt(btf); 810 811 if (sort_dump) 812 datums = sort_btf_c(btf); 813 for (i = 1; i < cnt; i++) { 814 int idx = datums ? datums[i].index : i; 815 816 err = btf_dump__dump_type(d, idx); 817 if (err) 818 goto done; 819 } 820 821 err = dump_btf_kfuncs(d, btf); 822 if (err) 823 goto done; 824 } 825 826 printf("#ifndef BPF_NO_PRESERVE_ACCESS_INDEX\n"); 827 printf("#pragma clang attribute pop\n"); 828 printf("#endif\n"); 829 printf("\n"); 830 printf("#endif /* __VMLINUX_H__ */\n"); 831 832 done: 833 free(datums); 834 btf_dump__free(d); 835 return err; 836 } 837 838 static const char sysfs_vmlinux[] = "/sys/kernel/btf/vmlinux"; 839 840 static struct btf *get_vmlinux_btf_from_sysfs(void) 841 { 842 struct btf *base; 843 844 base = btf__parse(sysfs_vmlinux, NULL); 845 if (!base) 846 p_err("failed to parse vmlinux BTF at '%s': %d\n", 847 sysfs_vmlinux, -errno); 848 849 return base; 850 } 851 852 #define BTF_NAME_BUFF_LEN 64 853 854 static bool btf_is_kernel_module(__u32 btf_id) 855 { 856 struct bpf_btf_info btf_info = {}; 857 char btf_name[BTF_NAME_BUFF_LEN]; 858 int btf_fd; 859 __u32 len; 860 int err; 861 862 btf_fd = bpf_btf_get_fd_by_id(btf_id); 863 if (btf_fd < 0) { 864 p_err("can't get BTF object by id (%u): %s", btf_id, strerror(errno)); 865 return false; 866 } 867 868 len = sizeof(btf_info); 869 btf_info.name = ptr_to_u64(btf_name); 870 btf_info.name_len = sizeof(btf_name); 871 err = bpf_btf_get_info_by_fd(btf_fd, &btf_info, &len); 872 close(btf_fd); 873 if (err) { 874 p_err("can't get BTF (ID %u) object info: %s", btf_id, strerror(errno)); 875 return false; 876 } 877 878 return btf_info.kernel_btf && strncmp(btf_name, "vmlinux", sizeof(btf_name)) != 0; 879 } 880 881 static int do_dump(int argc, char **argv) 882 { 883 bool dump_c = false, sort_dump_c = true; 884 struct btf *btf = NULL, *base = NULL; 885 __u32 root_type_ids[MAX_ROOT_IDS]; 886 bool have_id_filtering; 887 int root_type_cnt = 0; 888 __u32 btf_id = -1; 889 const char *src; 890 int fd = -1; 891 int err = 0; 892 int i; 893 894 if (!REQ_ARGS(2)) { 895 usage(); 896 return -1; 897 } 898 src = GET_ARG(); 899 if (is_prefix(src, "map")) { 900 struct bpf_map_info info = {}; 901 __u32 len = sizeof(info); 902 903 if (!REQ_ARGS(2)) { 904 usage(); 905 return -1; 906 } 907 908 fd = map_parse_fd_and_info(&argc, &argv, &info, &len, 909 BPF_F_RDONLY); 910 if (fd < 0) 911 return -1; 912 913 btf_id = info.btf_id; 914 if (argc && is_prefix(*argv, "key")) { 915 root_type_ids[root_type_cnt++] = info.btf_key_type_id; 916 NEXT_ARG(); 917 } else if (argc && is_prefix(*argv, "value")) { 918 root_type_ids[root_type_cnt++] = info.btf_value_type_id; 919 NEXT_ARG(); 920 } else if (argc && is_prefix(*argv, "all")) { 921 NEXT_ARG(); 922 } else if (argc && is_prefix(*argv, "kv")) { 923 root_type_ids[root_type_cnt++] = info.btf_key_type_id; 924 root_type_ids[root_type_cnt++] = info.btf_value_type_id; 925 NEXT_ARG(); 926 } else { 927 root_type_ids[root_type_cnt++] = info.btf_key_type_id; 928 root_type_ids[root_type_cnt++] = info.btf_value_type_id; 929 } 930 } else if (is_prefix(src, "prog")) { 931 struct bpf_prog_info info = {}; 932 __u32 len = sizeof(info); 933 934 if (!REQ_ARGS(2)) { 935 usage(); 936 return -1; 937 } 938 939 fd = prog_parse_fd(&argc, &argv); 940 if (fd < 0) 941 return -1; 942 943 err = bpf_prog_get_info_by_fd(fd, &info, &len); 944 if (err) { 945 p_err("can't get prog info: %s", strerror(errno)); 946 goto done; 947 } 948 949 btf_id = info.btf_id; 950 } else if (is_prefix(src, "id")) { 951 char *endptr; 952 953 btf_id = strtoul(*argv, &endptr, 0); 954 if (*endptr) { 955 p_err("can't parse %s as ID", *argv); 956 return -1; 957 } 958 NEXT_ARG(); 959 } else if (is_prefix(src, "file")) { 960 const char sysfs_prefix[] = "/sys/kernel/btf/"; 961 962 if (!base_btf && 963 strncmp(*argv, sysfs_prefix, sizeof(sysfs_prefix) - 1) == 0 && 964 strcmp(*argv, sysfs_vmlinux) != 0) 965 base = get_vmlinux_btf_from_sysfs(); 966 967 btf = btf__parse_split(*argv, base ?: base_btf); 968 if (!btf) { 969 err = -errno; 970 p_err("failed to load BTF from %s: %s", 971 *argv, strerror(errno)); 972 goto done; 973 } 974 NEXT_ARG(); 975 } else { 976 err = -1; 977 p_err("unrecognized BTF source specifier: '%s'", src); 978 goto done; 979 } 980 981 have_id_filtering = !!root_type_cnt; 982 983 while (argc) { 984 if (is_prefix(*argv, "format")) { 985 NEXT_ARG(); 986 if (argc < 1) { 987 p_err("expecting value for 'format' option\n"); 988 err = -EINVAL; 989 goto done; 990 } 991 if (strcmp(*argv, "c") == 0) { 992 dump_c = true; 993 } else if (strcmp(*argv, "raw") == 0) { 994 dump_c = false; 995 } else { 996 p_err("unrecognized format specifier: '%s', possible values: raw, c", 997 *argv); 998 err = -EINVAL; 999 goto done; 1000 } 1001 NEXT_ARG(); 1002 } else if (is_prefix(*argv, "root_id")) { 1003 __u32 root_id; 1004 char *end; 1005 1006 if (have_id_filtering) { 1007 p_err("cannot use root_id with other type filtering"); 1008 err = -EINVAL; 1009 goto done; 1010 } else if (root_type_cnt == MAX_ROOT_IDS) { 1011 p_err("only %d root_id are supported", MAX_ROOT_IDS); 1012 err = -E2BIG; 1013 goto done; 1014 } 1015 1016 NEXT_ARG(); 1017 root_id = strtoul(*argv, &end, 0); 1018 if (*end) { 1019 err = -1; 1020 p_err("can't parse %s as root ID", *argv); 1021 goto done; 1022 } 1023 for (i = 0; i < root_type_cnt; i++) { 1024 if (root_type_ids[i] == root_id) { 1025 err = -EINVAL; 1026 p_err("duplicate root_id %u supplied", root_id); 1027 goto done; 1028 } 1029 } 1030 root_type_ids[root_type_cnt++] = root_id; 1031 NEXT_ARG(); 1032 } else if (is_prefix(*argv, "unsorted")) { 1033 sort_dump_c = false; 1034 NEXT_ARG(); 1035 } else { 1036 p_err("unrecognized option: '%s'", *argv); 1037 err = -EINVAL; 1038 goto done; 1039 } 1040 } 1041 1042 if (!btf) { 1043 if (!base_btf && btf_is_kernel_module(btf_id)) { 1044 p_info("Warning: valid base BTF was not specified with -B option, falling back to standard base BTF (%s)", 1045 sysfs_vmlinux); 1046 base_btf = get_vmlinux_btf_from_sysfs(); 1047 } 1048 1049 btf = btf__load_from_kernel_by_id_split(btf_id, base_btf); 1050 if (!btf) { 1051 err = -errno; 1052 p_err("get btf by id (%u): %s", btf_id, strerror(errno)); 1053 goto done; 1054 } 1055 } 1056 1057 /* Invalid root IDs causes half emitted boilerplate and then unclean 1058 * exit. It's an ugly user experience, so handle common error here. 1059 */ 1060 for (i = 0; i < root_type_cnt; i++) { 1061 if (root_type_ids[i] >= btf__type_cnt(btf)) { 1062 err = -EINVAL; 1063 p_err("invalid root ID: %u", root_type_ids[i]); 1064 goto done; 1065 } 1066 } 1067 1068 if (dump_c) { 1069 if (json_output) { 1070 p_err("JSON output for C-syntax dump is not supported"); 1071 err = -ENOTSUP; 1072 goto done; 1073 } 1074 err = dump_btf_c(btf, root_type_ids, root_type_cnt, sort_dump_c); 1075 } else { 1076 err = dump_btf_raw(btf, root_type_ids, root_type_cnt); 1077 } 1078 1079 done: 1080 close(fd); 1081 btf__free(btf); 1082 btf__free(base); 1083 return err; 1084 } 1085 1086 static int btf_parse_fd(int *argc, char ***argv) 1087 { 1088 unsigned int id; 1089 char *endptr; 1090 int fd; 1091 1092 if (!is_prefix(*argv[0], "id")) { 1093 p_err("expected 'id', got: '%s'?", **argv); 1094 return -1; 1095 } 1096 NEXT_ARGP(); 1097 1098 id = strtoul(**argv, &endptr, 0); 1099 if (*endptr) { 1100 p_err("can't parse %s as ID", **argv); 1101 return -1; 1102 } 1103 NEXT_ARGP(); 1104 1105 fd = bpf_btf_get_fd_by_id(id); 1106 if (fd < 0) 1107 p_err("can't get BTF object by id (%u): %s", 1108 id, strerror(errno)); 1109 1110 return fd; 1111 } 1112 1113 static int 1114 build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type, 1115 void *info, __u32 *len) 1116 { 1117 static const char * const names[] = { 1118 [BPF_OBJ_UNKNOWN] = "unknown", 1119 [BPF_OBJ_PROG] = "prog", 1120 [BPF_OBJ_MAP] = "map", 1121 }; 1122 LIBBPF_OPTS(bpf_get_fd_by_id_opts, opts_ro); 1123 __u32 btf_id, id = 0; 1124 int err; 1125 int fd; 1126 1127 opts_ro.open_flags = BPF_F_RDONLY; 1128 1129 while (true) { 1130 switch (type) { 1131 case BPF_OBJ_PROG: 1132 err = bpf_prog_get_next_id(id, &id); 1133 break; 1134 case BPF_OBJ_MAP: 1135 err = bpf_map_get_next_id(id, &id); 1136 break; 1137 default: 1138 err = -1; 1139 p_err("unexpected object type: %u", type); 1140 goto err_free; 1141 } 1142 if (err) { 1143 if (errno == ENOENT) { 1144 err = 0; 1145 break; 1146 } 1147 p_err("can't get next %s: %s%s", names[type], 1148 strerror(errno), 1149 errno == EINVAL ? " -- kernel too old?" : ""); 1150 goto err_free; 1151 } 1152 1153 switch (type) { 1154 case BPF_OBJ_PROG: 1155 fd = bpf_prog_get_fd_by_id(id); 1156 break; 1157 case BPF_OBJ_MAP: 1158 fd = bpf_map_get_fd_by_id_opts(id, &opts_ro); 1159 break; 1160 default: 1161 err = -1; 1162 p_err("unexpected object type: %u", type); 1163 goto err_free; 1164 } 1165 if (fd < 0) { 1166 if (errno == ENOENT) 1167 continue; 1168 p_err("can't get %s by id (%u): %s", names[type], id, 1169 strerror(errno)); 1170 err = -1; 1171 goto err_free; 1172 } 1173 1174 memset(info, 0, *len); 1175 if (type == BPF_OBJ_PROG) 1176 err = bpf_prog_get_info_by_fd(fd, info, len); 1177 else 1178 err = bpf_map_get_info_by_fd(fd, info, len); 1179 close(fd); 1180 if (err) { 1181 p_err("can't get %s info: %s", names[type], 1182 strerror(errno)); 1183 goto err_free; 1184 } 1185 1186 switch (type) { 1187 case BPF_OBJ_PROG: 1188 btf_id = ((struct bpf_prog_info *)info)->btf_id; 1189 break; 1190 case BPF_OBJ_MAP: 1191 btf_id = ((struct bpf_map_info *)info)->btf_id; 1192 break; 1193 default: 1194 err = -1; 1195 p_err("unexpected object type: %u", type); 1196 goto err_free; 1197 } 1198 if (!btf_id) 1199 continue; 1200 1201 err = hashmap__append(tab, btf_id, id); 1202 if (err) { 1203 p_err("failed to append entry to hashmap for BTF ID %u, object ID %u: %s", 1204 btf_id, id, strerror(-err)); 1205 goto err_free; 1206 } 1207 } 1208 1209 return 0; 1210 1211 err_free: 1212 hashmap__free(tab); 1213 return err; 1214 } 1215 1216 static int 1217 build_btf_tables(struct hashmap *btf_prog_table, 1218 struct hashmap *btf_map_table) 1219 { 1220 struct bpf_prog_info prog_info; 1221 __u32 prog_len = sizeof(prog_info); 1222 struct bpf_map_info map_info; 1223 __u32 map_len = sizeof(map_info); 1224 int err = 0; 1225 1226 err = build_btf_type_table(btf_prog_table, BPF_OBJ_PROG, &prog_info, 1227 &prog_len); 1228 if (err) 1229 return err; 1230 1231 err = build_btf_type_table(btf_map_table, BPF_OBJ_MAP, &map_info, 1232 &map_len); 1233 if (err) { 1234 hashmap__free(btf_prog_table); 1235 return err; 1236 } 1237 1238 return 0; 1239 } 1240 1241 static void 1242 show_btf_plain(struct bpf_btf_info *info, int fd, 1243 struct hashmap *btf_prog_table, 1244 struct hashmap *btf_map_table) 1245 { 1246 struct hashmap_entry *entry; 1247 const char *name = u64_to_ptr(info->name); 1248 int n; 1249 1250 printf("%u: ", info->id); 1251 if (info->kernel_btf) 1252 printf("name [%s] ", name); 1253 else if (name && name[0]) 1254 printf("name %s ", name); 1255 else 1256 printf("name <anon> "); 1257 printf("size %uB", info->btf_size); 1258 1259 n = 0; 1260 hashmap__for_each_key_entry(btf_prog_table, entry, info->id) { 1261 printf("%s%lu", n++ == 0 ? " prog_ids " : ",", (unsigned long)entry->value); 1262 } 1263 1264 n = 0; 1265 hashmap__for_each_key_entry(btf_map_table, entry, info->id) { 1266 printf("%s%lu", n++ == 0 ? " map_ids " : ",", (unsigned long)entry->value); 1267 } 1268 1269 emit_obj_refs_plain(refs_table, info->id, "\n\tpids "); 1270 1271 printf("\n"); 1272 } 1273 1274 static void 1275 show_btf_json(struct bpf_btf_info *info, int fd, 1276 struct hashmap *btf_prog_table, 1277 struct hashmap *btf_map_table) 1278 { 1279 struct hashmap_entry *entry; 1280 const char *name = u64_to_ptr(info->name); 1281 1282 jsonw_start_object(json_wtr); /* btf object */ 1283 jsonw_uint_field(json_wtr, "id", info->id); 1284 jsonw_uint_field(json_wtr, "size", info->btf_size); 1285 1286 jsonw_name(json_wtr, "prog_ids"); 1287 jsonw_start_array(json_wtr); /* prog_ids */ 1288 hashmap__for_each_key_entry(btf_prog_table, entry, info->id) { 1289 jsonw_uint(json_wtr, entry->value); 1290 } 1291 jsonw_end_array(json_wtr); /* prog_ids */ 1292 1293 jsonw_name(json_wtr, "map_ids"); 1294 jsonw_start_array(json_wtr); /* map_ids */ 1295 hashmap__for_each_key_entry(btf_map_table, entry, info->id) { 1296 jsonw_uint(json_wtr, entry->value); 1297 } 1298 jsonw_end_array(json_wtr); /* map_ids */ 1299 1300 emit_obj_refs_json(refs_table, info->id, json_wtr); /* pids */ 1301 1302 jsonw_bool_field(json_wtr, "kernel", info->kernel_btf); 1303 1304 if (name && name[0]) 1305 jsonw_string_field(json_wtr, "name", name); 1306 1307 jsonw_end_object(json_wtr); /* btf object */ 1308 } 1309 1310 static int 1311 show_btf(int fd, struct hashmap *btf_prog_table, 1312 struct hashmap *btf_map_table) 1313 { 1314 struct bpf_btf_info info; 1315 __u32 len = sizeof(info); 1316 char name[64]; 1317 int err; 1318 1319 memset(&info, 0, sizeof(info)); 1320 err = bpf_btf_get_info_by_fd(fd, &info, &len); 1321 if (err) { 1322 p_err("can't get BTF object info: %s", strerror(errno)); 1323 return -1; 1324 } 1325 /* if kernel support emitting BTF object name, pass name pointer */ 1326 if (info.name_len) { 1327 memset(&info, 0, sizeof(info)); 1328 info.name_len = sizeof(name); 1329 info.name = ptr_to_u64(name); 1330 len = sizeof(info); 1331 1332 err = bpf_btf_get_info_by_fd(fd, &info, &len); 1333 if (err) { 1334 p_err("can't get BTF object info: %s", strerror(errno)); 1335 return -1; 1336 } 1337 } 1338 1339 if (json_output) 1340 show_btf_json(&info, fd, btf_prog_table, btf_map_table); 1341 else 1342 show_btf_plain(&info, fd, btf_prog_table, btf_map_table); 1343 1344 return 0; 1345 } 1346 1347 static int do_show(int argc, char **argv) 1348 { 1349 struct hashmap *btf_prog_table; 1350 struct hashmap *btf_map_table; 1351 int err, fd = -1; 1352 __u32 id = 0; 1353 1354 if (argc == 2) { 1355 fd = btf_parse_fd(&argc, &argv); 1356 if (fd < 0) 1357 return -1; 1358 } 1359 1360 if (argc) { 1361 if (fd >= 0) 1362 close(fd); 1363 return BAD_ARG(); 1364 } 1365 1366 btf_prog_table = hashmap__new(hash_fn_for_key_as_id, 1367 equal_fn_for_key_as_id, NULL); 1368 btf_map_table = hashmap__new(hash_fn_for_key_as_id, 1369 equal_fn_for_key_as_id, NULL); 1370 if (IS_ERR(btf_prog_table) || IS_ERR(btf_map_table)) { 1371 hashmap__free(btf_prog_table); 1372 hashmap__free(btf_map_table); 1373 if (fd >= 0) 1374 close(fd); 1375 p_err("failed to create hashmap for object references"); 1376 return -1; 1377 } 1378 err = build_btf_tables(btf_prog_table, btf_map_table); 1379 if (err) { 1380 if (fd >= 0) 1381 close(fd); 1382 return err; 1383 } 1384 build_obj_refs_table(&refs_table, BPF_OBJ_BTF); 1385 1386 if (fd >= 0) { 1387 err = show_btf(fd, btf_prog_table, btf_map_table); 1388 close(fd); 1389 goto exit_free; 1390 } 1391 1392 if (json_output) 1393 jsonw_start_array(json_wtr); /* root array */ 1394 1395 while (true) { 1396 err = bpf_btf_get_next_id(id, &id); 1397 if (err) { 1398 if (errno == ENOENT) { 1399 err = 0; 1400 break; 1401 } 1402 p_err("can't get next BTF object: %s%s", 1403 strerror(errno), 1404 errno == EINVAL ? " -- kernel too old?" : ""); 1405 err = -1; 1406 break; 1407 } 1408 1409 fd = bpf_btf_get_fd_by_id(id); 1410 if (fd < 0) { 1411 if (errno == ENOENT) 1412 continue; 1413 p_err("can't get BTF object by id (%u): %s", 1414 id, strerror(errno)); 1415 err = -1; 1416 break; 1417 } 1418 1419 err = show_btf(fd, btf_prog_table, btf_map_table); 1420 close(fd); 1421 if (err) 1422 break; 1423 } 1424 1425 if (json_output) 1426 jsonw_end_array(json_wtr); /* root array */ 1427 1428 exit_free: 1429 hashmap__free(btf_prog_table); 1430 hashmap__free(btf_map_table); 1431 delete_obj_refs_table(refs_table); 1432 1433 return err; 1434 } 1435 1436 static int do_help(int argc, char **argv) 1437 { 1438 if (json_output) { 1439 jsonw_null(json_wtr); 1440 return 0; 1441 } 1442 1443 fprintf(stderr, 1444 "Usage: %1$s %2$s { show | list } [id BTF_ID]\n" 1445 " %1$s %2$s dump BTF_SRC [format FORMAT] [root_id ROOT_ID]\n" 1446 " %1$s %2$s help\n" 1447 "\n" 1448 " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n" 1449 " FORMAT := { raw | c [unsorted] }\n" 1450 " " HELP_SPEC_MAP "\n" 1451 " " HELP_SPEC_PROGRAM "\n" 1452 " " HELP_SPEC_OPTIONS " |\n" 1453 " {-B|--base-btf} }\n" 1454 "", 1455 bin_name, "btf"); 1456 1457 return 0; 1458 } 1459 1460 static const struct cmd cmds[] = { 1461 { "show", do_show }, 1462 { "list", do_show }, 1463 { "help", do_help }, 1464 { "dump", do_dump }, 1465 { 0 } 1466 }; 1467 1468 int do_btf(int argc, char **argv) 1469 { 1470 return cmd_select(cmds, argc, argv, do_help); 1471 } 1472