diff mbox series

[bpf-next,v2,5/5] selftests/bpf: Add a selftest with available_filter_functions_addrs

Message ID 20240321200124.2220345-1-yonghong.song@linux.dev (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series bpf: Fix a couple of test failures with LTO kernel | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 8 this patch: 8
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 19 maintainers not CCed: linux-kselftest@vger.kernel.org john.fastabend@gmail.com ndesaulniers@google.com kuba@kernel.org jolsa@kernel.org mykolal@fb.com haoluo@google.com netdev@vger.kernel.org nathan@kernel.org eddyz87@gmail.com justinstitt@google.com sdf@google.com song@kernel.org kpsingh@kernel.org shuah@kernel.org hawk@kernel.org morbo@google.com martin.lau@linux.dev llvm@lists.linux.dev
netdev/build_clang success Errors and warnings before: 8 this patch: 8
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 8 this patch: 8
netdev/checkpatch warning CHECK: No space is necessary after a cast WARNING: line length of 83 exceeds 80 columns WARNING: line length of 87 exceeds 80 columns WARNING: line length of 93 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-37 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-38 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-15 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18 and -O2 optimization
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-7 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-8 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-10 success Logs for x86_64-gcc / build-release

Commit Message

Yonghong Song March 21, 2024, 8:01 p.m. UTC
The current kprobe_multi_bench_attach/kernel test
reads sym names from /sys/kernel/tracing/available_filter_functions.
Some names do not agree with the corresponding entries in /proc/kallsyms
since the corresponding /proc/kallsyms syms have suffix '.llvm.<hash>'.
Actually, if we pass symbol names in /proc/kallsyms,
kprobe_multi_attach will be okay.

This patch added a new subtest where addresses are retrieved from
/sys/kernel/tracing/available_filter_functions_addrs, and use the
address to consule /proc/kallsyms to get the function name.

Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 .../bpf/prog_tests/kprobe_multi_test.c        | 139 ++++++++++++++++++
 1 file changed, 139 insertions(+)

Comments

Jiri Olsa March 22, 2024, 12:26 p.m. UTC | #1
On Thu, Mar 21, 2024 at 01:01:24PM -0700, Yonghong Song wrote:
> The current kprobe_multi_bench_attach/kernel test
> reads sym names from /sys/kernel/tracing/available_filter_functions.
> Some names do not agree with the corresponding entries in /proc/kallsyms
> since the corresponding /proc/kallsyms syms have suffix '.llvm.<hash>'.
> Actually, if we pass symbol names in /proc/kallsyms,
> kprobe_multi_attach will be okay.
> 
> This patch added a new subtest where addresses are retrieved from
> /sys/kernel/tracing/available_filter_functions_addrs, and use the
> address to consule /proc/kallsyms to get the function name.

hm, I don't understand the reason for this test.. AFAICS test_kprobe_multi_bench_attach
is doing that already, just reading available_filter_functions file

both available_filter_functions_addrs and available_filter_functions have the
same functions, there's just extra addresses in available_filter_functions_addrs

> +	*symsp = syms;
> +	*cntp = cnt;
> +
> +error:
> +	fclose(f);
> +	hashmap__free(map);
> +	if (err) {
> +		for (i = 0; i < cnt; i++)
> +			free(syms[i]);
> +		free(syms);
> +	}
> +	return err;
> +}
> +
>  static void test_kprobe_multi_bench_attach(bool kernel)
>  {
>  	LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
> @@ -521,6 +617,47 @@ static void test_attach_override(void)
>  	kprobe_multi_override__destroy(skel);
>  }

there's lot of duplicated code in both
  get_syms_from_addr/get_syms
  test_attach_kernel_addrs_to_sym/test_kprobe_multi_bench_attach

would be great to put it together

