Add a USDT probe laid out so that its nop10 crosses a page boundary
and check that attaching to it succeeds and the probe fires. Without
the previous fix libbpf shifts the uprobe onto the nop10 and the
attach fails with -ENOTSUPP.

Signed-off-by: Jiayuan Chen <[email protected]>
---
 tools/testing/selftests/bpf/prog_tests/usdt.c | 54 +++++++++++++++++++
 tools/testing/selftests/bpf/usdt_2.c          | 16 ++++++
 2 files changed, 70 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/usdt.c 
b/tools/testing/selftests/bpf/prog_tests/usdt.c
index 8004c9568ffa..eff1e57ab13c 100644
--- a/tools/testing/selftests/bpf/prog_tests/usdt.c
+++ b/tools/testing/selftests/bpf/prog_tests/usdt.c
@@ -250,6 +250,7 @@ static void subtest_basic_usdt(bool optimized)
 #ifdef __x86_64__
 extern void usdt_1(void);
 extern void usdt_2(void);
+extern void usdt_2_cross_page(void);
 extern void usdt_red_zone_trigger(void);
 
 static unsigned char nop1[1] = { 0x90 };
@@ -342,6 +343,57 @@ static void subtest_optimized_attach(void)
        test_usdt__destroy(skel);
 }
 
+/*
+ * Test attachment to a USDT probe whose nop10 crosses a page boundary.
+ * The kernel can't optimize such nop10, so libbpf keeps the uprobe on
+ * the preceding 1-byte nop. Do not assume any particular placement
+ * here, though: however the probe ends up attached, the attachment
+ * must succeed and the probe must fire.
+ */
+static void subtest_optimized_attach_cross_page(void)
+{
+       long page_sz = sysconf(_SC_PAGESIZE);
+       struct test_usdt *skel;
+       __u8 *addr = NULL;
+       long i;
+
+       /* combo is placed up to a page of padding after the function start */
+       for (i = 0; i < 2 * page_sz; i++) {
+               if (!memcmp((void *)usdt_2_cross_page + i, nop1_nop10_combo, 
11)) {
+                       addr = (void *)usdt_2_cross_page + i;
+                       break;
+               }
+       }
+       if (!ASSERT_OK_PTR(addr, "find_nop1_nop10_combo"))
+               return;
+
+       /* layout sanity check: the nop10 must cross the page boundary */
+       if (!ASSERT_GT((unsigned long)(addr + 1) % page_sz + 10, page_sz,
+                      "nop10_crosses_page"))
+               return;
+
+       skel = test_usdt__open_and_load();
+       if (!ASSERT_OK_PTR(skel, "test_usdt__open_and_load"))
+               return;
+
+       skel->bss->my_pid = getpid();
+
+       skel->links.usdt0 = bpf_program__attach_usdt(skel->progs.usdt0,
+                                                    0 /*self*/, 
"/proc/self/exe",
+                                                    "optimized_attach",
+                                                    "usdt_2_cross_page", NULL);
+       if (!ASSERT_OK_PTR(skel->links.usdt0, "bpf_program__attach_usdt"))
+               goto cleanup;
+
+       usdt_2_cross_page();
+       usdt_2_cross_page();
+
+       ASSERT_EQ(skel->bss->usdt0_called, 2, "usdt0_called");
+
+cleanup:
+       test_usdt__destroy(skel);
+}
+
 /*
  * Test that USDT arguments survive nop10 optimization in a function where
  * the compiler places operands in the red zone.
@@ -660,6 +712,8 @@ void test_usdt(void)
                subtest_basic_usdt(true);
        if (test__start_subtest("optimized_attach"))
                subtest_optimized_attach();
+       if (test__start_subtest("optimized_attach_cross_page"))
+               subtest_optimized_attach_cross_page();
        if (test__start_subtest("optimized_red_zone"))
                subtest_optimized_red_zone();
 #endif
diff --git a/tools/testing/selftests/bpf/usdt_2.c 
b/tools/testing/selftests/bpf/usdt_2.c
index 5e38f8605b02..3b7024b9b08b 100644
--- a/tools/testing/selftests/bpf/usdt_2.c
+++ b/tools/testing/selftests/bpf/usdt_2.c
@@ -13,6 +13,22 @@ void usdt_2(void)
        USDT(optimized_attach, usdt_2);
 }
 
+/*
+ * Force the nop1,nop10 combo of the USDT probe to a spot where the nop10
+ * crosses a page boundary: .balign starts the padding exactly at a page
+ * start regardless of the compiler-generated prologue size, and the 4086
+ * one-byte nops put the nop1 at page offset 4086, so the following nop10
+ * occupies the last 9 bytes of that page and 1 byte of the next one.
+ * The kernel can't optimize such nop10, so libbpf must keep the uprobe
+ * on the 1-byte nop.
+ */
+__attribute__((noinline))
+void usdt_2_cross_page(void)
+{
+       asm volatile (".balign 4096, 0x90\n\t.skip 4086, 0x90");
+       USDT(optimized_attach, usdt_2_cross_page);
+}
+
 static volatile unsigned long usdt_red_zone_arg1 = 0xDEADBEEF;
 static volatile unsigned long usdt_red_zone_arg2 = 0xCAFEBABE;
 static volatile unsigned long usdt_red_zone_arg3 = 0xFEEDFACE;
-- 
2.43.0


Reply via email to