diff options
author | Martin KaFai Lau <kafai@fb.com> | 2018-12-07 19:42:31 -0500 |
---|---|---|
committer | Alexei Starovoitov <ast@kernel.org> | 2018-12-09 16:54:38 -0500 |
commit | 3d65014146c69bbc4d2947f60dbd722d352cdc46 (patch) | |
tree | ea591de92250d7ff871083d63bd5823c68b9bee9 /tools/lib/bpf/btf.c | |
parent | f0187f0b17fad7439f510eff4d65606c9ea1190f (diff) |
bpf: libbpf: Add btf_line_info support to libbpf
This patch adds bpf_line_info support to libbpf:
1) Parsing the line_info sec from ".BTF.ext"
2) Relocating the line_info. If the main prog *_info relocation
fails, it will ignore the remaining subprog line_info and continue.
If the subprog *_info relocation fails, it will bail out.
3) BPF_PROG_LOAD a prog with line_info
Signed-off-by: Martin KaFai Lau <kafai@fb.com>
Acked-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'tools/lib/bpf/btf.c')
-rw-r--r-- | tools/lib/bpf/btf.c | 209 |
1 files changed, 150 insertions, 59 deletions
diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c index aa4fa02b13fc..d682d3b8f7b9 100644 --- a/tools/lib/bpf/btf.c +++ b/tools/lib/bpf/btf.c | |||
@@ -37,16 +37,26 @@ struct btf { | |||
37 | int fd; | 37 | int fd; |
38 | }; | 38 | }; |
39 | 39 | ||
40 | struct btf_ext_info { | ||
41 | /* | ||
42 | * info points to a deep copy of the individual info section | ||
43 | * (e.g. func_info and line_info) from the .BTF.ext. | ||
44 | * It does not include the __u32 rec_size. | ||
45 | */ | ||
46 | void *info; | ||
47 | __u32 rec_size; | ||
48 | __u32 len; | ||
49 | }; | ||
50 | |||
40 | struct btf_ext { | 51 | struct btf_ext { |
41 | void *func_info; | 52 | struct btf_ext_info func_info; |
42 | __u32 func_info_rec_size; | 53 | struct btf_ext_info line_info; |
43 | __u32 func_info_len; | ||
44 | }; | 54 | }; |
45 | 55 | ||
46 | struct btf_sec_func_info { | 56 | struct btf_ext_info_sec { |
47 | __u32 sec_name_off; | 57 | __u32 sec_name_off; |
48 | __u32 num_func_info; | 58 | __u32 num_info; |
49 | /* Followed by num_func_info number of bpf func_info records */ | 59 | /* Followed by num_info * record_size number of bytes */ |
50 | __u8 data[0]; | 60 | __u8 data[0]; |
51 | }; | 61 | }; |
52 | 62 | ||
@@ -56,6 +66,14 @@ struct bpf_func_info_min { | |||
56 | __u32 type_id; | 66 | __u32 type_id; |
57 | }; | 67 | }; |
58 | 68 | ||
69 | /* The minimum bpf_line_info checked by the loader */ | ||
70 | struct bpf_line_info_min { | ||
71 | __u32 insn_off; | ||
72 | __u32 file_name_off; | ||
73 | __u32 line_off; | ||
74 | __u32 line_col; | ||
75 | }; | ||
76 | |||
59 | static inline __u64 ptr_to_u64(const void *ptr) | 77 | static inline __u64 ptr_to_u64(const void *ptr) |
60 | { | 78 | { |
61 | return (__u64) (unsigned long) ptr; | 79 | return (__u64) (unsigned long) ptr; |
@@ -486,12 +504,22 @@ exit_free: | |||
486 | return err; | 504 | return err; |
487 | } | 505 | } |
488 | 506 | ||
489 | static int btf_ext_copy_func_info(struct btf_ext *btf_ext, | 507 | struct btf_ext_sec_copy_param { |
490 | __u8 *data, __u32 data_size, | 508 | __u32 off; |
491 | btf_print_fn_t err_log) | 509 | __u32 len; |
510 | __u32 min_rec_size; | ||
511 | struct btf_ext_info *ext_info; | ||
512 | const char *desc; | ||
513 | }; | ||
514 | |||
515 | static int btf_ext_copy_info(struct btf_ext *btf_ext, | ||
516 | __u8 *data, __u32 data_size, | ||
517 | struct btf_ext_sec_copy_param *ext_sec, | ||
518 | btf_print_fn_t err_log) | ||
492 | { | 519 | { |
493 | const struct btf_ext_header *hdr = (struct btf_ext_header *)data; | 520 | const struct btf_ext_header *hdr = (struct btf_ext_header *)data; |
494 | const struct btf_sec_func_info *sinfo; | 521 | const struct btf_ext_info_sec *sinfo; |
522 | struct btf_ext_info *ext_info; | ||
495 | __u32 info_left, record_size; | 523 | __u32 info_left, record_size; |
496 | /* The start of the info sec (including the __u32 record_size). */ | 524 | /* The start of the info sec (including the __u32 record_size). */ |
497 | const void *info; | 525 | const void *info; |
@@ -500,66 +528,69 @@ static int btf_ext_copy_func_info(struct btf_ext *btf_ext, | |||
500 | data = data + hdr->hdr_len; | 528 | data = data + hdr->hdr_len; |
501 | data_size -= hdr->hdr_len; | 529 | data_size -= hdr->hdr_len; |
502 | 530 | ||
503 | if (hdr->func_info_off & 0x03) { | 531 | if (ext_sec->off & 0x03) { |
504 | elog("BTF.ext func_info section is not aligned to 4 bytes\n"); | 532 | elog(".BTF.ext %s section is not aligned to 4 bytes\n", |
533 | ext_sec->desc); | ||
505 | return -EINVAL; | 534 | return -EINVAL; |
506 | } | 535 | } |
507 | 536 | ||
508 | if (data_size < hdr->func_info_off || | 537 | if (data_size < ext_sec->off || |
509 | hdr->func_info_len > data_size - hdr->func_info_off) { | 538 | ext_sec->len > data_size - ext_sec->off) { |
510 | elog("func_info section (off:%u len:%u) is beyond the end of the ELF section .BTF.ext\n", | 539 | elog("%s section (off:%u len:%u) is beyond the end of the ELF section .BTF.ext\n", |
511 | hdr->func_info_off, hdr->func_info_len); | 540 | ext_sec->desc, ext_sec->off, ext_sec->len); |
512 | return -EINVAL; | 541 | return -EINVAL; |
513 | } | 542 | } |
514 | 543 | ||
515 | info = data + hdr->func_info_off; | 544 | info = data + ext_sec->off; |
516 | info_left = hdr->func_info_len; | 545 | info_left = ext_sec->len; |
517 | 546 | ||
518 | /* At least a func_info record size */ | 547 | /* At least a record size */ |
519 | if (info_left < sizeof(__u32)) { | 548 | if (info_left < sizeof(__u32)) { |
520 | elog("BTF.ext func_info record size not found"); | 549 | elog(".BTF.ext %s record size not found\n", ext_sec->desc); |
521 | return -EINVAL; | 550 | return -EINVAL; |
522 | } | 551 | } |
523 | 552 | ||
524 | /* The record size needs to meet the minimum standard */ | 553 | /* The record size needs to meet the minimum standard */ |
525 | record_size = *(__u32 *)info; | 554 | record_size = *(__u32 *)info; |
526 | if (record_size < sizeof(struct bpf_func_info_min) || | 555 | if (record_size < ext_sec->min_rec_size || |
527 | record_size & 0x03) { | 556 | record_size & 0x03) { |
528 | elog("BTF.ext func_info invalid record size"); | 557 | elog("%s section in .BTF.ext has invalid record size %u\n", |
558 | ext_sec->desc, record_size); | ||
529 | return -EINVAL; | 559 | return -EINVAL; |
530 | } | 560 | } |
531 | 561 | ||
532 | sinfo = info + sizeof(__u32); | 562 | sinfo = info + sizeof(__u32); |
533 | info_left -= sizeof(__u32); | 563 | info_left -= sizeof(__u32); |
534 | 564 | ||
535 | /* If no func_info records, return failure now so .BTF.ext | 565 | /* If no records, return failure now so .BTF.ext won't be used. */ |
536 | * won't be used. | ||
537 | */ | ||
538 | if (!info_left) { | 566 | if (!info_left) { |
539 | elog("BTF.ext no func info records"); | 567 | elog("%s section in .BTF.ext has no records", ext_sec->desc); |
540 | return -EINVAL; | 568 | return -EINVAL; |
541 | } | 569 | } |
542 | 570 | ||
543 | while (info_left) { | 571 | while (info_left) { |
544 | unsigned int sec_hdrlen = sizeof(struct btf_sec_func_info); | 572 | unsigned int sec_hdrlen = sizeof(struct btf_ext_info_sec); |
545 | __u64 total_record_size; | 573 | __u64 total_record_size; |
546 | __u32 num_records; | 574 | __u32 num_records; |
547 | 575 | ||
548 | if (info_left < sec_hdrlen) { | 576 | if (info_left < sec_hdrlen) { |
549 | elog("BTF.ext func_info header not found"); | 577 | elog("%s section header is not found in .BTF.ext\n", |
578 | ext_sec->desc); | ||
550 | return -EINVAL; | 579 | return -EINVAL; |
551 | } | 580 | } |
552 | 581 | ||
553 | num_records = sinfo->num_func_info; | 582 | num_records = sinfo->num_info; |
554 | if (num_records == 0) { | 583 | if (num_records == 0) { |
555 | elog("incorrect BTF.ext num_func_info"); | 584 | elog("%s section has incorrect num_records in .BTF.ext\n", |
585 | ext_sec->desc); | ||
556 | return -EINVAL; | 586 | return -EINVAL; |
557 | } | 587 | } |
558 | 588 | ||
559 | total_record_size = sec_hdrlen + | 589 | total_record_size = sec_hdrlen + |
560 | (__u64)num_records * record_size; | 590 | (__u64)num_records * record_size; |
561 | if (info_left < total_record_size) { | 591 | if (info_left < total_record_size) { |
562 | elog("incorrect BTF.ext num_func_info"); | 592 | elog("%s section has incorrect num_records in .BTF.ext\n", |
593 | ext_sec->desc); | ||
563 | return -EINVAL; | 594 | return -EINVAL; |
564 | } | 595 | } |
565 | 596 | ||
@@ -567,17 +598,49 @@ static int btf_ext_copy_func_info(struct btf_ext *btf_ext, | |||
567 | sinfo = (void *)sinfo + total_record_size; | 598 | sinfo = (void *)sinfo + total_record_size; |
568 | } | 599 | } |
569 | 600 | ||
570 | btf_ext->func_info_len = hdr->func_info_len - sizeof(__u32); | 601 | ext_info = ext_sec->ext_info; |
571 | btf_ext->func_info_rec_size = record_size; | 602 | ext_info->len = ext_sec->len - sizeof(__u32); |
572 | btf_ext->func_info = malloc(btf_ext->func_info_len); | 603 | ext_info->rec_size = record_size; |
573 | if (!btf_ext->func_info) | 604 | ext_info->info = malloc(ext_info->len); |
605 | if (!ext_info->info) | ||
574 | return -ENOMEM; | 606 | return -ENOMEM; |
575 | memcpy(btf_ext->func_info, info + sizeof(__u32), | 607 | memcpy(ext_info->info, info + sizeof(__u32), ext_info->len); |
576 | btf_ext->func_info_len); | ||
577 | 608 | ||
578 | return 0; | 609 | return 0; |
579 | } | 610 | } |
580 | 611 | ||
612 | static int btf_ext_copy_func_info(struct btf_ext *btf_ext, | ||
613 | __u8 *data, __u32 data_size, | ||
614 | btf_print_fn_t err_log) | ||
615 | { | ||
616 | const struct btf_ext_header *hdr = (struct btf_ext_header *)data; | ||
617 | struct btf_ext_sec_copy_param param = { | ||
618 | .off = hdr->func_info_off, | ||
619 | .len = hdr->func_info_len, | ||
620 | .min_rec_size = sizeof(struct bpf_func_info_min), | ||
621 | .ext_info = &btf_ext->func_info, | ||
622 | .desc = "func_info" | ||
623 | }; | ||
624 | |||
625 | return btf_ext_copy_info(btf_ext, data, data_size, ¶m, err_log); | ||
626 | } | ||
627 | |||
628 | static int btf_ext_copy_line_info(struct btf_ext *btf_ext, | ||
629 | __u8 *data, __u32 data_size, | ||
630 | btf_print_fn_t err_log) | ||
631 | { | ||
632 | const struct btf_ext_header *hdr = (struct btf_ext_header *)data; | ||
633 | struct btf_ext_sec_copy_param param = { | ||
634 | .off = hdr->line_info_off, | ||
635 | .len = hdr->line_info_len, | ||
636 | .min_rec_size = sizeof(struct bpf_line_info_min), | ||
637 | .ext_info = &btf_ext->line_info, | ||
638 | .desc = "line_info", | ||
639 | }; | ||
640 | |||
641 | return btf_ext_copy_info(btf_ext, data, data_size, ¶m, err_log); | ||
642 | } | ||
643 | |||
581 | static int btf_ext_parse_hdr(__u8 *data, __u32 data_size, | 644 | static int btf_ext_parse_hdr(__u8 *data, __u32 data_size, |
582 | btf_print_fn_t err_log) | 645 | btf_print_fn_t err_log) |
583 | { | 646 | { |
@@ -617,7 +680,8 @@ void btf_ext__free(struct btf_ext *btf_ext) | |||
617 | if (!btf_ext) | 680 | if (!btf_ext) |
618 | return; | 681 | return; |
619 | 682 | ||
620 | free(btf_ext->func_info); | 683 | free(btf_ext->func_info.info); |
684 | free(btf_ext->line_info.info); | ||
621 | free(btf_ext); | 685 | free(btf_ext); |
622 | } | 686 | } |
623 | 687 | ||
@@ -640,25 +704,32 @@ struct btf_ext *btf_ext__new(__u8 *data, __u32 size, btf_print_fn_t err_log) | |||
640 | return ERR_PTR(err); | 704 | return ERR_PTR(err); |
641 | } | 705 | } |
642 | 706 | ||
707 | err = btf_ext_copy_line_info(btf_ext, data, size, err_log); | ||
708 | if (err) { | ||
709 | btf_ext__free(btf_ext); | ||
710 | return ERR_PTR(err); | ||
711 | } | ||
712 | |||
643 | return btf_ext; | 713 | return btf_ext; |
644 | } | 714 | } |
645 | 715 | ||
646 | int btf_ext__reloc_func_info(struct btf *btf, struct btf_ext *btf_ext, | 716 | static int btf_ext_reloc_info(const struct btf *btf, |
647 | const char *sec_name, __u32 insns_cnt, | 717 | const struct btf_ext_info *ext_info, |
648 | void **func_info, __u32 *cnt) | 718 | const char *sec_name, __u32 insns_cnt, |
719 | void **info, __u32 *cnt) | ||
649 | { | 720 | { |
650 | __u32 sec_hdrlen = sizeof(struct btf_sec_func_info); | 721 | __u32 sec_hdrlen = sizeof(struct btf_ext_info_sec); |
651 | __u32 i, record_size, existing_flen, records_len; | 722 | __u32 i, record_size, existing_len, records_len; |
652 | struct btf_sec_func_info *sinfo; | 723 | struct btf_ext_info_sec *sinfo; |
653 | const char *info_sec_name; | 724 | const char *info_sec_name; |
654 | __u64 remain_len; | 725 | __u64 remain_len; |
655 | void *data; | 726 | void *data; |
656 | 727 | ||
657 | record_size = btf_ext->func_info_rec_size; | 728 | record_size = ext_info->rec_size; |
658 | sinfo = btf_ext->func_info; | 729 | sinfo = ext_info->info; |
659 | remain_len = btf_ext->func_info_len; | 730 | remain_len = ext_info->len; |
660 | while (remain_len > 0) { | 731 | while (remain_len > 0) { |
661 | records_len = sinfo->num_func_info * record_size; | 732 | records_len = sinfo->num_info * record_size; |
662 | info_sec_name = btf__name_by_offset(btf, sinfo->sec_name_off); | 733 | info_sec_name = btf__name_by_offset(btf, sinfo->sec_name_off); |
663 | if (strcmp(info_sec_name, sec_name)) { | 734 | if (strcmp(info_sec_name, sec_name)) { |
664 | remain_len -= sec_hdrlen + records_len; | 735 | remain_len -= sec_hdrlen + records_len; |
@@ -666,32 +737,52 @@ int btf_ext__reloc_func_info(struct btf *btf, struct btf_ext *btf_ext, | |||
666 | continue; | 737 | continue; |
667 | } | 738 | } |
668 | 739 | ||
669 | existing_flen = (*cnt) * record_size; | 740 | existing_len = (*cnt) * record_size; |
670 | data = realloc(*func_info, existing_flen + records_len); | 741 | data = realloc(*info, existing_len + records_len); |
671 | if (!data) | 742 | if (!data) |
672 | return -ENOMEM; | 743 | return -ENOMEM; |
673 | 744 | ||
674 | memcpy(data + existing_flen, sinfo->data, records_len); | 745 | memcpy(data + existing_len, sinfo->data, records_len); |
675 | /* adjust insn_off only, the rest data will be passed | 746 | /* adjust insn_off only, the rest data will be passed |
676 | * to the kernel. | 747 | * to the kernel. |
677 | */ | 748 | */ |
678 | for (i = 0; i < sinfo->num_func_info; i++) { | 749 | for (i = 0; i < sinfo->num_info; i++) { |
679 | struct bpf_func_info_min *record; | 750 | __u32 *insn_off; |
680 | 751 | ||
681 | record = data + existing_flen + i * record_size; | 752 | insn_off = data + existing_len + (i * record_size); |
682 | record->insn_off = | 753 | *insn_off = *insn_off / sizeof(struct bpf_insn) + |
683 | record->insn_off / sizeof(struct bpf_insn) + | ||
684 | insns_cnt; | 754 | insns_cnt; |
685 | } | 755 | } |
686 | *func_info = data; | 756 | *info = data; |
687 | *cnt += sinfo->num_func_info; | 757 | *cnt += sinfo->num_info; |
688 | return 0; | 758 | return 0; |
689 | } | 759 | } |
690 | 760 | ||
691 | return -ENOENT; | 761 | return -ENOENT; |
692 | } | 762 | } |
693 | 763 | ||
764 | int btf_ext__reloc_func_info(const struct btf *btf, const struct btf_ext *btf_ext, | ||
765 | const char *sec_name, __u32 insns_cnt, | ||
766 | void **func_info, __u32 *cnt) | ||
767 | { | ||
768 | return btf_ext_reloc_info(btf, &btf_ext->func_info, sec_name, | ||
769 | insns_cnt, func_info, cnt); | ||
770 | } | ||
771 | |||
772 | int btf_ext__reloc_line_info(const struct btf *btf, const struct btf_ext *btf_ext, | ||
773 | const char *sec_name, __u32 insns_cnt, | ||
774 | void **line_info, __u32 *cnt) | ||
775 | { | ||
776 | return btf_ext_reloc_info(btf, &btf_ext->line_info, sec_name, | ||
777 | insns_cnt, line_info, cnt); | ||
778 | } | ||
779 | |||
694 | __u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext) | 780 | __u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext) |
695 | { | 781 | { |
696 | return btf_ext->func_info_rec_size; | 782 | return btf_ext->func_info.rec_size; |
783 | } | ||
784 | |||
785 | __u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext) | ||
786 | { | ||
787 | return btf_ext->line_info.rec_size; | ||
697 | } | 788 | } |