>  
> +static void test_attach_kernel_addrs_to_sym(void)
> +{
> +	LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
> +	struct kprobe_multi_empty *skel;
> +	struct bpf_link *link;
> +	char **syms = NULL;
> +	size_t cnt = 0;
> +	int i, err;
> +
> +	err = get_syms_from_addr(&syms, &cnt);
> +	if (err == -ENOENT) {
> +		test__skip();
> +		return;
> +	}
> +	if (!ASSERT_OK(err, "get_syms_from_addr"))
> +		return;
> +
> +	skel = kprobe_multi_empty__open_and_load();
> +	if (!ASSERT_OK_PTR(skel, "kprobe_multi_empty__open_and_load"))
> +		goto cleanup;
> +
> +	opts.syms = (const char **) syms;
> +	opts.cnt = cnt;
> +
> +	link = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kprobe_empty,
> +						     NULL, &opts);
> +
> +	if (!ASSERT_OK_PTR(link, "bpf_program__attach_kprobe_multi_opts"))
> +		goto cleanup;
> +
> +	bpf_link__destroy(link);
> +
> +cleanup:
> +	kprobe_multi_empty__destroy(skel);
> +	if (syms) {
> +		for (i = 0; i < cnt; i++)
> +			free(syms[i]);
> +		free(syms);
> +	}
> +}
> +
>  void serial_test_kprobe_multi_bench_attach(void)
>  {
>  	if (test__start_subtest("kernel"))
> @@ -550,4 +687,6 @@ void test_kprobe_multi_test(void)
>  		test_attach_api_fails();
>  	if (test__start_subtest("attach_override"))
>  		test_attach_override();
> +	if (test__start_subtest("kernel_addrs_to_sym"))
> +		test_attach_kernel_addrs_to_sym();

we moved the bench subtests to serial_test_kprobe_multi_bench_attach,
not to clash with others in parallel mode

jirka
Yonghong Song March 22, 2024, 4:07 p.m. UTC | #2
On 3/22/24 5:26 AM, Jiri Olsa wrote:
> On Thu, Mar 21, 2024 at 01:01:24PM -0700, Yonghong Song wrote:
>> The current kprobe_multi_bench_attach/kernel test
>> reads sym names from /sys/kernel/tracing/available_filter_functions.
>> Some names do not agree with the corresponding entries in /proc/kallsyms
>> since the corresponding /proc/kallsyms syms have suffix '.llvm.<hash>'.
>> Actually, if we pass symbol names in /proc/kallsyms,
>> kprobe_multi_attach will be okay.
>>
>> This patch added a new subtest where addresses are retrieved from
>> /sys/kernel/tracing/available_filter_functions_addrs, and use the
>> address to consule /proc/kallsyms to get the function name.
> hm, I don't understand the reason for this test.. AFAICS test_kprobe_multi_bench_attach
> is doing that already, just reading available_filter_functions file
>
> both available_filter_functions_addrs and available_filter_functions have the
> same functions, there's just extra addresses in available_filter_functions_addrs

The goal is to include those kernel functions filtered in patch 4.
But we cannot use the names from available_filter_functions[_addrs],
and we need to get names from /proc/kallsyms. Hence this patch.
This will test if we give names (<name>.llvm.<hash>) to kernel
for kprobe_multi_attach, things will be okay.

>
>> +	*symsp = syms;
>> +	*cntp = cnt;
>> +
>> +error:
>> +	fclose(f);
>> +	hashmap__free(map);
>> +	if (err) {
>> +		for (i = 0; i < cnt; i++)
>> +			free(syms[i]);
>> +		free(syms);
>> +	}
>> +	return err;
>> +}
>> +
>>   static void test_kprobe_multi_bench_attach(bool kernel)
>>   {
>>   	LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
>> @@ -521,6 +617,47 @@ static void test_attach_override(void)
>>   	kprobe_multi_override__destroy(skel);
>>   }
> there's lot of duplicated code in both
>    get_syms_from_addr/get_syms
>    test_attach_kernel_addrs_to_sym/test_kprobe_multi_bench_attach
>
> would be great to put it together

I will give a try in the next revision.

