Skip to content

Commit

Permalink
selftests/bpf: Add tests for symbol versioning for uprobe
Browse files Browse the repository at this point in the history
This exercises the newly added dynsym symbol versioning logics.
Now we accept symbols in form of func, func@LIB_VERSION or
func@@LIB_VERSION.

The test rely on liburandom_read.so. For liburandom_read.so, we have:

    $ nm -D liburandom_read.so
                     w __cxa_finalize@GLIBC_2.17
                     w __gmon_start__
                     w _ITM_deregisterTMCloneTable
                     w _ITM_registerTMCloneTable
    0000000000000000 A LIBURANDOM_READ_1.0.0
    0000000000000000 A LIBURANDOM_READ_2.0.0
    000000000000081c T urandlib_api@@LIBURANDOM_READ_2.0.0
    0000000000000814 T urandlib_api@LIBURANDOM_READ_1.0.0
    0000000000000824 T urandlib_api_sameoffset@LIBURANDOM_READ_1.0.0
    0000000000000824 T urandlib_api_sameoffset@@LIBURANDOM_READ_2.0.0
    000000000000082c T urandlib_read_without_sema@@LIBURANDOM_READ_1.0.0
    00000000000007c4 T urandlib_read_with_sema@@LIBURANDOM_READ_1.0.0
    0000000000011018 D urandlib_read_with_sema_semaphore@@LIBURANDOM_READ_1.0.0

For `urandlib_api`, specifying `urandlib_api` will cause a conflict because
there are two symbols named urandlib_api and both are global bind.
For `urandlib_api_sameoffset`, there are also two symbols in the .so, but
both are at the same offset and essentially they refer to the same function
so no conflict.

Reviewed-by: Alan Maguire <alan.maguire@oracle.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Signed-off-by: Hengqi Chen <hengqi.chen@gmail.com>
  • Loading branch information
chenhengqi authored and Kernel Patches Daemon committed Sep 12, 2023
1 parent 6f81dea commit 9464edb
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 2 deletions.
5 changes: 3 additions & 2 deletions tools/testing/selftests/bpf/Makefile
Expand Up @@ -196,11 +196,12 @@ endif

# Filter out -static for liburandom_read.so and its dependent targets so that static builds
# do not fail. Static builds leave urandom_read relying on system-wide shared libraries.
$(OUTPUT)/liburandom_read.so: urandom_read_lib1.c urandom_read_lib2.c
$(OUTPUT)/liburandom_read.so: urandom_read_lib1.c urandom_read_lib2.c liburandom_read.map
$(call msg,LIB,,$@)
$(Q)$(CLANG) $(filter-out -static,$(CFLAGS) $(LDFLAGS)) \
$^ $(filter-out -static,$(LDLIBS)) \
$(filter %.c,$^) $(filter-out -static,$(LDLIBS)) \
-fuse-ld=$(LLD) -Wl,-znoseparate-code -Wl,--build-id=sha1 \
-Wl,--version-script=liburandom_read.map \
-fPIC -shared -o $@

$(OUTPUT)/urandom_read: urandom_read.c urandom_read_aux.c $(OUTPUT)/liburandom_read.so
Expand Down
15 changes: 15 additions & 0 deletions tools/testing/selftests/bpf/liburandom_read.map
@@ -0,0 +1,15 @@
LIBURANDOM_READ_1.0.0 {
global:
urandlib_api;
urandlib_api_sameoffset;
urandlib_read_without_sema;
urandlib_read_with_sema;
urandlib_read_with_sema_semaphore;
local:
*;
};

LIBURANDOM_READ_2.0.0 {
global:
urandlib_api;
} LIBURANDOM_READ_1.0.0;
95 changes: 95 additions & 0 deletions tools/testing/selftests/bpf/prog_tests/uprobe.c
@@ -0,0 +1,95 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023 Hengqi Chen */

#include <test_progs.h>
#include "test_uprobe.skel.h"

static FILE *urand_spawn(int *pid)
{
FILE *f;

/* urandom_read's stdout is wired into f */
f = popen("./urandom_read 1 report-pid", "r");
if (!f)
return NULL;

if (fscanf(f, "%d", pid) != 1) {
pclose(f);
errno = EINVAL;
return NULL;
}

return f;
}

static int urand_trigger(FILE **urand_pipe)
{
int exit_code;

/* pclose() waits for child process to exit and returns their exit code */
exit_code = pclose(*urand_pipe);
*urand_pipe = NULL;

return exit_code;
}

void test_uprobe(void)
{
LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts);
struct test_uprobe *skel;
FILE *urand_pipe = NULL;
int urand_pid = 0, err;

skel = test_uprobe__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_open"))
return;

urand_pipe = urand_spawn(&urand_pid);
if (!ASSERT_OK_PTR(urand_pipe, "urand_spawn"))
goto cleanup;

skel->bss->my_pid = urand_pid;

/* Manual attach uprobe to urandlib_api
* There are two `urandlib_api` symbols in .dynsym section:
* - urandlib_api@LIBURANDOM_READ_1.0.0
* - urandlib_api@@LIBURANDOM_READ_2.0.0
* Both are global bind and would cause a conflict if user
* specify the symbol name without a version suffix
*/
uprobe_opts.func_name = "urandlib_api";
skel->links.test4 = bpf_program__attach_uprobe_opts(skel->progs.test4,
urand_pid,
"./liburandom_read.so",
0 /* offset */,
&uprobe_opts);
if (!ASSERT_ERR_PTR(skel->links.test4, "urandlib_api_attach_conflict"))
goto cleanup;

