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.h> 12 #include <libbpf.h> 13 #include <linux/btf.h> 14 15 #include "btf.h" 16 #include "json_writer.h" 17 #include "main.h" 18 19 static const char * const btf_kind_str[NR_BTF_KINDS] = { 20 [BTF_KIND_UNKN] = "UNKNOWN", 21 [BTF_KIND_INT] = "INT", 22 [BTF_KIND_PTR] = "PTR", 23 [BTF_KIND_ARRAY] = "ARRAY", 24 [BTF_KIND_STRUCT] = "STRUCT", 25 [BTF_KIND_UNION] = "UNION", 26 [BTF_KIND_ENUM] = "ENUM", 27 [BTF_KIND_FWD] = "FWD", 28 [BTF_KIND_TYPEDEF] = "TYPEDEF", 29 [BTF_KIND_VOLATILE] = "VOLATILE", 30 [BTF_KIND_CONST] = "CONST", 31 [BTF_KIND_RESTRICT] = "RESTRICT", 32 [BTF_KIND_FUNC] = "FUNC", 33 [BTF_KIND_FUNC_PROTO] = "FUNC_PROTO", 34 [BTF_KIND_VAR] = "VAR", 35 [BTF_KIND_DATASEC] = "DATASEC", 36 }; 37 38 static const char *btf_int_enc_str(__u8 encoding) 39 { 40 switch (encoding) { 41 case 0: 42 return "(none)"; 43 case BTF_INT_SIGNED: 44 return "SIGNED"; 45 case BTF_INT_CHAR: 46 return "CHAR"; 47 case BTF_INT_BOOL: 48 return "BOOL"; 49 default: 50 return "UNKN"; 51 } 52 } 53 54 static const char *btf_var_linkage_str(__u32 linkage) 55 { 56 switch (linkage) { 57 case BTF_VAR_STATIC: 58 return "static"; 59 case BTF_VAR_GLOBAL_ALLOCATED: 60 return "global-alloc"; 61 default: 62 return "(unknown)"; 63 } 64 } 65 66 static const char *btf_str(const struct btf *btf, __u32 off) 67 { 68 if (!off) 69 return "(anon)"; 70 return btf__name_by_offset(btf, off) ? : "(invalid)"; 71 } 72 73 static int dump_btf_type(const struct btf *btf, __u32 id, 74 const struct btf_type *t) 75 { 76 json_writer_t *w = json_wtr; 77 int kind, safe_kind; 78 79 kind = BTF_INFO_KIND(t->info); 80 safe_kind = kind <= BTF_KIND_MAX ? kind : BTF_KIND_UNKN; 81 82 if (json_output) { 83 jsonw_start_object(w); 84 jsonw_uint_field(w, "id", id); 85 jsonw_string_field(w, "kind", btf_kind_str[safe_kind]); 86 jsonw_string_field(w, "name", btf_str(btf, t->name_off)); 87 } else { 88 printf("[%u] %s '%s'", id, btf_kind_str[safe_kind], 89 btf_str(btf, t->name_off)); 90 } 91 92 switch (BTF_INFO_KIND(t->info)) { 93 case BTF_KIND_INT: { 94 __u32 v = *(__u32 *)(t + 1); 95 const char *enc; 96 97 enc = btf_int_enc_str(BTF_INT_ENCODING(v)); 98 99 if (json_output) { 100 jsonw_uint_field(w, "size", t->size); 101 jsonw_uint_field(w, "bits_offset", BTF_INT_OFFSET(v)); 102 jsonw_uint_field(w, "nr_bits", BTF_INT_BITS(v)); 103 jsonw_string_field(w, "encoding", enc); 104 } else { 105 printf(" size=%u bits_offset=%u nr_bits=%u encoding=%s", 106 t->size, BTF_INT_OFFSET(v), BTF_INT_BITS(v), 107 enc); 108 } 109 break; 110 } 111 case BTF_KIND_PTR: 112 case BTF_KIND_CONST: 113 case BTF_KIND_VOLATILE: 114 case BTF_KIND_RESTRICT: 115 case BTF_KIND_TYPEDEF: 116 if (json_output) 117 jsonw_uint_field(w, "type_id", t->type); 118 else 119 printf(" type_id=%u", t->type); 120 break; 121 case BTF_KIND_ARRAY: { 122 const struct btf_array *arr = (const void *)(t + 1); 123 124 if (json_output) { 125 jsonw_uint_field(w, "type_id", arr->type); 126 jsonw_uint_field(w, "index_type_id", arr->index_type); 127 jsonw_uint_field(w, "nr_elems", arr->nelems); 128 } else { 129 printf(" type_id=%u index_type_id=%u nr_elems=%u", 130 arr->type, arr->index_type, arr->nelems); 131 } 132 break; 133 } 134 case BTF_KIND_STRUCT: 135 case BTF_KIND_UNION: { 136 const struct btf_member *m = (const void *)(t + 1); 137 __u16 vlen = BTF_INFO_VLEN(t->info); 138 int i; 139 140 if (json_output) { 141 jsonw_uint_field(w, "size", t->size); 142 jsonw_uint_field(w, "vlen", vlen); 143 jsonw_name(w, "members"); 144 jsonw_start_array(w); 145 } else { 146 printf(" size=%u vlen=%u", t->size, vlen); 147 } 148 for (i = 0; i < vlen; i++, m++) { 149 const char *name = btf_str(btf, m->name_off); 150 __u32 bit_off, bit_sz; 151 152 if (BTF_INFO_KFLAG(t->info)) { 153 bit_off = BTF_MEMBER_BIT_OFFSET(m->offset); 154 bit_sz = BTF_MEMBER_BITFIELD_SIZE(m->offset); 155 } else { 156 bit_off = m->offset; 157 bit_sz = 0; 158 } 159 160 if (json_output) { 161 jsonw_start_object(w); 162 jsonw_string_field(w, "name", name); 163 jsonw_uint_field(w, "type_id", m->type); 164 jsonw_uint_field(w, "bits_offset", bit_off); 165 if (bit_sz) { 166 jsonw_uint_field(w, "bitfield_size", 167 bit_sz); 168 } 169 jsonw_end_object(w); 170 } else { 171 printf("\n\t'%s' type_id=%u bits_offset=%u", 172 name, m->type, bit_off); 173 if (bit_sz) 174 printf(" bitfield_size=%u", bit_sz); 175 } 176 } 177 if (json_output) 178 jsonw_end_array(w); 179 break; 180 } 181 case BTF_KIND_ENUM: { 182 const struct btf_enum *v = (const void *)(t + 1); 183 __u16 vlen = BTF_INFO_VLEN(t->info); 184 int i; 185 186 if (json_output) { 187 jsonw_uint_field(w, "size", t->size); 188 jsonw_uint_field(w, "vlen", vlen); 189 jsonw_name(w, "values"); 190 jsonw_start_array(w); 191 } else { 192 printf(" size=%u vlen=%u", t->size, vlen); 193 } 194 for (i = 0; i < vlen; i++, v++) { 195 const char *name = btf_str(btf, v->name_off); 196 197 if (json_output) { 198 jsonw_start_object(w); 199 jsonw_string_field(w, "name", name); 200 jsonw_uint_field(w, "val", v->val); 201 jsonw_end_object(w); 202 } else { 203 printf("\n\t'%s' val=%u", name, v->val); 204 } 205 } 206 if (json_output) 207 jsonw_end_array(w); 208 break; 209 } 210 case BTF_KIND_FWD: { 211 const char *fwd_kind = BTF_INFO_KFLAG(t->info) ? "union" 212 : "struct"; 213 214 if (json_output) 215 jsonw_string_field(w, "fwd_kind", fwd_kind); 216 else 217 printf(" fwd_kind=%s", fwd_kind); 218 break; 219 } 220 case BTF_KIND_FUNC: 221 if (json_output) 222 jsonw_uint_field(w, "type_id", t->type); 223 else 224 printf(" type_id=%u", t->type); 225 break; 226 case BTF_KIND_FUNC_PROTO: { 227 const struct btf_param *p = (const void *)(t + 1); 228 __u16 vlen = BTF_INFO_VLEN(t->info); 229 int i; 230 231 if (json_output) { 232 jsonw_uint_field(w, "ret_type_id", t->type); 233 jsonw_uint_field(w, "vlen", vlen); 234 jsonw_name(w, "params"); 235 jsonw_start_array(w); 236 } else { 237 printf(" ret_type_id=%u vlen=%u", t->type, vlen); 238 } 239 for (i = 0; i < vlen; i++, p++) { 240 const char *name = btf_str(btf, p->name_off); 241 242 if (json_output) { 243 jsonw_start_object(w); 244 jsonw_string_field(w, "name", name); 245 jsonw_uint_field(w, "type_id", p->type); 246 jsonw_end_object(w); 247 } else { 248 printf("\n\t'%s' type_id=%u", name, p->type); 249 } 250 } 251 if (json_output) 252 jsonw_end_array(w); 253 break; 254 } 255 case BTF_KIND_VAR: { 256 const struct btf_var *v = (const void *)(t + 1); 257 const char *linkage; 258 259 linkage = btf_var_linkage_str(v->linkage); 260 261 if (json_output) { 262 jsonw_uint_field(w, "type_id", t->type); 263 jsonw_string_field(w, "linkage", linkage); 264 } else { 265 printf(" type_id=%u, linkage=%s", t->type, linkage); 266 } 267 break; 268 } 269 case BTF_KIND_DATASEC: { 270 const struct btf_var_secinfo *v = (const void *)(t+1); 271 __u16 vlen = BTF_INFO_VLEN(t->info); 272 int i; 273 274 if (json_output) { 275 jsonw_uint_field(w, "size", t->size); 276 jsonw_uint_field(w, "vlen", vlen); 277 jsonw_name(w, "vars"); 278 jsonw_start_array(w); 279 } else { 280 printf(" size=%u vlen=%u", t->size, vlen); 281 } 282 for (i = 0; i < vlen; i++, v++) { 283 if (json_output) { 284 jsonw_start_object(w); 285 jsonw_uint_field(w, "type_id", v->type); 286 jsonw_uint_field(w, "offset", v->offset); 287 jsonw_uint_field(w, "size", v->size); 288 jsonw_end_object(w); 289 } else { 290 printf("\n\ttype_id=%u offset=%u size=%u", 291 v->type, v->offset, v->size); 292 } 293 } 294 if (json_output) 295 jsonw_end_array(w); 296 break; 297 } 298 default: 299 break; 300 } 301 302 if (json_output) 303 jsonw_end_object(json_wtr); 304 else 305 printf("\n"); 306 307 return 0; 308 } 309 310 static int dump_btf_raw(const struct btf *btf, 311 __u32 *root_type_ids, int root_type_cnt) 312 { 313 const struct btf_type *t; 314 int i; 315 316 if (json_output) { 317 jsonw_start_object(json_wtr); 318 jsonw_name(json_wtr, "types"); 319 jsonw_start_array(json_wtr); 320 } 321 322 if (root_type_cnt) { 323 for (i = 0; i < root_type_cnt; i++) { 324 t = btf__type_by_id(btf, root_type_ids[i]); 325 dump_btf_type(btf, root_type_ids[i], t); 326 } 327 } else { 328 int cnt = btf__get_nr_types(btf); 329 330 for (i = 1; i <= cnt; i++) { 331 t = btf__type_by_id(btf, i); 332 dump_btf_type(btf, i, t); 333 } 334 } 335 336 if (json_output) { 337 jsonw_end_array(json_wtr); 338 jsonw_end_object(json_wtr); 339 } 340 return 0; 341 } 342 343 static void __printf(2, 0) btf_dump_printf(void *ctx, 344 const char *fmt, va_list args) 345 { 346 vfprintf(stdout, fmt, args); 347 } 348 349 static int dump_btf_c(const struct btf *btf, 350 __u32 *root_type_ids, int root_type_cnt) 351 { 352 struct btf_dump *d; 353 int err = 0, i; 354 355 d = btf_dump__new(btf, NULL, NULL, btf_dump_printf); 356 if (IS_ERR(d)) 357 return PTR_ERR(d); 358 359 if (root_type_cnt) { 360 for (i = 0; i < root_type_cnt; i++) { 361 err = btf_dump__dump_type(d, root_type_ids[i]); 362 if (err) 363 goto done; 364 } 365 } else { 366 int cnt = btf__get_nr_types(btf); 367 368 for (i = 1; i <= cnt; i++) { 369 err = btf_dump__dump_type(d, i); 370 if (err) 371 goto done; 372 } 373 } 374 375 done: 376 btf_dump__free(d); 377 return err; 378 } 379 380 static int do_dump(int argc, char **argv) 381 { 382 struct btf *btf = NULL; 383 __u32 root_type_ids[2]; 384 int root_type_cnt = 0; 385 bool dump_c = false; 386 __u32 btf_id = -1; 387 const char *src; 388 int fd = -1; 389 int err; 390 391 if (!REQ_ARGS(2)) { 392 usage(); 393 return -1; 394 } 395 src = GET_ARG(); 396 397 if (is_prefix(src, "map")) { 398 struct bpf_map_info info = {}; 399 __u32 len = sizeof(info); 400 401 if (!REQ_ARGS(2)) { 402 usage(); 403 return -1; 404 } 405 406 fd = map_parse_fd_and_info(&argc, &argv, &info, &len); 407 if (fd < 0) 408 return -1; 409 410 btf_id = info.btf_id; 411 if (argc && is_prefix(*argv, "key")) { 412 root_type_ids[root_type_cnt++] = info.btf_key_type_id; 413 NEXT_ARG(); 414 } else if (argc && is_prefix(*argv, "value")) { 415 root_type_ids[root_type_cnt++] = info.btf_value_type_id; 416 NEXT_ARG(); 417 } else if (argc && is_prefix(*argv, "all")) { 418 NEXT_ARG(); 419 } else if (argc && is_prefix(*argv, "kv")) { 420 root_type_ids[root_type_cnt++] = info.btf_key_type_id; 421 root_type_ids[root_type_cnt++] = info.btf_value_type_id; 422 NEXT_ARG(); 423 } else { 424 root_type_ids[root_type_cnt++] = info.btf_key_type_id; 425 root_type_ids[root_type_cnt++] = info.btf_value_type_id; 426 } 427 } else if (is_prefix(src, "prog")) { 428 struct bpf_prog_info info = {}; 429 __u32 len = sizeof(info); 430 431 if (!REQ_ARGS(2)) { 432 usage(); 433 return -1; 434 } 435 436 fd = prog_parse_fd(&argc, &argv); 437 if (fd < 0) 438 return -1; 439 440 err = bpf_obj_get_info_by_fd(fd, &info, &len); 441 if (err) { 442 p_err("can't get prog info: %s", strerror(errno)); 443 goto done; 444 } 445 446 btf_id = info.btf_id; 447 } else if (is_prefix(src, "id")) { 448 char *endptr; 449 450 btf_id = strtoul(*argv, &endptr, 0); 451 if (*endptr) { 452 p_err("can't parse %s as ID", **argv); 453 return -1; 454 } 455 NEXT_ARG(); 456 } else if (is_prefix(src, "file")) { 457 btf = btf__parse_elf(*argv, NULL); 458 if (IS_ERR(btf)) { 459 err = PTR_ERR(btf); 460 btf = NULL; 461 p_err("failed to load BTF from %s: %s", 462 *argv, strerror(err)); 463 goto done; 464 } 465 NEXT_ARG(); 466 } else { 467 err = -1; 468 p_err("unrecognized BTF source specifier: '%s'", src); 469 goto done; 470 } 471 472 while (argc) { 473 if (is_prefix(*argv, "format")) { 474 NEXT_ARG(); 475 if (argc < 1) { 476 p_err("expecting value for 'format' option\n"); 477 goto done; 478 } 479 if (strcmp(*argv, "c") == 0) { 480 dump_c = true; 481 } else if (strcmp(*argv, "raw") == 0) { 482 dump_c = false; 483 } else { 484 p_err("unrecognized format specifier: '%s', possible values: raw, c", 485 *argv); 486 goto done; 487 } 488 NEXT_ARG(); 489 } else { 490 p_err("unrecognized option: '%s'", *argv); 491 goto done; 492 } 493 } 494 495 if (!btf) { 496 err = btf__get_from_id(btf_id, &btf); 497 if (err) { 498 p_err("get btf by id (%u): %s", btf_id, strerror(err)); 499 goto done; 500 } 501 if (!btf) { 502 err = ENOENT; 503 p_err("can't find btf with ID (%u)", btf_id); 504 goto done; 505 } 506 } 507 508 if (dump_c) { 509 if (json_output) { 510 p_err("JSON output for C-syntax dump is not supported"); 511 err = -ENOTSUP; 512 goto done; 513 } 514 err = dump_btf_c(btf, root_type_ids, root_type_cnt); 515 } else { 516 err = dump_btf_raw(btf, root_type_ids, root_type_cnt); 517 } 518 519 done: 520 close(fd); 521 btf__free(btf); 522 return err; 523 } 524 525 static int do_help(int argc, char **argv) 526 { 527 if (json_output) { 528 jsonw_null(json_wtr); 529 return 0; 530 } 531 532 fprintf(stderr, 533 "Usage: %s btf dump BTF_SRC [format FORMAT]\n" 534 " %s btf help\n" 535 "\n" 536 " BTF_SRC := { id BTF_ID | prog PROG | map MAP [{key | value | kv | all}] | file FILE }\n" 537 " FORMAT := { raw | c }\n" 538 " " HELP_SPEC_MAP "\n" 539 " " HELP_SPEC_PROGRAM "\n" 540 " " HELP_SPEC_OPTIONS "\n" 541 "", 542 bin_name, bin_name); 543 544 return 0; 545 } 546 547 static const struct cmd cmds[] = { 548 { "help", do_help }, 549 { "dump", do_dump }, 550 { 0 } 551 }; 552 553 int do_btf(int argc, char **argv) 554 { 555 return cmd_select(cmds, argc, argv, do_help); 556 } 557