Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libbpf: Avoid joining .BTF.ext data with BPF programs by section name

Instead of using ELF section names as a joining key between .BTF.ext and
corresponding BPF programs, pre-build .BTF.ext section number to ELF
section index mapping during bpf_object__open() and use it later for
matching .BTF.ext information (func/line info or CO-RE relocations) to
their respective BPF programs and subprograms.

This simplifies corresponding joining logic and let's libbpf do
manipulations with BPF program's ELF sections like dropping leading '?'
character for non-autoloaded programs. Original joining logic in
bpf_object__relocate_core() (see relevant comment that's now removed)
was never elegant, so it's a good improvement regardless. But it also
avoids unnecessary internal assumptions about preserving original ELF
section name as BPF program's section name (which was broken when
SEC("?abc") support was added).

Fixes: a3820c481112 ("libbpf: Support opting out from autoloading BPF programs declaratively")
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20220426004511.2691730-5-andrii@kernel.org

authored by

Andrii Nakryiko and committed by
Alexei Starovoitov
11d5daa8 966a7509

+65 -29
+7 -2
tools/lib/bpf/btf.c
··· 2626 2626 const struct btf_ext_info_sec *sinfo; 2627 2627 struct btf_ext_info *ext_info; 2628 2628 __u32 info_left, record_size; 2629 + size_t sec_cnt = 0; 2629 2630 /* The start of the info sec (including the __u32 record_size). */ 2630 2631 void *info; 2631 2632 ··· 2690 2689 return -EINVAL; 2691 2690 } 2692 2691 2693 - total_record_size = sec_hdrlen + 2694 - (__u64)num_records * record_size; 2692 + total_record_size = sec_hdrlen + (__u64)num_records * record_size; 2695 2693 if (info_left < total_record_size) { 2696 2694 pr_debug("%s section has incorrect num_records in .BTF.ext\n", 2697 2695 ext_sec->desc); ··· 2699 2699 2700 2700 info_left -= total_record_size; 2701 2701 sinfo = (void *)sinfo + total_record_size; 2702 + sec_cnt++; 2702 2703 } 2703 2704 2704 2705 ext_info = ext_sec->ext_info; 2705 2706 ext_info->len = ext_sec->len - sizeof(__u32); 2706 2707 ext_info->rec_size = record_size; 2707 2708 ext_info->info = info + sizeof(__u32); 2709 + ext_info->sec_cnt = sec_cnt; 2708 2710 2709 2711 return 0; 2710 2712 } ··· 2790 2788 { 2791 2789 if (IS_ERR_OR_NULL(btf_ext)) 2792 2790 return; 2791 + free(btf_ext->func_info.sec_idxs); 2792 + free(btf_ext->line_info.sec_idxs); 2793 + free(btf_ext->core_relo_info.sec_idxs); 2793 2794 free(btf_ext->data); 2794 2795 free(btf_ext); 2795 2796 }
+51 -27
tools/lib/bpf/libbpf.c
··· 2765 2765 btf__set_pointer_size(obj->btf, 8); 2766 2766 } 2767 2767 if (btf_ext_data) { 2768 + struct btf_ext_info *ext_segs[3]; 2769 + int seg_num, sec_num; 2770 + 2768 2771 if (!obj->btf) { 2769 2772 pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n", 2770 2773 BTF_EXT_ELF_SEC, BTF_ELF_SEC); ··· 2780 2777 BTF_EXT_ELF_SEC, err); 2781 2778 obj->btf_ext = NULL; 2782 2779 goto out; 2780 + } 2781 + 2782 + /* setup .BTF.ext to ELF section mapping */ 2783 + ext_segs[0] = &obj->btf_ext->func_info; 2784 + ext_segs[1] = &obj->btf_ext->line_info; 2785 + ext_segs[2] = &obj->btf_ext->core_relo_info; 2786 + for (seg_num = 0; seg_num < ARRAY_SIZE(ext_segs); seg_num++) { 2787 + struct btf_ext_info *seg = ext_segs[seg_num]; 2788 + const struct btf_ext_info_sec *sec; 2789 + const char *sec_name; 2790 + Elf_Scn *scn; 2791 + 2792 + if (seg->sec_cnt == 0) 2793 + continue; 2794 + 2795 + seg->sec_idxs = calloc(seg->sec_cnt, sizeof(*seg->sec_idxs)); 2796 + if (!seg->sec_idxs) { 2797 + err = -ENOMEM; 2798 + goto out; 2799 + } 2800 + 2801 + sec_num = 0; 2802 + for_each_btf_ext_sec(seg, sec) { 2803 + /* preventively increment index to avoid doing 2804 + * this before every continue below 2805 + */ 2806 + sec_num++; 2807 + 2808 + sec_name = btf__name_by_offset(obj->btf, sec->sec_name_off); 2809 + if (str_is_empty(sec_name)) 2810 + continue; 2811 + scn = elf_sec_by_name(obj, sec_name); 2812 + if (!scn) 2813 + continue; 2814 + 2815 + seg->sec_idxs[sec_num - 1] = elf_ndxscn(scn); 2816 + } 2783 2817 } 2784 2818 } 2785 2819 out: ··· 5682 5642 struct bpf_program *prog; 5683 5643 struct bpf_insn *insn; 5684 5644 const char *sec_name; 5685 - int i, err = 0, insn_idx, sec_idx; 5645 + int i, err = 0, insn_idx, sec_idx, sec_num; 5686 5646 5687 5647 if (obj->btf_ext->core_relo_info.len == 0) 5688 5648 return 0; ··· 5703 5663 } 5704 5664 5705 5665 seg = &obj->btf_ext->core_relo_info; 5666 + sec_num = 0; 5706 5667 for_each_btf_ext_sec(seg, sec) { 5668 + sec_idx = seg->sec_idxs[sec_num]; 5669 + sec_num++; 5670 + 5707 5671 sec_name = btf__name_by_offset(obj->btf, sec->sec_name_off); 5708 5672 if (str_is_empty(sec_name)) { 5709 5673 err = -EINVAL; 5710 5674 goto out; 5711 5675 } 5712 - /* bpf_object's ELF is gone by now so it's not easy to find 5713 - * section index by section name, but we can find *any* 5714 - * bpf_program within desired section name and use it's 5715 - * prog->sec_idx to do a proper search by section index and 5716 - * instruction offset 5717 - */ 5718 - prog = NULL; 5719 - for (i = 0; i < obj->nr_programs; i++) { 5720 - if (strcmp(obj->programs[i].sec_name, sec_name) == 0) { 5721 - prog = &obj->programs[i]; 5722 - break; 5723 - } 5724 - } 5725 - if (!prog) { 5726 - pr_warn("sec '%s': failed to find a BPF program\n", sec_name); 5727 - return -ENOENT; 5728 - } 5729 - sec_idx = prog->sec_idx; 5730 5676 5731 - pr_debug("sec '%s': found %d CO-RE relocations\n", 5732 - sec_name, sec->num_info); 5677 + pr_debug("sec '%s': found %d CO-RE relocations\n", sec_name, sec->num_info); 5733 5678 5734 5679 for_each_btf_ext_rec(seg, sec, i, rec) { 5735 5680 if (rec->insn_off % BPF_INSN_SZ) ··· 5898 5873 void *rec, *rec_end, *new_prog_info; 5899 5874 const struct btf_ext_info_sec *sec; 5900 5875 size_t old_sz, new_sz; 5901 - const char *sec_name; 5902 - int i, off_adj; 5876 + int i, sec_num, sec_idx, off_adj; 5903 5877 5878 + sec_num = 0; 5904 5879 for_each_btf_ext_sec(ext_info, sec) { 5905 - sec_name = btf__name_by_offset(obj->btf, sec->sec_name_off); 5906 - if (!sec_name) 5907 - return -EINVAL; 5908 - if (strcmp(sec_name, prog->sec_name) != 0) 5880 + sec_idx = ext_info->sec_idxs[sec_num]; 5881 + sec_num++; 5882 + if (prog->sec_idx != sec_idx) 5909 5883 continue; 5910 5884 5911 5885 for_each_btf_ext_rec(ext_info, sec, i, rec) {
+7
tools/lib/bpf/libbpf_internal.h
··· 376 376 void *info; 377 377 __u32 rec_size; 378 378 __u32 len; 379 + /* optional (maintained internally by libbpf) mapping between .BTF.ext 380 + * section and corresponding ELF section. This is used to join 381 + * information like CO-RE relocation records with corresponding BPF 382 + * programs defined in ELF sections 383 + */ 384 + __u32 *sec_idxs; 385 + int sec_cnt; 379 386 }; 380 387 381 388 #define for_each_btf_ext_sec(seg, sec) \