Skip to content

Commit

Permalink
selftests/bpf: fix uprobe offset calculation in selftests
Browse files Browse the repository at this point in the history
Fix how selftests determine relative offset of a function that is
uprobed. Previously, there was an assumption that uprobed function is
always in the first executable region, which is not always the case
(libbpf CI hits this case now). So get_base_addr() approach in isolation
doesn't work anymore. So teach get_uprobe_offset() to determine correct
memory mapping and calculate uprobe offset correctly.

While at it, I merged together two implementations of
get_uprobe_offset() helper, moving powerpc64-specific logic inside (had
to add extra {} block to avoid unused variable error for insn).

Also ensured that uprobed functions are never inlined, but are still
static (and thus local to each selftest), by using a no-op asm volatile
block internally. I didn't want to keep them global __weak, because some
tests use uprobe's ref counter offset (to test USDT-like logic) which is
not compatible with non-refcounted uprobe. So it's nicer to have each
test uprobe target local to the file and guaranteed to not be inlined or
skipped by the compiler (which can happen with static functions,
especially if compiling selftests with -O2).

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20220126193058.3390292-1-andrii@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
  • Loading branch information
Andrii Nakryiko authored and Alexei Starovoitov committed Jan 27, 2022
1 parent e5465a9 commit ff94368
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 66 deletions.
6 changes: 2 additions & 4 deletions tools/testing/selftests/bpf/benchs/bench_trigger.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ static void *uprobe_producer_without_nop(void *input)
static void usetup(bool use_retprobe, bool use_nop)
{
size_t uprobe_offset;
ssize_t base_addr;
struct bpf_link *link;

setup_libbpf();
Expand All @@ -165,11 +164,10 @@ static void usetup(bool use_retprobe, bool use_nop)
exit(1);
}

base_addr = get_base_addr();
if (use_nop)
uprobe_offset = get_uprobe_offset(&uprobe_target_with_nop, base_addr);
uprobe_offset = get_uprobe_offset(&uprobe_target_with_nop);
else
uprobe_offset = get_uprobe_offset(&uprobe_target_without_nop, base_addr);
uprobe_offset = get_uprobe_offset(&uprobe_target_without_nop);

