diff options
author | Jiong Wang <jiong.wang@netronome.com> | 2018-03-01 21:01:17 -0500 |
---|---|---|
committer | Alexei Starovoitov <ast@kernel.org> | 2018-03-01 21:29:48 -0500 |
commit | 73bb5b4f8f1468f7e433a30d8fbe820b24578991 (patch) | |
tree | 535085832b071c55ad00a57a2c04fb0aab636d54 /tools/bpf/bpftool/prog.c | |
parent | 3197239d24dcecef1dbc260733b377ade731b748 (diff) |
tools: bpftool: factor out xlated dump related code into separate file
This patch factors out those code of dumping xlated eBPF instructions into
xlated_dumper.[h|c].
They are quite independent dumper functions, so better to be kept
separately.
New dumper support will be added in later patches in this set.
Signed-off-by: Jiong Wang <jiong.wang@netronome.com>
Acked-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'tools/bpf/bpftool/prog.c')
-rw-r--r-- | tools/bpf/bpftool/prog.c | 255 |
1 files changed, 1 insertions, 254 deletions
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c index 950d11dd42ab..c5afee9838e6 100644 --- a/tools/bpf/bpftool/prog.c +++ b/tools/bpf/bpftool/prog.c | |||
@@ -48,7 +48,7 @@ | |||
48 | #include <libbpf.h> | 48 | #include <libbpf.h> |
49 | 49 | ||
50 | #include "main.h" | 50 | #include "main.h" |
51 | #include "disasm.h" | 51 | #include "xlated_dumper.h" |
52 | 52 | ||
53 | static const char * const prog_type_name[] = { | 53 | static const char * const prog_type_name[] = { |
54 | [BPF_PROG_TYPE_UNSPEC] = "unspec", | 54 | [BPF_PROG_TYPE_UNSPEC] = "unspec", |
@@ -407,259 +407,6 @@ static int do_show(int argc, char **argv) | |||
407 | return err; | 407 | return err; |
408 | } | 408 | } |
409 | 409 | ||
410 | #define SYM_MAX_NAME 256 | ||
411 | |||
412 | struct kernel_sym { | ||
413 | unsigned long address; | ||
414 | char name[SYM_MAX_NAME]; | ||
415 | }; | ||
416 | |||
417 | struct dump_data { | ||
418 | unsigned long address_call_base; | ||
419 | struct kernel_sym *sym_mapping; | ||
420 | __u32 sym_count; | ||
421 | char scratch_buff[SYM_MAX_NAME]; | ||
422 | }; | ||
423 | |||
424 | static int kernel_syms_cmp(const void *sym_a, const void *sym_b) | ||
425 | { | ||
426 | return ((struct kernel_sym *)sym_a)->address - | ||
427 | ((struct kernel_sym *)sym_b)->address; | ||
428 | } | ||
429 | |||
430 | static void kernel_syms_load(struct dump_data *dd) | ||
431 | { | ||
432 | struct kernel_sym *sym; | ||
433 | char buff[256]; | ||
434 | void *tmp, *address; | ||
435 | FILE *fp; | ||
436 | |||
437 | fp = fopen("/proc/kallsyms", "r"); | ||
438 | if (!fp) | ||
439 | return; | ||
440 | |||
441 | while (!feof(fp)) { | ||
442 | if (!fgets(buff, sizeof(buff), fp)) | ||
443 | break; | ||
444 | tmp = realloc(dd->sym_mapping, | ||
445 | (dd->sym_count + 1) * | ||
446 | sizeof(*dd->sym_mapping)); | ||
447 | if (!tmp) { | ||
448 | out: | ||
449 | free(dd->sym_mapping); | ||
450 | dd->sym_mapping = NULL; | ||
451 | fclose(fp); | ||
452 | return; | ||
453 | } | ||
454 | dd->sym_mapping = tmp; | ||
455 | sym = &dd->sym_mapping[dd->sym_count]; | ||
456 | if (sscanf(buff, "%p %*c %s", &address, sym->name) != 2) | ||
457 | continue; | ||
458 | sym->address = (unsigned long)address; | ||
459 | if (!strcmp(sym->name, "__bpf_call_base")) { | ||
460 | dd->address_call_base = sym->address; | ||
461 | /* sysctl kernel.kptr_restrict was set */ | ||
462 | if (!sym->address) | ||
463 | goto out; | ||
464 | } | ||
465 | if (sym->address) | ||
466 | dd->sym_count++; | ||
467 | } | ||
468 | |||
469 | fclose(fp); | ||
470 | |||
471 | qsort(dd->sym_mapping, dd->sym_count, | ||
472 | sizeof(*dd->sym_mapping), kernel_syms_cmp); | ||
473 | } | ||
474 | |||
475 | static void kernel_syms_destroy(struct dump_data *dd) | ||
476 | { | ||
477 | free(dd->sym_mapping); | ||
478 | } | ||
479 | |||
480 | static struct kernel_sym *kernel_syms_search(struct dump_data *dd, | ||
481 | unsigned long key) | ||
482 | { | ||
483 | struct kernel_sym sym = { | ||
484 | .address = key, | ||
485 | }; | ||
486 | |||
487 | return dd->sym_mapping ? | ||
488 | bsearch(&sym, dd->sym_mapping, dd->sym_count, | ||
489 | sizeof(*dd->sym_mapping), kernel_syms_cmp) : NULL; | ||
490 | } | ||
491 | |||
492 | static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...) | ||
493 | { | ||
494 | va_list args; | ||
495 | |||
496 | va_start(args, fmt); | ||
497 | vprintf(fmt, args); | ||
498 | va_end(args); | ||
499 | } | ||
500 | |||
501 | static const char *print_call_pcrel(struct dump_data *dd, | ||
502 | struct kernel_sym *sym, | ||
503 | unsigned long address, | ||
504 | const struct bpf_insn *insn) | ||
505 | { | ||
506 | if (sym) | ||
507 | snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), | ||
508 | "%+d#%s", insn->off, sym->name); | ||
509 | else | ||
510 | snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), | ||
511 | "%+d#0x%lx", insn->off, address); | ||
512 | return dd->scratch_buff; | ||
513 | } | ||
514 | |||
515 | static const char *print_call_helper(struct dump_data *dd, | ||
516 | struct kernel_sym *sym, | ||
517 | unsigned long address) | ||
518 | { | ||
519 | if (sym) | ||
520 | snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), | ||
521 | "%s", sym->name); | ||
522 | else | ||
523 | snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), | ||
524 | "0x%lx", address); | ||
525 | return dd->scratch_buff; | ||
526 | } | ||
527 | |||
528 | static const char *print_call(void *private_data, | ||
529 | const struct bpf_insn *insn) | ||
530 | { | ||
531 | struct dump_data *dd = private_data; | ||
532 | unsigned long address = dd->address_call_base + insn->imm; | ||
533 | struct kernel_sym *sym; | ||
534 | |||
535 | sym = kernel_syms_search(dd, address); | ||
536 | if (insn->src_reg == BPF_PSEUDO_CALL) | ||
537 | return print_call_pcrel(dd, sym, address, insn); | ||
538 | else | ||
539 | return print_call_helper(dd, sym, address); | ||
540 | } | ||
541 | |||
542 | static const char *print_imm(void *private_data, | ||
543 | const struct bpf_insn *insn, | ||
544 | __u64 full_imm) | ||
545 | { | ||
546 | struct dump_data *dd = private_data; | ||
547 | |||
548 | if (insn->src_reg == BPF_PSEUDO_MAP_FD) | ||
549 | snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), | ||
550 | "map[id:%u]", insn->imm); | ||
551 | else | ||
552 | snprintf(dd->scratch_buff, sizeof(dd->scratch_buff), | ||
553 | "0x%llx", (unsigned long long)full_imm); | ||
554 | return dd->scratch_buff; | ||
555 | } | ||
556 | |||
557 | static void dump_xlated_plain(struct dump_data *dd, void *buf, | ||
558 | unsigned int len, bool opcodes) | ||
559 | { | ||
560 | const struct bpf_insn_cbs cbs = { | ||
561 | .cb_print = print_insn, | ||
562 | .cb_call = print_call, | ||
563 | .cb_imm = print_imm, | ||
564 | .private_data = dd, | ||
565 | }; | ||
566 | struct bpf_insn *insn = buf; | ||
567 | bool double_insn = false; | ||
568 | unsigned int i; | ||
569 | |||
570 | for (i = 0; i < len / sizeof(*insn); i++) { | ||
571 | if (double_insn) { | ||
572 | double_insn = false; | ||
573 | continue; | ||
574 | } | ||
575 | |||
576 | double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); | ||
577 | |||
578 | printf("% 4d: ", i); | ||
579 | print_bpf_insn(&cbs, NULL, insn + i, true); | ||
580 | |||
581 | if (opcodes) { | ||
582 | printf(" "); | ||
583 | fprint_hex(stdout, insn + i, 8, " "); | ||
584 | if (double_insn && i < len - 1) { | ||
585 | printf(" "); | ||
586 | fprint_hex(stdout, insn + i + 1, 8, " "); | ||
587 | } | ||
588 | printf("\n"); | ||
589 | } | ||
590 | } | ||
591 | } | ||
592 | |||
593 | static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...) | ||
594 | { | ||
595 | unsigned int l = strlen(fmt); | ||
596 | char chomped_fmt[l]; | ||
597 | va_list args; | ||
598 | |||
599 | va_start(args, fmt); | ||
600 | if (l > 0) { | ||
601 | strncpy(chomped_fmt, fmt, l - 1); | ||
602 | chomped_fmt[l - 1] = '\0'; | ||
603 | } | ||
604 | jsonw_vprintf_enquote(json_wtr, chomped_fmt, args); | ||
605 | va_end(args); | ||
606 | } | ||
607 | |||
608 | static void dump_xlated_json(struct dump_data *dd, void *buf, | ||
609 | unsigned int len, bool opcodes) | ||
610 | { | ||
611 | const struct bpf_insn_cbs cbs = { | ||
612 | .cb_print = print_insn_json, | ||
613 | .cb_call = print_call, | ||
614 | .cb_imm = print_imm, | ||
615 | .private_data = dd, | ||
616 | }; | ||
617 | struct bpf_insn *insn = buf; | ||
618 | bool double_insn = false; | ||
619 | unsigned int i; | ||
620 | |||
621 | jsonw_start_array(json_wtr); | ||
622 | for (i = 0; i < len / sizeof(*insn); i++) { | ||
623 | if (double_insn) { | ||
624 | double_insn = false; | ||
625 | continue; | ||
626 | } | ||
627 | double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); | ||
628 | |||
629 | jsonw_start_object(json_wtr); | ||
630 | jsonw_name(json_wtr, "disasm"); | ||
631 | print_bpf_insn(&cbs, NULL, insn + i, true); | ||
632 | |||
633 | if (opcodes) { | ||
634 | jsonw_name(json_wtr, "opcodes"); | ||
635 | jsonw_start_object(json_wtr); | ||
636 | |||
637 | jsonw_name(json_wtr, "code"); | ||
638 | jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code); | ||
639 | |||
640 | jsonw_name(json_wtr, "src_reg"); | ||
641 | jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg); | ||
642 | |||
643 | jsonw_name(json_wtr, "dst_reg"); | ||
644 | jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg); | ||
645 | |||
646 | jsonw_name(json_wtr, "off"); | ||
647 | print_hex_data_json((uint8_t *)(&insn[i].off), 2); | ||
648 | |||
649 | jsonw_name(json_wtr, "imm"); | ||
650 | if (double_insn && i < len - 1) | ||
651 | print_hex_data_json((uint8_t *)(&insn[i].imm), | ||
652 | 12); | ||
653 | else | ||
654 | print_hex_data_json((uint8_t *)(&insn[i].imm), | ||
655 | 4); | ||
656 | jsonw_end_object(json_wtr); | ||
657 | } | ||
658 | jsonw_end_object(json_wtr); | ||
659 | } | ||
660 | jsonw_end_array(json_wtr); | ||
661 | } | ||
662 | |||
663 | static int do_dump(int argc, char **argv) | 410 | static int do_dump(int argc, char **argv) |
664 | { | 411 | { |
665 | struct bpf_prog_info info = {}; | 412 | struct bpf_prog_info info = {}; |