• Yonghong Song's avatar
    libbpf: Handle <orig_name>.llvm.<hash> symbol properly · c56e5977
    Yonghong Song authored
    With CONFIG_LTO_CLANG_THIN enabled, with some of previous
    version of kernel code base ([1]), I hit the following
    error:
       test_ksyms:PASS:kallsyms_fopen 0 nsec
       test_ksyms:FAIL:ksym_find symbol 'bpf_link_fops' not found
       #118     ksyms:FAIL
    
    The reason is that 'bpf_link_fops' is renamed to
       bpf_link_fops.llvm.8325593422554671469
    Due to cross-file inlining, the static variable 'bpf_link_fops'
    in syscall.c is used by a function in another file. To avoid
    potential duplicated names, the llvm added suffix
    '.llvm.<hash>' ([2]) to 'bpf_link_fops' variable.
    Such renaming caused a problem in libbpf if 'bpf_link_fops'
    is used in bpf prog as a ksym but 'bpf_link_fops' does not
    match any symbol in /proc/kallsyms.
    
    To fix this issue, libbpf needs to understand that suffix '.llvm.<hash>'
    is caused by clang lto kernel and to process such symbols properly.
    
    With latest bpf-next code base built with CONFIG_LTO_CLANG_THIN,
    I cannot reproduce the above failure any more. But such an issue
    could happen with other symbols or in the future for bpf_link_fops symbol.
    
    For example, with my current kernel, I got the following from
    /proc/kallsyms:
      ffffffff84782154 d __func__.net_ratelimit.llvm.6135436931166841955
      ffffffff85f0a500 d tk_core.llvm.726630847145216431
      ffffffff85fdb960 d __fs_reclaim_map.llvm.10487989720912350772
      ffffffff864c7300 d fake_dst_ops.llvm.54750082607048300
    
    I could not easily create a selftest to test newly-added
    libbpf functionality with a static C test since I do not know
    which symbol is cross-file inlined. But based on my particular kernel,
    the following test change can run successfully.
    
    >  diff --git a/tools/testing/selftests/bpf/prog_tests/ksyms.c b/tools/testing/selftests/bpf/prog_tests/ksyms.c
    >  index 6a86d1f07800..904a103f7b1d 100644
    >  --- a/tools/testing/selftests/bpf/prog_tests/ksyms.c
    >  +++ b/tools/testing/selftests/bpf/prog_tests/ksyms.c
    >  @@ -42,6 +42,7 @@ void test_ksyms(void)
    >          ASSERT_EQ(data->out__bpf_link_fops, link_fops_addr, "bpf_link_fops");
    >          ASSERT_EQ(data->out__bpf_link_fops1, 0, "bpf_link_fops1");
    >          ASSERT_EQ(data->out__btf_size, btf_size, "btf_size");
    >  +       ASSERT_NEQ(data->out__fake_dst_ops, 0, "fake_dst_ops");
    >          ASSERT_EQ(data->out__per_cpu_start, per_cpu_start_addr, "__per_cpu_start");
    >
    >   cleanup:
    >  diff --git a/tools/testing/selftests/bpf/progs/test_ksyms.c b/tools/testing/selftests/bpf/progs/test_ksyms.c
    >  index 6c9cbb5a3bdf..fe91eef54b66 100644
    >  --- a/tools/testing/selftests/bpf/progs/test_ksyms.c
    >  +++ b/tools/testing/selftests/bpf/progs/test_ksyms.c
    >  @@ -9,11 +9,13 @@ __u64 out__bpf_link_fops = -1;
    >   __u64 out__bpf_link_fops1 = -1;
    >   __u64 out__btf_size = -1;
    >   __u64 out__per_cpu_start = -1;
    >  +__u64 out__fake_dst_ops = -1;
    >
    >   extern const void bpf_link_fops __ksym;
    >   extern const void __start_BTF __ksym;
    >   extern const void __stop_BTF __ksym;
    >   extern const void __per_cpu_start __ksym;
    >  +extern const void fake_dst_ops __ksym;
    >   /* non-existing symbol, weak, default to zero */
    >   extern const void bpf_link_fops1 __ksym __weak;
    >
    >  @@ -23,6 +25,7 @@ int handler(const void *ctx)
    >          out__bpf_link_fops = (__u64)&bpf_link_fops;
    >          out__btf_size = (__u64)(&__stop_BTF - &__start_BTF);
    >          out__per_cpu_start = (__u64)&__per_cpu_start;
    >  +       out__fake_dst_ops = (__u64)&fake_dst_ops;
    >
    >          out__bpf_link_fops1 = (__u64)&bpf_link_fops1;
    
    This patch fixed the issue in libbpf such that
    the suffix '.llvm.<hash>' will be ignored during comparison of
    bpf prog ksym vs. symbols in /proc/kallsyms, this resolved the issue.
    Currently, only static variables in /proc/kallsyms are checked
    with '.llvm.<hash>' suffix since in bpf programs function ksyms
    with '.llvm.<hash>' suffix are most likely kfunc's and unlikely
    to be cross-file inlined.
    
    Note that currently kernel does not support gcc build with lto.
    
      [1] https://lore.kernel.org/bpf/20240302165017.1627295-1-yonghong.song@linux.dev/
      [2] https://github.com/llvm/llvm-project/blob/release/18.x/llvm/include/llvm/IR/ModuleSummaryIndex.h#L1714-L1719Signed-off-by: default avatarYonghong Song <yonghong.song@linux.dev>
    Link: https://lore.kernel.org/r/20240326041458.1198161-1-yonghong.song@linux.devSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
    c56e5977
libbpf.c 370 KB