>
>>   
>> +static void test_attach_kernel_addrs_to_sym(void)
>> +{
>> +	LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
>> +	struct kprobe_multi_empty *skel;
>> +	struct bpf_link *link;
>> +	char **syms = NULL;
>> +	size_t cnt = 0;
>> +	int i, err;
>> +
>> +	err = get_syms_from_addr(&syms, &cnt);
>> +	if (err == -ENOENT) {
>> +		test__skip();
>> +		return;
>> +	}
>> +	if (!ASSERT_OK(err, "get_syms_from_addr"))
>> +		return;
>> +
>> +	skel = kprobe_multi_empty__open_and_load();
>> +	if (!ASSERT_OK_PTR(skel, "kprobe_multi_empty__open_and_load"))
>> +		goto cleanup;
>> +
>> +	opts.syms = (const char **) syms;
>> +	opts.cnt = cnt;
>> +
>> +	link = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kprobe_empty,
>> +						     NULL, &opts);
>> +
>> +	if (!ASSERT_OK_PTR(link, "bpf_program__attach_kprobe_multi_opts"))
>> +		goto cleanup;
>> +
>> +	bpf_link__destroy(link);
>> +
>> +cleanup:
>> +	kprobe_multi_empty__destroy(skel);
>> +	if (syms) {
>> +		for (i = 0; i < cnt; i++)
>> +			free(syms[i]);
>> +		free(syms);
>> +	}
>> +}
>> +
>>   void serial_test_kprobe_multi_bench_attach(void)
>>   {
>>   	if (test__start_subtest("kernel"))
>> @@ -550,4 +687,6 @@ void test_kprobe_multi_test(void)
>>   		test_attach_api_fails();
>>   	if (test__start_subtest("attach_override"))
>>   		test_attach_override();
>> +	if (test__start_subtest("kernel_addrs_to_sym"))
>> +		test_attach_kernel_addrs_to_sym();
> we moved the bench subtests to serial_test_kprobe_multi_bench_attach,
> not to clash with others in parallel mode

Okay, I will put it in serial mode in next revision.

>
> jirka
Andrii Nakryiko March 22, 2024, 9:58 p.m. UTC | #3
On Fri, Mar 22, 2024 at 9:07 AM Yonghong Song <yonghong.song@linux.dev> wrote:
>
>
> On 3/22/24 5:26 AM, Jiri Olsa wrote:
> > On Thu, Mar 21, 2024 at 01:01:24PM -0700, Yonghong Song wrote:
> >> The current kprobe_multi_bench_attach/kernel test
> >> reads sym names from /sys/kernel/tracing/available_filter_functions.
> >> Some names do not agree with the corresponding entries in /proc/kallsyms
> >> since the corresponding /proc/kallsyms syms have suffix '.llvm.<hash>'.
> >> Actually, if we pass symbol names in /proc/kallsyms,
> >> kprobe_multi_attach will be okay.
> >>
> >> This patch added a new subtest where addresses are retrieved from
> >> /sys/kernel/tracing/available_filter_functions_addrs, and use the
> >> address to consule /proc/kallsyms to get the function name.
> > hm, I don't understand the reason for this test.. AFAICS test_kprobe_multi_bench_attach
> > is doing that already, just reading available_filter_functions file
> >
> > both available_filter_functions_addrs and available_filter_functions have the
> > same functions, there's just extra addresses in available_filter_functions_addrs
>
> The goal is to include those kernel functions filtered in patch 4.
> But we cannot use the names from available_filter_functions[_addrs],
> and we need to get names from /proc/kallsyms. Hence this patch.
> This will test if we give names (<name>.llvm.<hash>) to kernel
> for kprobe_multi_attach, things will be okay.
>

for patch #4 it would be good to not skip those *.llvm.* functions,
but find the full name using kallsyms. While here we can use address
based multi-attachment, while getting addresses from
available_filter_functions_addrs? That way we'll have a test that
benchmarks both symbol lookup paths in the kernel (where user provides
symbol names as strings) and address-based lookup (where user provides
raw addresses).

> >
> >> +    *symsp = syms;
> >> +    *cntp = cnt;
> >> +
> >> +error:
> >> +    fclose(f);
> >> +    hashmap__free(map);
> >> +    if (err) {
> >> +            for (i = 0; i < cnt; i++)
> >> +                    free(syms[i]);
> >> +            free(syms);
> >> +    }
> >> +    return err;
> >> +}
> >> +