uprobe_opts.func_name = "urandlib_api@LIBURANDOM_READ_1.0.0";
skel->links.test4 = bpf_program__attach_uprobe_opts(skel->progs.test4,
urand_pid,
"./liburandom_read.so",
0 /* offset */,
&uprobe_opts);
if (!ASSERT_OK_PTR(skel->links.test4, "urandlib_api_attach_ok"))
goto cleanup;

/* Auto attach 3 u[ret]probes to urandlib_api_sameoffset */
err = test_uprobe__attach(skel);
if (!ASSERT_OK(err, "skel_attach"))
goto cleanup;

/* trigger urandom_read */
ASSERT_OK(urand_trigger(&urand_pipe), "urand_exit_code");

ASSERT_EQ(skel->bss->test1_result, 1, "urandlib_api_sameoffset");
ASSERT_EQ(skel->bss->test2_result, 1, "urandlib_api_sameoffset@v1");
ASSERT_EQ(skel->bss->test3_result, 3, "urandlib_api_sameoffset@@v2");
ASSERT_EQ(skel->bss->test4_result, 1, "urandlib_api");

cleanup:
if (urand_pipe)
pclose(urand_pipe);
test_uprobe__destroy(skel);
}
61 changes: 61 additions & 0 deletions tools/testing/selftests/bpf/progs/test_uprobe.c
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2023 Hengqi Chen */

#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>

pid_t my_pid = 0;

int test1_result = 0;
int test2_result = 0;
int test3_result = 0;
int test4_result = 0;

SEC("uprobe/./liburandom_read.so:urandlib_api_sameoffset")
int BPF_UPROBE(test1)
{
pid_t pid = bpf_get_current_pid_tgid() >> 32;

if (pid != my_pid)
return 0;

test1_result = 1;
return 0;
}

SEC("uprobe/./liburandom_read.so:urandlib_api_sameoffset@LIBURANDOM_READ_1.0.0")
int BPF_UPROBE(test2)
{
pid_t pid = bpf_get_current_pid_tgid() >> 32;

if (pid != my_pid)
return 0;

test2_result = 1;
return 0;
}

SEC("uretprobe/./liburandom_read.so:urandlib_api_sameoffset@@LIBURANDOM_READ_2.0.0")
int BPF_URETPROBE(test3, int ret)
{
pid_t pid = bpf_get_current_pid_tgid() >> 32;

if (pid != my_pid)
return 0;

test3_result = ret;
return 0;
}

SEC("uprobe")
int BPF_UPROBE(test4)
{
pid_t pid = bpf_get_current_pid_tgid() >> 32;

if (pid != my_pid)
return 0;

test4_result = 1;
return 0;
}
9 changes: 9 additions & 0 deletions tools/testing/selftests/bpf/urandom_read.c
Expand Up @@ -21,6 +21,11 @@ void urand_read_without_sema(int iter_num, int iter_cnt, int read_sz);
void urandlib_read_with_sema(int iter_num, int iter_cnt, int read_sz);
void urandlib_read_without_sema(int iter_num, int iter_cnt, int read_sz);

int urandlib_api(void);
__asm__(".symver urandlib_api_old,urandlib_api@LIBURANDOM_READ_1.0.0");
int urandlib_api_old(void);
int urandlib_api_sameoffset(void);

unsigned short urand_read_with_sema_semaphore SEC(".probes");

static __attribute__((noinline))
Expand Down Expand Up @@ -83,6 +88,10 @@ int main(int argc, char *argv[])

urandom_read(fd, count);

urandlib_api();
urandlib_api_old();
urandlib_api_sameoffset();

close(fd);
return 0;
}
41 changes: 41 additions & 0 deletions tools/testing/selftests/bpf/urandom_read_lib1.c
Expand Up @@ -11,3 +11,44 @@ void urandlib_read_with_sema(int iter_num, int iter_cnt, int read_sz)
{
STAP_PROBE3(urandlib, read_with_sema, iter_num, iter_cnt, read_sz);
}

/* Symbol versioning is different between static and shared library.
* Properly versioned symbols are needed for shared library, but
* only the symbol of the new version is needed for static library.
* Starting with GNU C 10, use symver attribute instead of .symver assembler
* directive, which works better with GCC LTO builds.
*/
#if defined(__GNUC__) && __GNUC__ >= 10

#define DEFAULT_VERSION(internal_name, api_name, version) \
__attribute__((symver(#api_name "@@" #version)))
#define COMPAT_VERSION(internal_name, api_name, version) \
__attribute__((symver(#api_name "@" #version)))

#else

#define COMPAT_VERSION(internal_name, api_name, version) \
asm(".symver " #internal_name "," #api_name "@" #version);
#define DEFAULT_VERSION(internal_name, api_name, version) \
asm(".symver " #internal_name "," #api_name "@@" #version);

#endif

COMPAT_VERSION(urandlib_api_v1, urandlib_api, LIBURANDOM_READ_1.0.0)
int urandlib_api_v1(void)
{
return 1;
}

DEFAULT_VERSION(urandlib_api_v2, urandlib_api, LIBURANDOM_READ_2.0.0)
int urandlib_api_v2(void)
{
return 2;
}

COMPAT_VERSION(urandlib_api_sameoffset, urandlib_api_sameoffset, LIBURANDOM_READ_1.0.0)
DEFAULT_VERSION(urandlib_api_sameoffset, urandlib_api_sameoffset, LIBURANDOM_READ_2.0.0)
int urandlib_api_sameoffset(void)
{
return 3;
}

0 comments on commit 9464edb

Please sign in to comment.