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, 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 if (fd < 0) 910 return -1; 911 912 btf_id = info.btf_id; 913 if (argc && is_prefix(*argv, "key")) { 914 root_type_ids[root_type_cnt++] = info.btf_key_type_id; 915 NEXT_ARG(); 916 } else if (argc && is_prefix(*argv, "value")) { 917 root_type_ids[root_type_cnt++] = info.btf_value_type_id; 918 NEXT_ARG(); 919 } else if (argc && is_prefix(*argv, "all")) { 920 NEXT_ARG(); 921 } else if (argc && is_prefix(*argv, "kv")) { 922 root_type_ids[root_type_cnt++] = info.btf_key_type_id; 923 root_type_ids[root_type_cnt++] = info.btf_value_type_id; 924 NEXT_ARG(); 925 } else { 926 root_type_ids[root_type_cnt++] = info.btf_key_type_id; 927 root_type_ids[root_type_cnt++] = info.btf_value_type_id; 928 } 929 } else if (is_prefix(src, "prog")) { 930 struct bpf_prog_info info = {}; 931 __u32 len = sizeof(info); 932 933 if (!REQ_ARGS(2)) { 934 usage(); 935 return -1; 936 } 937 938 fd = prog_parse_fd(&argc, &argv); 939 if (fd < 0) 940 return -1; 941 942 err = bpf_prog_get_info_by_fd(fd, &info, &len); 943 if (err) { 944 p_err("can't get prog info: %s", strerror(errno)); 945 goto done; 946 } 947 948 btf_id = info.btf_id; 949 } else if (is_prefix(src, "id")) { 950 char *endptr; 951 952 btf_id = strtoul(*argv, &endptr, 0); 953 if (*endptr) { 954 p_err("can't parse %s as ID", *argv); 955 return -1; 956 } 957 NEXT_ARG(); 958 } else if (is_prefix(src, "file")) { 959 const char sysfs_prefix[] = "/sys/kernel/btf/"; 960 961 if (!base_btf && 962 strncmp(*argv, sysfs_prefix, sizeof(sysfs_prefix) - 1) == 0 && 963 strcmp(*argv, sysfs_vmlinux) != 0) 964 base = get_vmlinux_btf_from_sysfs(); 965 966 btf = btf__parse_split(*argv, base ?: base_btf); 967 if (!btf) { 968 err = -errno; 969 p_err("failed to load BTF from %s: %s", 970 *argv, strerror(errno)); 971 goto done; 972 } 973 NEXT_ARG(); 974 } else { 975 err = -1; 976 p_err("unrecognized BTF source specifier: '%s'", src); 977 goto done; 978 } 979 980 have_id_filtering = !!root_type_cnt; 981 982 while (argc) { 983 if (is_prefix(*argv, "format")) { 984 NEXT_ARG(); 985 if (argc < 1) { 986 p_err("expecting value for 'format' option\n"); 987 err = -EINVAL; 988 goto done; 989 } 990 if (strcmp(*argv, "c") == 0) { 991 dump_c = true; 992 } else if (strcmp(*argv, "raw") == 0) { 993 dump_c = false; 994 } else { 995 p_err("unrecognized format specifier: '%s', possible values: raw, c", 996 *argv); 997 err = -EINVAL; 998 goto done; 999 } 1000 NEXT_ARG(); 1001 } else if (is_prefix(*argv, "root_id")) { 1002 __u32 root_id; 1003 char *end; 1004 1005 if (have_id_filtering) { 1006 p_err("cannot use root_id with other type filtering"); 1007 err = -EINVAL; 1008 goto done; 1009 } else if (root_type_cnt == MAX_ROOT_IDS) { 1010 p_err("only %d root_id are supported", MAX_ROOT_IDS); 1011 err = -E2BIG; 1012 goto done; 1013 } 1014 1015 NEXT_ARG(); 1016 root_id = strtoul(*argv, &end, 0); 1017 if (*end) { 1018 err = -1; 1019 p_err("can't parse %s as root ID", *argv); 1020 goto done; 1021 } 1022 for (i = 0; i < root_type_cnt; i++) { 1023 if (root_type_ids[i] == root_id) { 1024 err = -EINVAL; 1025 p_err("duplicate root_id %d supplied", root_id); 1026 goto done; 1027 } 1028 } 1029 root_type_ids[root_type_cnt++] = root_id; 1030 NEXT_ARG(); 1031 } else if (is_prefix(*argv, "unsorted")) { 1032 sort_dump_c = false; 1033 NEXT_ARG(); 1034 } else { 1035 p_err("unrecognized option: '%s'", *argv); 1036 err = -EINVAL; 1037 goto done; 1038 } 1039 } 1040 1041 if (!btf) { 1042 if (!base_btf && btf_is_kernel_module(btf_id)) { 1043 p_info("Warning: valid base BTF was not specified with -B option, falling back to standard base BTF (%s)", 1044 sysfs_vmlinux); 1045 base_btf = get_vmlinux_btf_from_sysfs(); 1046 } 1047 1048 btf = btf__load_from_kernel_by_id_split(btf_id, base_btf); 1049 if (!btf) { 1050 err = -errno; 1051 p_err("get btf by id (%u): %s", btf_id, strerror(errno)); 1052 goto done; 1053 } 1054 } 1055 1056 /* Invalid root IDs causes half emitted boilerplate and then unclean 1057 * exit. It's an ugly user experience, so handle common error here. 1058 */ 1059 for (i = 0; i < root_type_cnt; i++) { 1060 if (root_type_ids[i] >= btf__type_cnt(btf)) { 1061 err = -EINVAL; 1062 p_err("invalid root ID: %u", root_type_ids[i]); 1063 goto done; 1064 } 1065 } 1066 1067 if (dump_c) { 1068 if (json_output) { 1069 p_err("JSON output for C-syntax dump is not supported"); 1070 err = -ENOTSUP; 1071 goto done; 1072 } 1073 err = dump_btf_c(btf, root_type_ids, root_type_cnt, sort_dump_c); 1074 } else { 1075 err = dump_btf_raw(btf, root_type_ids, root_type_cnt); 1076 } 1077 1078 done: 1079 close(fd); 1080 btf__free(btf); 1081 btf__free(base); 1082 return err; 1083 } 1084 1085 static int btf_parse_fd(int *argc, char ***argv) 1086 { 1087 unsigned int id; 1088 char *endptr; 1089 int fd; 1090 1091 if (!is_prefix(*argv[0], "id")) { 1092 p_err("expected 'id', got: '%s'?", **argv); 1093 return -1; 1094 } 1095 NEXT_ARGP(); 1096 1097 id = strtoul(**argv, &endptr, 0); 1098 if (*endptr) { 1099 p_err("can't parse %s as ID", **argv); 1100 return -1; 1101 } 1102 NEXT_ARGP(); 1103 1104 fd = bpf_btf_get_fd_by_id(id); 1105 if (fd < 0) 1106 p_err("can't get BTF object by id (%u): %s", 1107 id, strerror(errno)); 1108 1109 return fd; 1110 } 1111 1112 static int 1113 build_btf_type_table(struct hashmap *tab, enum bpf_obj_type type, 1114 void *info, __u32 *len) 1115 { 1116 static const char * const names[] = { 1117 [BPF_OBJ_UNKNOWN] = "unknown", 1118 [BPF_OBJ_PROG] = "prog", 1119 [BPF_OBJ_MAP] = "map", 1120 }; 1121 __u32 btf_id, id = 0; 1122 int err; 1123 int fd; 1124 1125 while (true) { 1126 switch (type) { 1127 case BPF_OBJ_PROG: 1128 err = bpf_prog_get_next_id(id, &id); 1129 break; 1130 case BPF_OBJ_MAP: 1131 err = bpf_map_get_next_id(id, &id); 1132 break; 1133 default: 1134 err = -1; 1135 p_err("unexpected object type: %d", type); 1136 goto err_free; 1137 } 1138 if (err) { 1139 if (errno == ENOENT) { 1140 err = 0; 1141 break; 1142 } 1143 p_err("can't get next %s: %s%s", names[type], 1144 strerror(errno), 1145 errno == EINVAL ? " -- kernel too old?" : ""); 1146 goto err_free; 1147 } 1148 1149 switch (type) { 1150 case BPF_OBJ_PROG: 1151 fd = bpf_prog_get_fd_by_id(id); 1152 break; 1153 case BPF_OBJ_MAP: 1154 fd = bpf_map_get_fd_by_id(id); 1155 break; 1156 default: 1157 err = -1; 1158 p_err("unexpected object type: %d", type); 1159 goto err_free; 1160 } 1161 if (fd < 0) { 1162 if (errno == ENOENT) 1163 continue; 1164 p_err("can't get %s by id (%u): %s", names[type], id, 1165 strerror(errno)); 1166 err = -1; 1167 goto err_free; 1168 } 1169 1170 memset(info, 0, *len); 1171 if (type == BPF_OBJ_PROG) 1172 err = bpf_prog_get_info_by_fd(fd, info, len); 1173 else 1174 err = bpf_map_get_info_by_fd(fd, info, len); 1175 close(fd); 1176 if (err) { 1177 p_err("can't get %s info: %s", names[type], 1178 strerror(errno)); 1179 goto err_free; 1180 } 1181 1182 switch (type) { 1183 case BPF_OBJ_PROG: 1184 btf_id = ((struct bpf_prog_info *)info)->btf_id; 1185 break; 1186 case BPF_OBJ_MAP: 1187 btf_id = ((struct bpf_map_info *)info)->btf_id; 1188 break; 1189 default: 1190 err = -1; 1191 p_err("unexpected object type: %d", type); 1192 goto err_free; 1193 } 1194 if (!btf_id) 1195 continue; 1196 1197 err = hashmap__append(tab, btf_id, id); 1198 if (err) { 1199 p_err("failed to append entry to hashmap for BTF ID %u, object ID %u: %s", 1200 btf_id, id, strerror(-err)); 1201 goto err_free; 1202 } 1203 } 1204 1205 return 0; 1206 1207 err_free: 1208 hashmap__free(tab); 1209 return err; 1210 } 1211 1212 static int 1213 build_btf_tables(struct hashmap *btf_prog_table, 1214 struct hashmap *btf_map_table) 1215 { 1216 struct bpf_prog_info prog_info; 1217 __u32 prog_len = sizeof(prog_info); 1218 struct bpf_map_info map_info; 1219 __u32 map_len = sizeof(map_info); 1220 int err = 0; 1221 1222 err = build_btf_type_table(btf_prog_table, BPF_OBJ_PROG, &prog_info, 1223 &prog_len); 1224 if (err) 1225 return err; 1226 1227 err = build_btf_type_table(btf_map_table, BPF_OBJ_MAP, &map_info, 1228 &map_len); 1229 if (err) { 1230 hashmap__free(btf_prog_table); 1231 return err; 1232 } 1233 1234 return 0; 1235 } 1236 1237 static void 1238 show_btf_plain(struct bpf_btf_info *info, int fd, 1239 struct hashmap *btf_prog_table, 1240 struct hashmap *btf_map_table) 1241 { 1242 struct hashmap_entry *entry; 1243 const char *name = u64_to_ptr(info->name); 1244 int n; 1245 1246 printf("%u: ", info->id); 1247 if (info->kernel_btf) 1248 printf("name [%s] ", name); 1249 else if (name && name[0]) 1250 printf("name %s ", name); 1251 else 1252 printf("name <anon> "); 1253 printf("size %uB", info->btf_size); 1254 1255 n = 0; 1256 hashmap__for_each_key_entry(btf_prog_table, entry, info->id) { 1257 printf("%s%lu", n++ == 0 ? " prog_ids " : ",", entry->value); 1258 } 1259 1260 n = 0; 1261 hashmap__for_each_key_entry(btf_map_table, entry, info->id) { 1262 printf("%s%lu", n++ == 0 ? " map_ids " : ",", entry->value); 1263 } 1264 1265 emit_obj_refs_plain(refs_table, info->id, "\n\tpids "); 1266 1267 printf("\n"); 1268 } 1269 1270 static void 1271 show_btf_json(struct bpf_btf_info *info, int fd, 1272 struct hashmap *btf_prog_table, 1273 struct hashmap *btf_map_table) 1274 { 1275 struct hashmap_entry *entry; 1276 const char *name = u64_to_ptr(info->name); 1277 1278 jsonw_start_object(json_wtr); /* btf object */ 1279 jsonw_uint_field(json_wtr, "id", info->id); 1280 jsonw_uint_field(json_wtr, "size", info->btf_size); 1281 1282 jsonw_name(json_wtr, "prog_ids"); 1283 jsonw_start_array(json_wtr); /* prog_ids */ 1284 hashmap__for_each_key_entry(btf_prog_table, entry, info->id) { 1285 jsonw_uint(json_wtr, entry->value); 1286 } 1287 jsonw_end_array(json_wtr); /* prog_ids */ 1288 1289 jsonw_name(json_wtr, "map_ids"); 1290 jsonw_start_array(json_wtr); /* map_ids */ 1291 hashmap__for_each_key_entry(btf_map_table, entry, info->id) { 1292 jsonw_uint(json_wtr, entry->value); 1293 } 1294 jsonw_end_array(json_wtr); /* map_ids */ 1295 1296 emit_obj_refs_json(refs_table, info->id, json_wtr); /* pids */ 1297 1298 jsonw_bool_field(json_wtr, "kernel", info->kernel_btf); 1299 1300 if (name && name[0]) 1301 jsonw_string_field(json_wtr, "name", name); 1302 1303 jsonw_end_object(json_wtr); /* btf object */ 1304 } 1305 1306 static int 1307 show_btf(int fd, struct hashmap *btf_prog_table, 1308 struct hashmap *btf_map_table) 1309 { 1310 struct bpf_btf_info info; 1311 __u32 len = sizeof(info); 1312 char name[64]; 1313 int err; 1314 1315 memset(&info, 0, sizeof(info)); 1316 err = bpf_btf_get_info_by_fd(fd, &info, &len); 1317 if (err) { 1318 p_err("can't get BTF object info: %s", strerror(errno)); 1319 return -1; 1320 } 1321 /* if kernel support emitting BTF object name, pass name pointer */ 1322 if (info.name_len) { 1323 memset(&info, 0, sizeof(info)); 1324 info.name_len = sizeof(name); 1325 info.name = ptr_to_u64(name); 1326 len = sizeof(info); 1327 1328 err = bpf_btf_get_info_by_fd(fd, &info, &len); 1329 if (err) { 1330 p_err("can't get BTF object info: %s", strerror(errno)); 1331 return -1; 1332 } 1333 } 1334 1335 if (json_output) 1336 show_btf_json(&info, fd, btf_prog_table, btf_map_table); 1337 else 1338 show_btf_plain(&info, fd, btf_prog_table, btf_map_table); 1339 1340 return 0; 1341 } 1342 1343 static int do_show(int argc, char **argv) 1344 { 1345 struct hashmap *btf_prog_table; 1346 struct hashmap *btf_map_table; 1347 int err, fd = -1; 1348 __u32 id = 0; 1349 1350 if (argc == 2) { 1351 fd = btf_parse_fd(&argc, &argv); 1352 if (fd < 0) 1353 return -1; 1354 } 1355 1356 if (argc) { 1357 if (fd >= 0) 1358 close(fd); 1359 return BAD_ARG(); 1360 } 1361 1362 btf_prog_table = hashmap__new(hash_fn_for_key_as_id, 1363 equal_fn_for_key_as_id, NULL); 1364 btf_map_table = hashmap__new(hash_fn_for_key_as_id, 1365 equal_fn_for_key_as_id, NULL); 1366 if (IS_ERR(btf_prog_table) || IS_ERR(btf_map_table)) { 1367 hashmap__free(btf_prog_table); 1368 hashmap__free(btf_map_table); 1369 if (fd >= 0) 1370 close(fd); 1371 p_err("failed to create hashmap for object references"); 1372 return -1; 1373 } 1374 err = build_btf_tables(btf_prog_table, btf_map_table); 1375 if (err) { 1376 if (fd >= 0) 1377 close(fd); 1378 return err; 1379 } 1380 build_obj_refs_table(&refs_table, BPF_OBJ_BTF); 1381 1382 if (fd >= 0) { 1383 err = show_btf(fd, btf_prog_table, btf_map_table); 1384 close(fd); 1385 goto exit_free; 1386 } 1387 1388 if (json_output) 1389 jsonw_start_array(json_wtr); /* root array */ 1390 1391 while (true) { 1392 err = bpf_btf_get_next_id(id, &id); 1393 if (err) { 1394 if (errno == ENOENT) { 1395 err = 0; 1396 break; 1397 } 1398 p_err("can't get next BTF object: %s%s", 1399 strerror(errno), 1400 errno == EINVAL ? " -- kernel too old?" : ""); 1401 err = -1; 1402 break; 1403 } 1404 1405 fd = bpf_btf_get_fd_by_id(id); 1406 if (fd < 0) { 1407 if (errno == ENOENT) 1408 continue; 1409 p_err("can't get BTF object by id (%u): %s", 1410 id, strerror(errno)); 1411 err = -1; 1412 break; 1413 } 1414 1415 err = show_btf(fd, btf_prog_table, btf_map_table); 1416 close(fd); 1417 if (err) 1418 break; 1419 } 1420 1421 if (json_output) 1422 jsonw_end_array(json_wtr); /* root array */ 1423 1424 exit_free: 1425 hashmap__free(btf_prog_table); 1426 hashmap__free(btf_map_table); 1427 delete_obj_refs_table(refs_table); 1428 1429 return err; 1430 } 1431 1432 static int do_help(int argc, char **argv) 1433 { 1434 if (json_output) { 1435 jsonw_null(json_wtr); 1436 return 0; 1437 } 1438 1439 fprintf(stderr, 1440 "Usage: %1$s %2$s { show | list } [id BTF_ID]\n" 1441 " %1$s %2$s dump BTF_SRC [format FORMAT] [root_id ROOT_ID]\n" 1442 " %1$s %2$s help\n" 1443 "\n" 1444 " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n" 1445 " FORMAT := { raw | c [unsorted] }\n" 1446 " " HELP_SPEC_MAP "\n" 1447 " " HELP_SPEC_PROGRAM "\n" 1448 " " HELP_SPEC_OPTIONS " |\n" 1449 " {-B|--base-btf} }\n" 1450 "", 1451 bin_name, "btf"); 1452 1453 return 0; 1454 } 1455 1456 static const struct cmd cmds[] = { 1457 { "show", do_show }, 1458 { "list", do_show }, 1459 { "help", do_help }, 1460 { "dump", do_dump }, 1461 { 0 } 1462 }; 1463 1464 int do_btf(int argc, char **argv) 1465 { 1466 return cmd_select(cmds, argc, argv, do_help); 1467 } 1468