[...]
Yonghong Song March 22, 2024, 10:23 p.m. UTC | #4
On 3/22/24 2:58 PM, Andrii Nakryiko wrote:
> On Fri, Mar 22, 2024 at 9:07 AM Yonghong Song <yonghong.song@linux.dev> wrote:
>>
>> On 3/22/24 5:26 AM, Jiri Olsa wrote:
>>> On Thu, Mar 21, 2024 at 01:01:24PM -0700, Yonghong Song wrote:
>>>> The current kprobe_multi_bench_attach/kernel test
>>>> reads sym names from /sys/kernel/tracing/available_filter_functions.
>>>> Some names do not agree with the corresponding entries in /proc/kallsyms
>>>> since the corresponding /proc/kallsyms syms have suffix '.llvm.<hash>'.
>>>> Actually, if we pass symbol names in /proc/kallsyms,
>>>> kprobe_multi_attach will be okay.
>>>>
>>>> This patch added a new subtest where addresses are retrieved from
>>>> /sys/kernel/tracing/available_filter_functions_addrs, and use the
>>>> address to consule /proc/kallsyms to get the function name.
>>> hm, I don't understand the reason for this test.. AFAICS test_kprobe_multi_bench_attach
>>> is doing that already, just reading available_filter_functions file
>>>
>>> both available_filter_functions_addrs and available_filter_functions have the
>>> same functions, there's just extra addresses in available_filter_functions_addrs
>> The goal is to include those kernel functions filtered in patch 4.
>> But we cannot use the names from available_filter_functions[_addrs],
>> and we need to get names from /proc/kallsyms. Hence this patch.
>> This will test if we give names (<name>.llvm.<hash>) to kernel
>> for kprobe_multi_attach, things will be okay.
>>
> for patch #4 it would be good to not skip those *.llvm.* functions,
> but find the full name using kallsyms. While here we can use address

Okay, I will change patch #4 to lookup /proc/kallsyms to find the
full name. If we did that, then current patch #5 is not needed.

> based multi-attachment, while getting addresses from
> available_filter_functions_addrs? That way we'll have a test that
> benchmarks both symbol lookup paths in the kernel (where user provides
> symbol names as strings) and address-based lookup (where user provides
> raw addresses).

yes, we can use available_filter_functions_addrs
to have addrs available to kernel to do multi-attach.
will do. This will be yet another bench test.

>
>>>> +    *symsp = syms;
>>>> +    *cntp = cnt;
>>>> +
>>>> +error:
>>>> +    fclose(f);
>>>> +    hashmap__free(map);
>>>> +    if (err) {
>>>> +            for (i = 0; i < cnt; i++)
>>>> +                    free(syms[i]);
>>>> +            free(syms);
>>>> +    }
>>>> +    return err;
>>>> +}
>>>> +
> [...]
diff mbox series

Patch

diff --git a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c
index f6130f4f3d88..142309ced146 100644
--- a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c
+++ b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c
@@ -438,6 +438,102 @@  static int get_syms(char ***symsp, size_t *cntp, bool kernel)
 	return err;
 }
 