link = bpf_program__attach_uprobe(ctx.skel->progs.bench_trigger_uprobe,
use_retprobe,
Expand Down
18 changes: 8 additions & 10 deletions tools/testing/selftests/bpf/prog_tests/attach_probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
/* this is how USDT semaphore is actually defined, except volatile modifier */
volatile unsigned short uprobe_ref_ctr __attribute__((unused)) __attribute((section(".probes")));

/* attach point */
static void method(void) {
return ;
/* uprobe attach point */
static void trigger_func(void)
{
asm volatile ("");
}

void test_attach_probe(void)
Expand All @@ -17,8 +18,7 @@ void test_attach_probe(void)
struct bpf_link *kprobe_link, *kretprobe_link;
struct bpf_link *uprobe_link, *uretprobe_link;
struct test_attach_probe* skel;
size_t uprobe_offset;
ssize_t base_addr, ref_ctr_offset;
ssize_t uprobe_offset, ref_ctr_offset;
bool legacy;

/* Check if new-style kprobe/uprobe API is supported.
Expand All @@ -34,11 +34,9 @@ void test_attach_probe(void)
*/
legacy = access("/sys/bus/event_source/devices/kprobe/type", F_OK) != 0;

base_addr = get_base_addr();
if (CHECK(base_addr < 0, "get_base_addr",
"failed to find base addr: %zd", base_addr))
uprobe_offset = get_uprobe_offset(&trigger_func);
if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset"))
return;
uprobe_offset = get_uprobe_offset(&method, base_addr);

ref_ctr_offset = get_rel_offset((uintptr_t)&uprobe_ref_ctr);
if (!ASSERT_GE(ref_ctr_offset, 0, "ref_ctr_offset"))
Expand Down Expand Up @@ -103,7 +101,7 @@ void test_attach_probe(void)
goto cleanup;

/* trigger & validate uprobe & uretprobe */
method();
trigger_func();

if (CHECK(skel->bss->uprobe_res != 3, "check_uprobe_res",
"wrong uprobe res: %d\n", skel->bss->uprobe_res))
Expand Down
16 changes: 11 additions & 5 deletions tools/testing/selftests/bpf/prog_tests/bpf_cookie.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
#include <test_progs.h>
#include "test_bpf_cookie.skel.h"

/* uprobe attach point */
static void trigger_func(void)
{
asm volatile ("");
}

static void kprobe_subtest(struct test_bpf_cookie *skel)
{
DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, opts);
Expand Down Expand Up @@ -62,11 +68,11 @@ static void uprobe_subtest(struct test_bpf_cookie *skel)
DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
struct bpf_link *link1 = NULL, *link2 = NULL;
struct bpf_link *retlink1 = NULL, *retlink2 = NULL;
size_t uprobe_offset;
ssize_t base_addr;
ssize_t uprobe_offset;

base_addr = get_base_addr();
uprobe_offset = get_uprobe_offset(&get_base_addr, base_addr);
uprobe_offset = get_uprobe_offset(&trigger_func);
if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset"))
goto cleanup;

/* attach two uprobes */
opts.bpf_cookie = 0x100;
Expand Down Expand Up @@ -99,7 +105,7 @@ static void uprobe_subtest(struct test_bpf_cookie *skel)
goto cleanup;

/* trigger uprobe && uretprobe */
get_base_addr();
trigger_func();

ASSERT_EQ(skel->bss->uprobe_res, 0x100 | 0x200, "uprobe_res");
ASSERT_EQ(skel->bss->uretprobe_res, 0x1000 | 0x2000, "uretprobe_res");
Expand Down
16 changes: 10 additions & 6 deletions tools/testing/selftests/bpf/prog_tests/task_pt_regs.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,22 @@
#include <test_progs.h>
#include "test_task_pt_regs.skel.h"

/* uprobe attach point */
static void trigger_func(void)
{
asm volatile ("");
}

void test_task_pt_regs(void)
{
struct test_task_pt_regs *skel;
struct bpf_link *uprobe_link;
size_t uprobe_offset;
ssize_t base_addr;
ssize_t uprobe_offset;
bool match;

base_addr = get_base_addr();
if (!ASSERT_GT(base_addr, 0, "get_base_addr"))
uprobe_offset = get_uprobe_offset(&trigger_func);
if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset"))
return;
uprobe_offset = get_uprobe_offset(&get_base_addr, base_addr);

skel = test_task_pt_regs__open_and_load();
if (!ASSERT_OK_PTR(skel, "skel_open"))
Expand All @@ -32,7 +36,7 @@ void test_task_pt_regs(void)
skel->links.handle_uprobe = uprobe_link;

/* trigger & validate uprobe */
get_base_addr();
trigger_func();

if (!ASSERT_EQ(skel->bss->uprobe_res, 1, "check_uprobe_res"))
goto cleanup;
Expand Down
70 changes: 31 additions & 39 deletions tools/testing/selftests/bpf/trace_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,17 +138,36 @@ void read_trace_pipe(void)
}
}

ssize_t get_uprobe_offset(const void *addr)
{
size_t start, end, base;
char buf[256];
bool found;
FILE *f;

f = fopen("/proc/self/maps", "r");
if (!f)
return -errno;

while (fscanf(f, "%zx-%zx %s %zx %*[^\n]\n", &start, &end, buf, &base) == 4) {
if (buf[2] == 'x' && (uintptr_t)addr >= start && (uintptr_t)addr < end) {
found = true;
break;
}
}

fclose(f);

if (!found)
return -ESRCH;

#if defined(__powerpc64__) && defined(_CALL_ELF) && _CALL_ELF == 2

#define OP_RT_RA_MASK 0xffff0000UL
#define LIS_R2 0x3c400000UL
#define ADDIS_R2_R12 0x3c4c0000UL
#define ADDI_R2_R2 0x38420000UL

ssize_t get_uprobe_offset(const void *addr, ssize_t base)
{
u32 *insn = (u32 *)(uintptr_t)addr;

/*
* A PPC64 ABIv2 function may have a local and a global entry
* point. We need to use the local entry point when patching
Expand All @@ -165,43 +184,16 @@ ssize_t get_uprobe_offset(const void *addr, ssize_t base)
* lis r2,XXXX
* addi r2,r2,XXXX
*/
if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) ||
((*insn & OP_RT_RA_MASK) == LIS_R2)) &&
((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2))
return (ssize_t)(insn + 2) - base;
else
return (uintptr_t)addr - base;
}

#else
{
const u32 *insn = (const u32 *)(uintptr_t)addr;

ssize_t get_uprobe_offset(const void *addr, ssize_t base)
{
return (uintptr_t)addr - base;
}

#endif

ssize_t get_base_addr(void)
{
size_t start, offset;
char buf[256];
FILE *f;

f = fopen("/proc/self/maps", "r");
if (!f)
return -errno;

while (fscanf(f, "%zx-%*x %s %zx %*[^\n]\n",
&start, buf, &offset) == 3) {
if (strcmp(buf, "r-xp") == 0) {
fclose(f);
return start - offset;
}
if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) ||
((*insn & OP_RT_RA_MASK) == LIS_R2)) &&
((*(insn + 1) & OP_RT_RA_MASK) == ADDI_R2_R2))
return (uintptr_t)(insn + 2) - start + base;
}

fclose(f);
return -EINVAL;
#endif
return (uintptr_t)addr - start + base;
}

ssize_t get_rel_offset(uintptr_t addr)
Expand Down
3 changes: 1 addition & 2 deletions tools/testing/selftests/bpf/trace_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ int kallsyms_find(const char *sym, unsigned long long *addr);

void read_trace_pipe(void);

ssize_t get_uprobe_offset(const void *addr, ssize_t base);
ssize_t get_base_addr(void);
ssize_t get_uprobe_offset(const void *addr);
ssize_t get_rel_offset(uintptr_t addr);

#endif

0 comments on commit ff94368

Please sign in to comment.