+static int get_syms_from_addr(char ***symsp, size_t *cntp)
+{
+	char *name = NULL, **syms = NULL;
+	size_t cap = 0, cnt = 0, i;
+	struct hashmap *map;
+	struct ksym *ks;
+	char buf[256];
+	FILE *f;
+	int err = 0;
+	void *addr;
+
+	if (!ASSERT_OK(load_kallsyms(), "load_kallsyms"))
+		return -EINVAL;
+	/*
+	 * The available_filter_functions contains many duplicates,
+	 * but other than that all symbols are usable in kprobe multi
+	 * interface.
+	 * Filtering out duplicates by using hashmap__add, which won't
+	 * add existing entry.
+	 */
+
+	if (access("/sys/kernel/tracing/trace", F_OK) == 0)
+		f = fopen("/sys/kernel/tracing/available_filter_functions_addrs", "r");
+	else
+		f = fopen("/sys/kernel/debug/tracing/available_filter_functions_addrs", "r");
+
+	if (!f)
+		return -ENOENT;
+
+	map = hashmap__new(symbol_hash, symbol_equal, NULL);
+	if (IS_ERR(map)) {
+		err = libbpf_get_error(map);
+		goto error;
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		if (strchr(buf, '['))
+			continue;
+
+		if (sscanf(buf, "%p$*[^\n]\n", &addr) != 1)
+			continue;
+
+		ks = ksym_search((long)addr);
+		if (!ks)
+			return -EINVAL;
+
+		name = ks->name;
+
+		/*
+		 * We attach to almost all kernel functions and some of them
+		 * will cause 'suspicious RCU usage' when fprobe is attached
+		 * to them. Filter out the current culprits - arch_cpu_idle
+		 * default_idle and rcu_* functions.
+		 */
+		if (!strcmp(name, "arch_cpu_idle"))
+			continue;
+		if (!strcmp(name, "default_idle"))
+			continue;
+		if (!strncmp(name, "rcu_", 4))
+			continue;
+		if (!strcmp(name, "bpf_dispatcher_xdp_func"))
+			continue;
+		if (!strncmp(name, "__ftrace_invalid_address__",
+			     sizeof("__ftrace_invalid_address__") - 1))
+			continue;
+
+		err = hashmap__add(map, name, 0);
+		if (err == -EEXIST) {
+			err = 0;
+			continue;
+		}
+		if (err)
+			goto error;
+
+		err = libbpf_ensure_mem((void **) &syms, &cap,
+					sizeof(*syms), cnt + 1);
+		if (err)
+			goto error;
+
+		syms[cnt++] = name;
+	}
+
+	*symsp = syms;
+	*cntp = cnt;
+
+error:
+	fclose(f);
+	hashmap__free(map);
+	if (err) {
+		for (i = 0; i < cnt; i++)
+			free(syms[i]);
+		free(syms);
+	}
+	return err;
+}
+
 static void test_kprobe_multi_bench_attach(bool kernel)
 {
 	LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
@@ -521,6 +617,47 @@  static void test_attach_override(void)
 	kprobe_multi_override__destroy(skel);
 }
 
+static void test_attach_kernel_addrs_to_sym(void)
+{
+	LIBBPF_OPTS(bpf_kprobe_multi_opts, opts);
+	struct kprobe_multi_empty *skel;
+	struct bpf_link *link;
+	char **syms = NULL;
+	size_t cnt = 0;
+	int i, err;
+
+	err = get_syms_from_addr(&syms, &cnt);
+	if (err == -ENOENT) {
+		test__skip();
+		return;
+	}
+	if (!ASSERT_OK(err, "get_syms_from_addr"))
+		return;
+
+	skel = kprobe_multi_empty__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "kprobe_multi_empty__open_and_load"))
+		goto cleanup;
+
+	opts.syms = (const char **) syms;
+	opts.cnt = cnt;
+
+	link = bpf_program__attach_kprobe_multi_opts(skel->progs.test_kprobe_empty,
+						     NULL, &opts);
+
+	if (!ASSERT_OK_PTR(link, "bpf_program__attach_kprobe_multi_opts"))
+		goto cleanup;
+
+	bpf_link__destroy(link);
+
+cleanup:
+	kprobe_multi_empty__destroy(skel);
+	if (syms) {
+		for (i = 0; i < cnt; i++)
+			free(syms[i]);
+		free(syms);
+	}
+}
+
 void serial_test_kprobe_multi_bench_attach(void)
 {
 	if (test__start_subtest("kernel"))
@@ -550,4 +687,6 @@  void test_kprobe_multi_test(void)
 		test_attach_api_fails();
 	if (test__start_subtest("attach_override"))
 		test_attach_override();
+	if (test__start_subtest("kernel_addrs_to_sym"))
+		test_attach_kernel_addrs_to_sym();
 }