diff mbox series

[v2,bpf-next,15/17] selftests/bpf: add function linking selftest

Message ID 20210416202404.3443623-16-andrii@kernel.org
State Superseded
Headers show
Series BPF static linker: support externs | expand

Commit Message

Andrii Nakryiko April 16, 2021, 8:24 p.m. UTC
Add selftest validating various aspects of statically linking functions:
  - no conflicts and correct resolution for name-conflicting static funcs;
  - correct resolution of extern functions;
  - correct handling of weak functions, both resolution itself and libbpf's
    handling of unused weak function that "lost" (it leaves gaps in code with
    no ELF symbols);
  - correct handling of hidden visibility to turn global function into
    "static" for the purpose of BPF verification.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 tools/testing/selftests/bpf/Makefile          |  3 +-
 .../selftests/bpf/prog_tests/linked_funcs.c   | 42 +++++++++++
 .../selftests/bpf/progs/linked_funcs1.c       | 73 +++++++++++++++++++
 .../selftests/bpf/progs/linked_funcs2.c       | 73 +++++++++++++++++++
 4 files changed, 190 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/linked_funcs.c
 create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs1.c
 create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs2.c

Comments

Yonghong Song April 23, 2021, 12:50 a.m. UTC | #1
On 4/16/21 1:24 PM, Andrii Nakryiko wrote:
> Add selftest validating various aspects of statically linking functions:

>    - no conflicts and correct resolution for name-conflicting static funcs;

>    - correct resolution of extern functions;

>    - correct handling of weak functions, both resolution itself and libbpf's

>      handling of unused weak function that "lost" (it leaves gaps in code with

>      no ELF symbols);

>    - correct handling of hidden visibility to turn global function into

>      "static" for the purpose of BPF verification.

> 

> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>


Ack with a small nit below.

Acked-by: Yonghong Song <yhs@fb.com>


> ---

>   tools/testing/selftests/bpf/Makefile          |  3 +-

>   .../selftests/bpf/prog_tests/linked_funcs.c   | 42 +++++++++++

>   .../selftests/bpf/progs/linked_funcs1.c       | 73 +++++++++++++++++++

>   .../selftests/bpf/progs/linked_funcs2.c       | 73 +++++++++++++++++++

>   4 files changed, 190 insertions(+), 1 deletion(-)

>   create mode 100644 tools/testing/selftests/bpf/prog_tests/linked_funcs.c

>   create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs1.c

>   create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs2.c

> 

> diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile

> index 666b462c1218..427ccfec1a6a 100644

> --- a/tools/testing/selftests/bpf/Makefile

> +++ b/tools/testing/selftests/bpf/Makefile

> @@ -308,9 +308,10 @@ endef

>   

>   SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c

>   

> -LINKED_SKELS := test_static_linked.skel.h

> +LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h

>   

>   test_static_linked.skel.h-deps := test_static_linked1.o test_static_linked2.o

> +linked_funcs.skel.h-deps := linked_funcs1.o linked_funcs2.o

>   

>   LINKED_BPF_SRCS := $(patsubst %.o,%.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps)))

>   

> diff --git a/tools/testing/selftests/bpf/prog_tests/linked_funcs.c b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c

> new file mode 100644

> index 000000000000..03bf8ef131ce

> --- /dev/null

> +++ b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c

> @@ -0,0 +1,42 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/* Copyright (c) 2021 Facebook */

> +

> +#include <test_progs.h>

> +#include <sys/syscall.h>

> +#include "linked_funcs.skel.h"

> +

> +void test_linked_funcs(void)

> +{

> +	int err;

> +	struct linked_funcs *skel;

> +

> +	skel = linked_funcs__open();

> +	if (!ASSERT_OK_PTR(skel, "skel_open"))

> +		return;

> +

> +	skel->rodata->my_tid = syscall(SYS_gettid);

> +	skel->rodata->syscall_id = SYS_getpgid;

> +

> +	err = linked_funcs__load(skel);

> +	if (!ASSERT_OK(err, "skel_load"))

> +		goto cleanup;

> +

> +	err = linked_funcs__attach(skel);

> +	if (!ASSERT_OK(err, "skel_attach"))

> +		goto cleanup;

> +

> +	/* trigger */

> +	syscall(SYS_getpgid);

> +

> +	ASSERT_EQ(skel->bss->output_val1, 2000 + 2000, "output_val1");

> +	ASSERT_EQ(skel->bss->output_ctx1, SYS_getpgid, "output_ctx1");

> +	ASSERT_EQ(skel->bss->output_weak1, 42, "output_weak1");

> +

> +	ASSERT_EQ(skel->bss->output_val2, 2 * 1000 + 2 * (2 * 1000), "output_val2");

> +	ASSERT_EQ(skel->bss->output_ctx2, SYS_getpgid, "output_ctx2");

> +	/* output_weak2 should never be updated */

> +	ASSERT_EQ(skel->bss->output_weak2, 0, "output_weak2");

> +

> +cleanup:

> +	linked_funcs__destroy(skel);

> +}

> diff --git a/tools/testing/selftests/bpf/progs/linked_funcs1.c b/tools/testing/selftests/bpf/progs/linked_funcs1.c

> new file mode 100644

> index 000000000000..cc621d4e4d82

> --- /dev/null

> +++ b/tools/testing/selftests/bpf/progs/linked_funcs1.c

> @@ -0,0 +1,73 @@

> +// SPDX-License-Identifier: GPL-2.0

> +/* Copyright (c) 2021 Facebook */

> +

> +#include "vmlinux.h"

> +#include <bpf/bpf_helpers.h>

> +#include <bpf/bpf_tracing.h>

> +

> +/* weak and shared between two files */

> +const volatile int my_tid __weak = 0;

> +const volatile long syscall_id __weak = 0;


Since the new compiler (llvm13) is recommended for this patch set.
We can simplify the above two definition with
   int my_tid __weak;
   long syscall_id __weak;
The same for the other file.

But I am also okay with the current form
to *satisfy* llvm10 some people may still use.

> +

> +int output_val1 = 0;

> +int output_ctx1 = 0;

> +int output_weak1 = 0;

> +

> +/* same "subprog" name in all files, but it's ok because they all are static */

> +static __noinline int subprog(int x)

> +{

> +	/* but different formula */

> +	return x * 1;

> +}

> +

> +/* Global functions can't be void */

> +int set_output_val1(int x)

> +{

> +	output_val1 = x + subprog(x);

> +	return x;

> +}

> +

> +/* This function can't be verified as global, as it assumes raw_tp/sys_enter

> + * context and accesses syscall id (second argument). So we mark it as

> + * __hidden, so that libbpf will mark it as static in the final object file,

> + * right before verifying it in the kernel.

> + *

> + * But we don't mark it as __hidden here, rather at extern site. __hidden is

> + * "contaminating" visibility, so it will get propagated from either extern or

> + * actual definition (including from the losing __weak definition).

> + */

> +void set_output_ctx1(__u64 *ctx)

> +{

> +	output_ctx1 = ctx[1]; /* long id, same as in BPF_PROG below */

> +}

> +

> +/* this weak instance should win because it's the first one */

> +__weak int set_output_weak(int x)

> +{

> +	output_weak1 = x;

> +	return x;

> +}

> +

> +extern int set_output_val2(int x);

> +

> +/* here we'll force set_output_ctx2() to be __hidden in the final obj file */

> +__hidden extern void set_output_ctx2(__u64 *ctx);

> +

[...]
Alexei Starovoitov April 23, 2021, 2:34 a.m. UTC | #2
On Thu, Apr 22, 2021 at 5:51 PM Yonghong Song <yhs@fb.com> wrote:
> > +

> > +/* weak and shared between two files */

> > +const volatile int my_tid __weak = 0;

> > +const volatile long syscall_id __weak = 0;

>

> Since the new compiler (llvm13) is recommended for this patch set.

> We can simplify the above two definition with

>    int my_tid __weak;

>    long syscall_id __weak;

> The same for the other file.

>

> But I am also okay with the current form

> to *satisfy* llvm10 some people may still use.


The test won't work with anything, but the latest llvm trunk,
so " = 0" is useless.
Let's remove it.
Especially from the tests that rely on the latest llvm.
No one can backport the latest llvm BPF backend to llvm10 front-end.
Andrii Nakryiko April 23, 2021, 4:29 a.m. UTC | #3
On Thu, Apr 22, 2021 at 7:35 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>

> On Thu, Apr 22, 2021 at 5:51 PM Yonghong Song <yhs@fb.com> wrote:

> > > +

> > > +/* weak and shared between two files */

> > > +const volatile int my_tid __weak = 0;

> > > +const volatile long syscall_id __weak = 0;

> >

> > Since the new compiler (llvm13) is recommended for this patch set.

> > We can simplify the above two definition with

> >    int my_tid __weak;

> >    long syscall_id __weak;

> > The same for the other file.

> >

> > But I am also okay with the current form

> > to *satisfy* llvm10 some people may still use.

>

> The test won't work with anything, but the latest llvm trunk,

> so " = 0" is useless.

> Let's remove it.

> Especially from the tests that rely on the latest llvm.

> No one can backport the latest llvm BPF backend to llvm10 front-end.


Sure, I'll drop = 0, just a habit by now.
Andrii Nakryiko April 23, 2021, 5:18 p.m. UTC | #4
On Thu, Apr 22, 2021 at 5:50 PM Yonghong Song <yhs@fb.com> wrote:
>

>

>

> On 4/16/21 1:24 PM, Andrii Nakryiko wrote:

> > Add selftest validating various aspects of statically linking functions:

> >    - no conflicts and correct resolution for name-conflicting static funcs;

> >    - correct resolution of extern functions;

> >    - correct handling of weak functions, both resolution itself and libbpf's

> >      handling of unused weak function that "lost" (it leaves gaps in code with

> >      no ELF symbols);

> >    - correct handling of hidden visibility to turn global function into

> >      "static" for the purpose of BPF verification.

> >

> > Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

>

> Ack with a small nit below.

>

> Acked-by: Yonghong Song <yhs@fb.com>

>

> > ---

> >   tools/testing/selftests/bpf/Makefile          |  3 +-

> >   .../selftests/bpf/prog_tests/linked_funcs.c   | 42 +++++++++++

> >   .../selftests/bpf/progs/linked_funcs1.c       | 73 +++++++++++++++++++

> >   .../selftests/bpf/progs/linked_funcs2.c       | 73 +++++++++++++++++++

> >   4 files changed, 190 insertions(+), 1 deletion(-)

> >   create mode 100644 tools/testing/selftests/bpf/prog_tests/linked_funcs.c

> >   create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs1.c

> >   create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs2.c

> >

> > diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile

> > index 666b462c1218..427ccfec1a6a 100644

> > --- a/tools/testing/selftests/bpf/Makefile

> > +++ b/tools/testing/selftests/bpf/Makefile

> > @@ -308,9 +308,10 @@ endef

> >

> >   SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c

> >

> > -LINKED_SKELS := test_static_linked.skel.h

> > +LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h

> >

> >   test_static_linked.skel.h-deps := test_static_linked1.o test_static_linked2.o

> > +linked_funcs.skel.h-deps := linked_funcs1.o linked_funcs2.o

> >

> >   LINKED_BPF_SRCS := $(patsubst %.o,%.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps)))

> >

> > diff --git a/tools/testing/selftests/bpf/prog_tests/linked_funcs.c b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c

> > new file mode 100644

> > index 000000000000..03bf8ef131ce

> > --- /dev/null

> > +++ b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c

> > @@ -0,0 +1,42 @@

> > +// SPDX-License-Identifier: GPL-2.0

> > +/* Copyright (c) 2021 Facebook */

> > +

> > +#include <test_progs.h>

> > +#include <sys/syscall.h>

> > +#include "linked_funcs.skel.h"

> > +

> > +void test_linked_funcs(void)

> > +{

> > +     int err;

> > +     struct linked_funcs *skel;

> > +

> > +     skel = linked_funcs__open();

> > +     if (!ASSERT_OK_PTR(skel, "skel_open"))

> > +             return;

> > +

> > +     skel->rodata->my_tid = syscall(SYS_gettid);

> > +     skel->rodata->syscall_id = SYS_getpgid;

> > +

> > +     err = linked_funcs__load(skel);

> > +     if (!ASSERT_OK(err, "skel_load"))

> > +             goto cleanup;

> > +

> > +     err = linked_funcs__attach(skel);

> > +     if (!ASSERT_OK(err, "skel_attach"))

> > +             goto cleanup;

> > +

> > +     /* trigger */

> > +     syscall(SYS_getpgid);

> > +

> > +     ASSERT_EQ(skel->bss->output_val1, 2000 + 2000, "output_val1");

> > +     ASSERT_EQ(skel->bss->output_ctx1, SYS_getpgid, "output_ctx1");

> > +     ASSERT_EQ(skel->bss->output_weak1, 42, "output_weak1");

> > +

> > +     ASSERT_EQ(skel->bss->output_val2, 2 * 1000 + 2 * (2 * 1000), "output_val2");

> > +     ASSERT_EQ(skel->bss->output_ctx2, SYS_getpgid, "output_ctx2");

> > +     /* output_weak2 should never be updated */

> > +     ASSERT_EQ(skel->bss->output_weak2, 0, "output_weak2");

> > +

> > +cleanup:

> > +     linked_funcs__destroy(skel);

> > +}

> > diff --git a/tools/testing/selftests/bpf/progs/linked_funcs1.c b/tools/testing/selftests/bpf/progs/linked_funcs1.c

> > new file mode 100644

> > index 000000000000..cc621d4e4d82

> > --- /dev/null

> > +++ b/tools/testing/selftests/bpf/progs/linked_funcs1.c

> > @@ -0,0 +1,73 @@

> > +// SPDX-License-Identifier: GPL-2.0

> > +/* Copyright (c) 2021 Facebook */

> > +

> > +#include "vmlinux.h"

> > +#include <bpf/bpf_helpers.h>

> > +#include <bpf/bpf_tracing.h>

> > +

> > +/* weak and shared between two files */

> > +const volatile int my_tid __weak = 0;

> > +const volatile long syscall_id __weak = 0;

>

> Since the new compiler (llvm13) is recommended for this patch set.

> We can simplify the above two definition with

>    int my_tid __weak;

>    long syscall_id __weak;

> The same for the other file.


This is not about old vs new compilers. I wanted to use .rodata
variables, but I'll switch to .bss, no problem.

>

> But I am also okay with the current form

> to *satisfy* llvm10 some people may still use.

>

> > +

> > +int output_val1 = 0;

> > +int output_ctx1 = 0;

> > +int output_weak1 = 0;

> > +

> > +/* same "subprog" name in all files, but it's ok because they all are static */

> > +static __noinline int subprog(int x)

> > +{

> > +     /* but different formula */

> > +     return x * 1;

> > +}

> > +

> > +/* Global functions can't be void */

> > +int set_output_val1(int x)

> > +{

> > +     output_val1 = x + subprog(x);

> > +     return x;

> > +}

> > +

> > +/* This function can't be verified as global, as it assumes raw_tp/sys_enter

> > + * context and accesses syscall id (second argument). So we mark it as

> > + * __hidden, so that libbpf will mark it as static in the final object file,

> > + * right before verifying it in the kernel.

> > + *

> > + * But we don't mark it as __hidden here, rather at extern site. __hidden is

> > + * "contaminating" visibility, so it will get propagated from either extern or

> > + * actual definition (including from the losing __weak definition).

> > + */

> > +void set_output_ctx1(__u64 *ctx)

> > +{

> > +     output_ctx1 = ctx[1]; /* long id, same as in BPF_PROG below */

> > +}

> > +

> > +/* this weak instance should win because it's the first one */

> > +__weak int set_output_weak(int x)

> > +{

> > +     output_weak1 = x;

> > +     return x;

> > +}

> > +

> > +extern int set_output_val2(int x);

> > +

> > +/* here we'll force set_output_ctx2() to be __hidden in the final obj file */

> > +__hidden extern void set_output_ctx2(__u64 *ctx);

> > +

> [...]
Yonghong Song April 23, 2021, 5:35 p.m. UTC | #5
On 4/23/21 10:18 AM, Andrii Nakryiko wrote:
> On Thu, Apr 22, 2021 at 5:50 PM Yonghong Song <yhs@fb.com> wrote:

>>

>>

>>

>> On 4/16/21 1:24 PM, Andrii Nakryiko wrote:

>>> Add selftest validating various aspects of statically linking functions:

>>>     - no conflicts and correct resolution for name-conflicting static funcs;

>>>     - correct resolution of extern functions;

>>>     - correct handling of weak functions, both resolution itself and libbpf's

>>>       handling of unused weak function that "lost" (it leaves gaps in code with

>>>       no ELF symbols);

>>>     - correct handling of hidden visibility to turn global function into

>>>       "static" for the purpose of BPF verification.

>>>

>>> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

>>

>> Ack with a small nit below.

>>

>> Acked-by: Yonghong Song <yhs@fb.com>

>>

>>> ---

>>>    tools/testing/selftests/bpf/Makefile          |  3 +-

>>>    .../selftests/bpf/prog_tests/linked_funcs.c   | 42 +++++++++++

>>>    .../selftests/bpf/progs/linked_funcs1.c       | 73 +++++++++++++++++++

>>>    .../selftests/bpf/progs/linked_funcs2.c       | 73 +++++++++++++++++++

>>>    4 files changed, 190 insertions(+), 1 deletion(-)

>>>    create mode 100644 tools/testing/selftests/bpf/prog_tests/linked_funcs.c

>>>    create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs1.c

>>>    create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs2.c

>>>

>>> diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile

>>> index 666b462c1218..427ccfec1a6a 100644

>>> --- a/tools/testing/selftests/bpf/Makefile

>>> +++ b/tools/testing/selftests/bpf/Makefile

>>> @@ -308,9 +308,10 @@ endef

>>>

>>>    SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c

>>>

>>> -LINKED_SKELS := test_static_linked.skel.h

>>> +LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h

>>>

>>>    test_static_linked.skel.h-deps := test_static_linked1.o test_static_linked2.o

>>> +linked_funcs.skel.h-deps := linked_funcs1.o linked_funcs2.o

>>>

>>>    LINKED_BPF_SRCS := $(patsubst %.o,%.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps)))

>>>

>>> diff --git a/tools/testing/selftests/bpf/prog_tests/linked_funcs.c b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c

>>> new file mode 100644

>>> index 000000000000..03bf8ef131ce

>>> --- /dev/null

>>> +++ b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c

>>> @@ -0,0 +1,42 @@

>>> +// SPDX-License-Identifier: GPL-2.0

>>> +/* Copyright (c) 2021 Facebook */

>>> +

>>> +#include <test_progs.h>

>>> +#include <sys/syscall.h>

>>> +#include "linked_funcs.skel.h"

>>> +

>>> +void test_linked_funcs(void)

>>> +{

>>> +     int err;

>>> +     struct linked_funcs *skel;

>>> +

>>> +     skel = linked_funcs__open();

>>> +     if (!ASSERT_OK_PTR(skel, "skel_open"))

>>> +             return;

>>> +

>>> +     skel->rodata->my_tid = syscall(SYS_gettid);

>>> +     skel->rodata->syscall_id = SYS_getpgid;

>>> +

>>> +     err = linked_funcs__load(skel);

>>> +     if (!ASSERT_OK(err, "skel_load"))

>>> +             goto cleanup;

>>> +

>>> +     err = linked_funcs__attach(skel);

>>> +     if (!ASSERT_OK(err, "skel_attach"))

>>> +             goto cleanup;

>>> +

>>> +     /* trigger */

>>> +     syscall(SYS_getpgid);

>>> +

>>> +     ASSERT_EQ(skel->bss->output_val1, 2000 + 2000, "output_val1");

>>> +     ASSERT_EQ(skel->bss->output_ctx1, SYS_getpgid, "output_ctx1");

>>> +     ASSERT_EQ(skel->bss->output_weak1, 42, "output_weak1");

>>> +

>>> +     ASSERT_EQ(skel->bss->output_val2, 2 * 1000 + 2 * (2 * 1000), "output_val2");

>>> +     ASSERT_EQ(skel->bss->output_ctx2, SYS_getpgid, "output_ctx2");

>>> +     /* output_weak2 should never be updated */

>>> +     ASSERT_EQ(skel->bss->output_weak2, 0, "output_weak2");

>>> +

>>> +cleanup:

>>> +     linked_funcs__destroy(skel);

>>> +}

>>> diff --git a/tools/testing/selftests/bpf/progs/linked_funcs1.c b/tools/testing/selftests/bpf/progs/linked_funcs1.c

>>> new file mode 100644

>>> index 000000000000..cc621d4e4d82

>>> --- /dev/null

>>> +++ b/tools/testing/selftests/bpf/progs/linked_funcs1.c

>>> @@ -0,0 +1,73 @@

>>> +// SPDX-License-Identifier: GPL-2.0

>>> +/* Copyright (c) 2021 Facebook */

>>> +

>>> +#include "vmlinux.h"

>>> +#include <bpf/bpf_helpers.h>

>>> +#include <bpf/bpf_tracing.h>

>>> +

>>> +/* weak and shared between two files */

>>> +const volatile int my_tid __weak = 0;

>>> +const volatile long syscall_id __weak = 0;

>>

>> Since the new compiler (llvm13) is recommended for this patch set.

>> We can simplify the above two definition with

>>     int my_tid __weak;

>>     long syscall_id __weak;

>> The same for the other file.

> 

> This is not about old vs new compilers. I wanted to use .rodata

> variables, but I'll switch to .bss, no problem.


I see. You can actually hone one "const volatile ing my_tid __weak = 0" 
and another "long syscall_id __weak". This way, you will be able to
test both .rodata and .bss section.

> 

>>

>> But I am also okay with the current form

>> to *satisfy* llvm10 some people may still use.

>>

>>> +

>>> +int output_val1 = 0;

>>> +int output_ctx1 = 0;

>>> +int output_weak1 = 0;

>>> +

>>> +/* same "subprog" name in all files, but it's ok because they all are static */

>>> +static __noinline int subprog(int x)

>>> +{

>>> +     /* but different formula */

>>> +     return x * 1;

>>> +}

>>> +

>>> +/* Global functions can't be void */

>>> +int set_output_val1(int x)

>>> +{

>>> +     output_val1 = x + subprog(x);

>>> +     return x;

>>> +}

>>> +

>>> +/* This function can't be verified as global, as it assumes raw_tp/sys_enter

>>> + * context and accesses syscall id (second argument). So we mark it as

>>> + * __hidden, so that libbpf will mark it as static in the final object file,

>>> + * right before verifying it in the kernel.

>>> + *

>>> + * But we don't mark it as __hidden here, rather at extern site. __hidden is

>>> + * "contaminating" visibility, so it will get propagated from either extern or

>>> + * actual definition (including from the losing __weak definition).

>>> + */

>>> +void set_output_ctx1(__u64 *ctx)

>>> +{

>>> +     output_ctx1 = ctx[1]; /* long id, same as in BPF_PROG below */

>>> +}

>>> +

>>> +/* this weak instance should win because it's the first one */

>>> +__weak int set_output_weak(int x)

>>> +{

>>> +     output_weak1 = x;

>>> +     return x;

>>> +}

>>> +

>>> +extern int set_output_val2(int x);

>>> +

>>> +/* here we'll force set_output_ctx2() to be __hidden in the final obj file */

>>> +__hidden extern void set_output_ctx2(__u64 *ctx);

>>> +

>> [...]
Andrii Nakryiko April 23, 2021, 5:55 p.m. UTC | #6
On Fri, Apr 23, 2021 at 10:35 AM Yonghong Song <yhs@fb.com> wrote:
>

>

>

> On 4/23/21 10:18 AM, Andrii Nakryiko wrote:

> > On Thu, Apr 22, 2021 at 5:50 PM Yonghong Song <yhs@fb.com> wrote:

> >>

> >>

> >>

> >> On 4/16/21 1:24 PM, Andrii Nakryiko wrote:

> >>> Add selftest validating various aspects of statically linking functions:

> >>>     - no conflicts and correct resolution for name-conflicting static funcs;

> >>>     - correct resolution of extern functions;

> >>>     - correct handling of weak functions, both resolution itself and libbpf's

> >>>       handling of unused weak function that "lost" (it leaves gaps in code with

> >>>       no ELF symbols);

> >>>     - correct handling of hidden visibility to turn global function into

> >>>       "static" for the purpose of BPF verification.

> >>>

> >>> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

> >>

> >> Ack with a small nit below.

> >>

> >> Acked-by: Yonghong Song <yhs@fb.com>

> >>

> >>> ---

> >>>    tools/testing/selftests/bpf/Makefile          |  3 +-

> >>>    .../selftests/bpf/prog_tests/linked_funcs.c   | 42 +++++++++++

> >>>    .../selftests/bpf/progs/linked_funcs1.c       | 73 +++++++++++++++++++

> >>>    .../selftests/bpf/progs/linked_funcs2.c       | 73 +++++++++++++++++++

> >>>    4 files changed, 190 insertions(+), 1 deletion(-)

> >>>    create mode 100644 tools/testing/selftests/bpf/prog_tests/linked_funcs.c

> >>>    create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs1.c

> >>>    create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs2.c

> >>>

> >>> diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile

> >>> index 666b462c1218..427ccfec1a6a 100644

> >>> --- a/tools/testing/selftests/bpf/Makefile

> >>> +++ b/tools/testing/selftests/bpf/Makefile

> >>> @@ -308,9 +308,10 @@ endef

> >>>

> >>>    SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c

> >>>

> >>> -LINKED_SKELS := test_static_linked.skel.h

> >>> +LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h

> >>>

> >>>    test_static_linked.skel.h-deps := test_static_linked1.o test_static_linked2.o

> >>> +linked_funcs.skel.h-deps := linked_funcs1.o linked_funcs2.o

> >>>

> >>>    LINKED_BPF_SRCS := $(patsubst %.o,%.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps)))

> >>>

> >>> diff --git a/tools/testing/selftests/bpf/prog_tests/linked_funcs.c b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c

> >>> new file mode 100644

> >>> index 000000000000..03bf8ef131ce

> >>> --- /dev/null

> >>> +++ b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c

> >>> @@ -0,0 +1,42 @@

> >>> +// SPDX-License-Identifier: GPL-2.0

> >>> +/* Copyright (c) 2021 Facebook */

> >>> +

> >>> +#include <test_progs.h>

> >>> +#include <sys/syscall.h>

> >>> +#include "linked_funcs.skel.h"

> >>> +

> >>> +void test_linked_funcs(void)

> >>> +{

> >>> +     int err;

> >>> +     struct linked_funcs *skel;

> >>> +

> >>> +     skel = linked_funcs__open();

> >>> +     if (!ASSERT_OK_PTR(skel, "skel_open"))

> >>> +             return;

> >>> +

> >>> +     skel->rodata->my_tid = syscall(SYS_gettid);

> >>> +     skel->rodata->syscall_id = SYS_getpgid;

> >>> +

> >>> +     err = linked_funcs__load(skel);

> >>> +     if (!ASSERT_OK(err, "skel_load"))

> >>> +             goto cleanup;

> >>> +

> >>> +     err = linked_funcs__attach(skel);

> >>> +     if (!ASSERT_OK(err, "skel_attach"))

> >>> +             goto cleanup;

> >>> +

> >>> +     /* trigger */

> >>> +     syscall(SYS_getpgid);

> >>> +

> >>> +     ASSERT_EQ(skel->bss->output_val1, 2000 + 2000, "output_val1");

> >>> +     ASSERT_EQ(skel->bss->output_ctx1, SYS_getpgid, "output_ctx1");

> >>> +     ASSERT_EQ(skel->bss->output_weak1, 42, "output_weak1");

> >>> +

> >>> +     ASSERT_EQ(skel->bss->output_val2, 2 * 1000 + 2 * (2 * 1000), "output_val2");

> >>> +     ASSERT_EQ(skel->bss->output_ctx2, SYS_getpgid, "output_ctx2");

> >>> +     /* output_weak2 should never be updated */

> >>> +     ASSERT_EQ(skel->bss->output_weak2, 0, "output_weak2");

> >>> +

> >>> +cleanup:

> >>> +     linked_funcs__destroy(skel);

> >>> +}

> >>> diff --git a/tools/testing/selftests/bpf/progs/linked_funcs1.c b/tools/testing/selftests/bpf/progs/linked_funcs1.c

> >>> new file mode 100644

> >>> index 000000000000..cc621d4e4d82

> >>> --- /dev/null

> >>> +++ b/tools/testing/selftests/bpf/progs/linked_funcs1.c

> >>> @@ -0,0 +1,73 @@

> >>> +// SPDX-License-Identifier: GPL-2.0

> >>> +/* Copyright (c) 2021 Facebook */

> >>> +

> >>> +#include "vmlinux.h"

> >>> +#include <bpf/bpf_helpers.h>

> >>> +#include <bpf/bpf_tracing.h>

> >>> +

> >>> +/* weak and shared between two files */

> >>> +const volatile int my_tid __weak = 0;

> >>> +const volatile long syscall_id __weak = 0;

> >>

> >> Since the new compiler (llvm13) is recommended for this patch set.

> >> We can simplify the above two definition with

> >>     int my_tid __weak;

> >>     long syscall_id __weak;

> >> The same for the other file.

> >

> > This is not about old vs new compilers. I wanted to use .rodata

> > variables, but I'll switch to .bss, no problem.

>

> I see. You can actually hone one "const volatile ing my_tid __weak = 0"

> and another "long syscall_id __weak". This way, you will be able to

> test both .rodata and .bss section.


I wonder if you meant to have one my_tid __weak in .bss and another
my_tid __weak in .rodata. Or just my_tid in .bss and syscall_id in
.rodata?

If the former (mixing ELF sections across definitions of the same
symbol), then it's disallowed right now. libbpf will error out on
mismatched sections. I tested this with normal compilation, it does
work and the final section is the section of the winner.

But I think that's quite confusing, actually, so I'm going to leave it
disallowed for now. E.g., if one file expects a read-write variable
and another expects that same variable to be read-only, and the winner
ends up being read-only one, then the file expecting read-write will
essentially have incorrect code (and will be rejected by BPF verifier,
if anything attempts to write). So I think it's better to reject it at
the linking time.

But I'll do one (my_tid) as .bss, and another (syscall_id) as .rodata.

>

> >

> >>

> >> But I am also okay with the current form

> >> to *satisfy* llvm10 some people may still use.

> >>

> >>> +

> >>> +int output_val1 = 0;

> >>> +int output_ctx1 = 0;

> >>> +int output_weak1 = 0;

> >>> +

> >>> +/* same "subprog" name in all files, but it's ok because they all are static */

> >>> +static __noinline int subprog(int x)

> >>> +{

> >>> +     /* but different formula */

> >>> +     return x * 1;

> >>> +}

> >>> +

> >>> +/* Global functions can't be void */

> >>> +int set_output_val1(int x)

> >>> +{

> >>> +     output_val1 = x + subprog(x);

> >>> +     return x;

> >>> +}

> >>> +

> >>> +/* This function can't be verified as global, as it assumes raw_tp/sys_enter

> >>> + * context and accesses syscall id (second argument). So we mark it as

> >>> + * __hidden, so that libbpf will mark it as static in the final object file,

> >>> + * right before verifying it in the kernel.

> >>> + *

> >>> + * But we don't mark it as __hidden here, rather at extern site. __hidden is

> >>> + * "contaminating" visibility, so it will get propagated from either extern or

> >>> + * actual definition (including from the losing __weak definition).

> >>> + */

> >>> +void set_output_ctx1(__u64 *ctx)

> >>> +{

> >>> +     output_ctx1 = ctx[1]; /* long id, same as in BPF_PROG below */

> >>> +}

> >>> +

> >>> +/* this weak instance should win because it's the first one */

> >>> +__weak int set_output_weak(int x)

> >>> +{

> >>> +     output_weak1 = x;

> >>> +     return x;

> >>> +}

> >>> +

> >>> +extern int set_output_val2(int x);

> >>> +

> >>> +/* here we'll force set_output_ctx2() to be __hidden in the final obj file */

> >>> +__hidden extern void set_output_ctx2(__u64 *ctx);

> >>> +

> >> [...]
Yonghong Song April 23, 2021, 5:58 p.m. UTC | #7
On 4/23/21 10:55 AM, Andrii Nakryiko wrote:
> On Fri, Apr 23, 2021 at 10:35 AM Yonghong Song <yhs@fb.com> wrote:

>>

>>

>>

>> On 4/23/21 10:18 AM, Andrii Nakryiko wrote:

>>> On Thu, Apr 22, 2021 at 5:50 PM Yonghong Song <yhs@fb.com> wrote:

>>>>

>>>>

>>>>

>>>> On 4/16/21 1:24 PM, Andrii Nakryiko wrote:

>>>>> Add selftest validating various aspects of statically linking functions:

>>>>>      - no conflicts and correct resolution for name-conflicting static funcs;

>>>>>      - correct resolution of extern functions;

>>>>>      - correct handling of weak functions, both resolution itself and libbpf's

>>>>>        handling of unused weak function that "lost" (it leaves gaps in code with

>>>>>        no ELF symbols);

>>>>>      - correct handling of hidden visibility to turn global function into

>>>>>        "static" for the purpose of BPF verification.

>>>>>

>>>>> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

>>>>

>>>> Ack with a small nit below.

>>>>

>>>> Acked-by: Yonghong Song <yhs@fb.com>

>>>>

>>>>> ---

>>>>>     tools/testing/selftests/bpf/Makefile          |  3 +-

>>>>>     .../selftests/bpf/prog_tests/linked_funcs.c   | 42 +++++++++++

>>>>>     .../selftests/bpf/progs/linked_funcs1.c       | 73 +++++++++++++++++++

>>>>>     .../selftests/bpf/progs/linked_funcs2.c       | 73 +++++++++++++++++++

>>>>>     4 files changed, 190 insertions(+), 1 deletion(-)

>>>>>     create mode 100644 tools/testing/selftests/bpf/prog_tests/linked_funcs.c

>>>>>     create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs1.c

>>>>>     create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs2.c

>>>>>

>>>>> diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile

>>>>> index 666b462c1218..427ccfec1a6a 100644

>>>>> --- a/tools/testing/selftests/bpf/Makefile

>>>>> +++ b/tools/testing/selftests/bpf/Makefile

>>>>> @@ -308,9 +308,10 @@ endef

>>>>>

>>>>>     SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c

>>>>>

>>>>> -LINKED_SKELS := test_static_linked.skel.h

>>>>> +LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h

>>>>>

>>>>>     test_static_linked.skel.h-deps := test_static_linked1.o test_static_linked2.o

>>>>> +linked_funcs.skel.h-deps := linked_funcs1.o linked_funcs2.o

>>>>>

>>>>>     LINKED_BPF_SRCS := $(patsubst %.o,%.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps)))

>>>>>

>>>>> diff --git a/tools/testing/selftests/bpf/prog_tests/linked_funcs.c b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c

>>>>> new file mode 100644

>>>>> index 000000000000..03bf8ef131ce

>>>>> --- /dev/null

>>>>> +++ b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c

>>>>> @@ -0,0 +1,42 @@

>>>>> +// SPDX-License-Identifier: GPL-2.0

>>>>> +/* Copyright (c) 2021 Facebook */

>>>>> +

>>>>> +#include <test_progs.h>

>>>>> +#include <sys/syscall.h>

>>>>> +#include "linked_funcs.skel.h"

>>>>> +

>>>>> +void test_linked_funcs(void)

>>>>> +{

>>>>> +     int err;

>>>>> +     struct linked_funcs *skel;

>>>>> +

>>>>> +     skel = linked_funcs__open();

>>>>> +     if (!ASSERT_OK_PTR(skel, "skel_open"))

>>>>> +             return;

>>>>> +

>>>>> +     skel->rodata->my_tid = syscall(SYS_gettid);

>>>>> +     skel->rodata->syscall_id = SYS_getpgid;

>>>>> +

>>>>> +     err = linked_funcs__load(skel);

>>>>> +     if (!ASSERT_OK(err, "skel_load"))

>>>>> +             goto cleanup;

>>>>> +

>>>>> +     err = linked_funcs__attach(skel);

>>>>> +     if (!ASSERT_OK(err, "skel_attach"))

>>>>> +             goto cleanup;

>>>>> +

>>>>> +     /* trigger */

>>>>> +     syscall(SYS_getpgid);

>>>>> +

>>>>> +     ASSERT_EQ(skel->bss->output_val1, 2000 + 2000, "output_val1");

>>>>> +     ASSERT_EQ(skel->bss->output_ctx1, SYS_getpgid, "output_ctx1");

>>>>> +     ASSERT_EQ(skel->bss->output_weak1, 42, "output_weak1");

>>>>> +

>>>>> +     ASSERT_EQ(skel->bss->output_val2, 2 * 1000 + 2 * (2 * 1000), "output_val2");

>>>>> +     ASSERT_EQ(skel->bss->output_ctx2, SYS_getpgid, "output_ctx2");

>>>>> +     /* output_weak2 should never be updated */

>>>>> +     ASSERT_EQ(skel->bss->output_weak2, 0, "output_weak2");

>>>>> +

>>>>> +cleanup:

>>>>> +     linked_funcs__destroy(skel);

>>>>> +}

>>>>> diff --git a/tools/testing/selftests/bpf/progs/linked_funcs1.c b/tools/testing/selftests/bpf/progs/linked_funcs1.c

>>>>> new file mode 100644

>>>>> index 000000000000..cc621d4e4d82

>>>>> --- /dev/null

>>>>> +++ b/tools/testing/selftests/bpf/progs/linked_funcs1.c

>>>>> @@ -0,0 +1,73 @@

>>>>> +// SPDX-License-Identifier: GPL-2.0

>>>>> +/* Copyright (c) 2021 Facebook */

>>>>> +

>>>>> +#include "vmlinux.h"

>>>>> +#include <bpf/bpf_helpers.h>

>>>>> +#include <bpf/bpf_tracing.h>

>>>>> +

>>>>> +/* weak and shared between two files */

>>>>> +const volatile int my_tid __weak = 0;

>>>>> +const volatile long syscall_id __weak = 0;

>>>>

>>>> Since the new compiler (llvm13) is recommended for this patch set.

>>>> We can simplify the above two definition with

>>>>      int my_tid __weak;

>>>>      long syscall_id __weak;

>>>> The same for the other file.

>>>

>>> This is not about old vs new compilers. I wanted to use .rodata

>>> variables, but I'll switch to .bss, no problem.

>>

>> I see. You can actually hone one "const volatile ing my_tid __weak = 0"

>> and another "long syscall_id __weak". This way, you will be able to

>> test both .rodata and .bss section.

> 

> I wonder if you meant to have one my_tid __weak in .bss and another

> my_tid __weak in .rodata. Or just my_tid in .bss and syscall_id in

> .rodata?

> 

> If the former (mixing ELF sections across definitions of the same

> symbol), then it's disallowed right now. libbpf will error out on

> mismatched sections. I tested this with normal compilation, it does

> work and the final section is the section of the winner.

> 

> But I think that's quite confusing, actually, so I'm going to leave it

> disallowed for now. E.g., if one file expects a read-write variable

> and another expects that same variable to be read-only, and the winner

> ends up being read-only one, then the file expecting read-write will

> essentially have incorrect code (and will be rejected by BPF verifier,

> if anything attempts to write). So I think it's better to reject it at

> the linking time.

> 

> But I'll do one (my_tid) as .bss, and another (syscall_id) as .rodata.


I mean this one. Permitting the same variable in both .bss and .rodata
sections is never a good practice.

> 

>>

>>>

>>>>

>>>> But I am also okay with the current form

>>>> to *satisfy* llvm10 some people may still use.

>>>>

>>>>> +

>>>>> +int output_val1 = 0;

>>>>> +int output_ctx1 = 0;

>>>>> +int output_weak1 = 0;

>>>>> +

>>>>> +/* same "subprog" name in all files, but it's ok because they all are static */

>>>>> +static __noinline int subprog(int x)

>>>>> +{

>>>>> +     /* but different formula */

>>>>> +     return x * 1;

>>>>> +}

>>>>> +

>>>>> +/* Global functions can't be void */

>>>>> +int set_output_val1(int x)

>>>>> +{

>>>>> +     output_val1 = x + subprog(x);

>>>>> +     return x;

>>>>> +}

>>>>> +

>>>>> +/* This function can't be verified as global, as it assumes raw_tp/sys_enter

>>>>> + * context and accesses syscall id (second argument). So we mark it as

>>>>> + * __hidden, so that libbpf will mark it as static in the final object file,

>>>>> + * right before verifying it in the kernel.

>>>>> + *

>>>>> + * But we don't mark it as __hidden here, rather at extern site. __hidden is

>>>>> + * "contaminating" visibility, so it will get propagated from either extern or

>>>>> + * actual definition (including from the losing __weak definition).

>>>>> + */

>>>>> +void set_output_ctx1(__u64 *ctx)

>>>>> +{

>>>>> +     output_ctx1 = ctx[1]; /* long id, same as in BPF_PROG below */

>>>>> +}

>>>>> +

>>>>> +/* this weak instance should win because it's the first one */

>>>>> +__weak int set_output_weak(int x)

>>>>> +{

>>>>> +     output_weak1 = x;

>>>>> +     return x;

>>>>> +}

>>>>> +

>>>>> +extern int set_output_val2(int x);

>>>>> +

>>>>> +/* here we'll force set_output_ctx2() to be __hidden in the final obj file */

>>>>> +__hidden extern void set_output_ctx2(__u64 *ctx);

>>>>> +

>>>> [...]
Andrii Nakryiko April 23, 2021, 5:59 p.m. UTC | #8
On Fri, Apr 23, 2021 at 10:58 AM Yonghong Song <yhs@fb.com> wrote:
>

>

>

> On 4/23/21 10:55 AM, Andrii Nakryiko wrote:

> > On Fri, Apr 23, 2021 at 10:35 AM Yonghong Song <yhs@fb.com> wrote:

> >>

> >>

> >>

> >> On 4/23/21 10:18 AM, Andrii Nakryiko wrote:

> >>> On Thu, Apr 22, 2021 at 5:50 PM Yonghong Song <yhs@fb.com> wrote:

> >>>>

> >>>>

> >>>>

> >>>> On 4/16/21 1:24 PM, Andrii Nakryiko wrote:

> >>>>> Add selftest validating various aspects of statically linking functions:

> >>>>>      - no conflicts and correct resolution for name-conflicting static funcs;

> >>>>>      - correct resolution of extern functions;

> >>>>>      - correct handling of weak functions, both resolution itself and libbpf's

> >>>>>        handling of unused weak function that "lost" (it leaves gaps in code with

> >>>>>        no ELF symbols);

> >>>>>      - correct handling of hidden visibility to turn global function into

> >>>>>        "static" for the purpose of BPF verification.

> >>>>>

> >>>>> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>

> >>>>

> >>>> Ack with a small nit below.

> >>>>

> >>>> Acked-by: Yonghong Song <yhs@fb.com>

> >>>>

> >>>>> ---

> >>>>>     tools/testing/selftests/bpf/Makefile          |  3 +-

> >>>>>     .../selftests/bpf/prog_tests/linked_funcs.c   | 42 +++++++++++

> >>>>>     .../selftests/bpf/progs/linked_funcs1.c       | 73 +++++++++++++++++++

> >>>>>     .../selftests/bpf/progs/linked_funcs2.c       | 73 +++++++++++++++++++

> >>>>>     4 files changed, 190 insertions(+), 1 deletion(-)

> >>>>>     create mode 100644 tools/testing/selftests/bpf/prog_tests/linked_funcs.c

> >>>>>     create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs1.c

> >>>>>     create mode 100644 tools/testing/selftests/bpf/progs/linked_funcs2.c

> >>>>>

> >>>>> diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile

> >>>>> index 666b462c1218..427ccfec1a6a 100644

> >>>>> --- a/tools/testing/selftests/bpf/Makefile

> >>>>> +++ b/tools/testing/selftests/bpf/Makefile

> >>>>> @@ -308,9 +308,10 @@ endef

> >>>>>

> >>>>>     SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c

> >>>>>

> >>>>> -LINKED_SKELS := test_static_linked.skel.h

> >>>>> +LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h

> >>>>>

> >>>>>     test_static_linked.skel.h-deps := test_static_linked1.o test_static_linked2.o

> >>>>> +linked_funcs.skel.h-deps := linked_funcs1.o linked_funcs2.o

> >>>>>

> >>>>>     LINKED_BPF_SRCS := $(patsubst %.o,%.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps)))

> >>>>>

> >>>>> diff --git a/tools/testing/selftests/bpf/prog_tests/linked_funcs.c b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c

> >>>>> new file mode 100644

> >>>>> index 000000000000..03bf8ef131ce

> >>>>> --- /dev/null

> >>>>> +++ b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c

> >>>>> @@ -0,0 +1,42 @@

> >>>>> +// SPDX-License-Identifier: GPL-2.0

> >>>>> +/* Copyright (c) 2021 Facebook */

> >>>>> +

> >>>>> +#include <test_progs.h>

> >>>>> +#include <sys/syscall.h>

> >>>>> +#include "linked_funcs.skel.h"

> >>>>> +

> >>>>> +void test_linked_funcs(void)

> >>>>> +{

> >>>>> +     int err;

> >>>>> +     struct linked_funcs *skel;

> >>>>> +

> >>>>> +     skel = linked_funcs__open();

> >>>>> +     if (!ASSERT_OK_PTR(skel, "skel_open"))

> >>>>> +             return;

> >>>>> +

> >>>>> +     skel->rodata->my_tid = syscall(SYS_gettid);

> >>>>> +     skel->rodata->syscall_id = SYS_getpgid;

> >>>>> +

> >>>>> +     err = linked_funcs__load(skel);

> >>>>> +     if (!ASSERT_OK(err, "skel_load"))

> >>>>> +             goto cleanup;

> >>>>> +

> >>>>> +     err = linked_funcs__attach(skel);

> >>>>> +     if (!ASSERT_OK(err, "skel_attach"))

> >>>>> +             goto cleanup;

> >>>>> +

> >>>>> +     /* trigger */

> >>>>> +     syscall(SYS_getpgid);

> >>>>> +

> >>>>> +     ASSERT_EQ(skel->bss->output_val1, 2000 + 2000, "output_val1");

> >>>>> +     ASSERT_EQ(skel->bss->output_ctx1, SYS_getpgid, "output_ctx1");

> >>>>> +     ASSERT_EQ(skel->bss->output_weak1, 42, "output_weak1");

> >>>>> +

> >>>>> +     ASSERT_EQ(skel->bss->output_val2, 2 * 1000 + 2 * (2 * 1000), "output_val2");

> >>>>> +     ASSERT_EQ(skel->bss->output_ctx2, SYS_getpgid, "output_ctx2");

> >>>>> +     /* output_weak2 should never be updated */

> >>>>> +     ASSERT_EQ(skel->bss->output_weak2, 0, "output_weak2");

> >>>>> +

> >>>>> +cleanup:

> >>>>> +     linked_funcs__destroy(skel);

> >>>>> +}

> >>>>> diff --git a/tools/testing/selftests/bpf/progs/linked_funcs1.c b/tools/testing/selftests/bpf/progs/linked_funcs1.c

> >>>>> new file mode 100644

> >>>>> index 000000000000..cc621d4e4d82

> >>>>> --- /dev/null

> >>>>> +++ b/tools/testing/selftests/bpf/progs/linked_funcs1.c

> >>>>> @@ -0,0 +1,73 @@

> >>>>> +// SPDX-License-Identifier: GPL-2.0

> >>>>> +/* Copyright (c) 2021 Facebook */

> >>>>> +

> >>>>> +#include "vmlinux.h"

> >>>>> +#include <bpf/bpf_helpers.h>

> >>>>> +#include <bpf/bpf_tracing.h>

> >>>>> +

> >>>>> +/* weak and shared between two files */

> >>>>> +const volatile int my_tid __weak = 0;

> >>>>> +const volatile long syscall_id __weak = 0;

> >>>>

> >>>> Since the new compiler (llvm13) is recommended for this patch set.

> >>>> We can simplify the above two definition with

> >>>>      int my_tid __weak;

> >>>>      long syscall_id __weak;

> >>>> The same for the other file.

> >>>

> >>> This is not about old vs new compilers. I wanted to use .rodata

> >>> variables, but I'll switch to .bss, no problem.

> >>

> >> I see. You can actually hone one "const volatile ing my_tid __weak = 0"

> >> and another "long syscall_id __weak". This way, you will be able to

> >> test both .rodata and .bss section.

> >

> > I wonder if you meant to have one my_tid __weak in .bss and another

> > my_tid __weak in .rodata. Or just my_tid in .bss and syscall_id in

> > .rodata?

> >

> > If the former (mixing ELF sections across definitions of the same

> > symbol), then it's disallowed right now. libbpf will error out on

> > mismatched sections. I tested this with normal compilation, it does

> > work and the final section is the section of the winner.

> >

> > But I think that's quite confusing, actually, so I'm going to leave it

> > disallowed for now. E.g., if one file expects a read-write variable

> > and another expects that same variable to be read-only, and the winner

> > ends up being read-only one, then the file expecting read-write will

> > essentially have incorrect code (and will be rejected by BPF verifier,

> > if anything attempts to write). So I think it's better to reject it at

> > the linking time.

> >

> > But I'll do one (my_tid) as .bss, and another (syscall_id) as .rodata.

>

> I mean this one. Permitting the same variable in both .bss and .rodata

> sections is never a good practice.


Ok, cool, that's what we do right now. I wonder why it is allowed by
user-space linkers, it seems dangerous.

>

> >

> >>

> >>>

> >>>>

> >>>> But I am also okay with the current form

> >>>> to *satisfy* llvm10 some people may still use.

> >>>>

> >>>>> +

> >>>>> +int output_val1 = 0;

> >>>>> +int output_ctx1 = 0;

> >>>>> +int output_weak1 = 0;

> >>>>> +

> >>>>> +/* same "subprog" name in all files, but it's ok because they all are static */

> >>>>> +static __noinline int subprog(int x)

> >>>>> +{

> >>>>> +     /* but different formula */

> >>>>> +     return x * 1;

> >>>>> +}

> >>>>> +

> >>>>> +/* Global functions can't be void */

> >>>>> +int set_output_val1(int x)

> >>>>> +{

> >>>>> +     output_val1 = x + subprog(x);

> >>>>> +     return x;

> >>>>> +}

> >>>>> +

> >>>>> +/* This function can't be verified as global, as it assumes raw_tp/sys_enter

> >>>>> + * context and accesses syscall id (second argument). So we mark it as

> >>>>> + * __hidden, so that libbpf will mark it as static in the final object file,

> >>>>> + * right before verifying it in the kernel.

> >>>>> + *

> >>>>> + * But we don't mark it as __hidden here, rather at extern site. __hidden is

> >>>>> + * "contaminating" visibility, so it will get propagated from either extern or

> >>>>> + * actual definition (including from the losing __weak definition).

> >>>>> + */

> >>>>> +void set_output_ctx1(__u64 *ctx)

> >>>>> +{

> >>>>> +     output_ctx1 = ctx[1]; /* long id, same as in BPF_PROG below */

> >>>>> +}

> >>>>> +

> >>>>> +/* this weak instance should win because it's the first one */

> >>>>> +__weak int set_output_weak(int x)

> >>>>> +{

> >>>>> +     output_weak1 = x;

> >>>>> +     return x;

> >>>>> +}

> >>>>> +

> >>>>> +extern int set_output_val2(int x);

> >>>>> +

> >>>>> +/* here we'll force set_output_ctx2() to be __hidden in the final obj file */

> >>>>> +__hidden extern void set_output_ctx2(__u64 *ctx);

> >>>>> +

> >>>> [...]
diff mbox series

Patch

diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 666b462c1218..427ccfec1a6a 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -308,9 +308,10 @@  endef
 
 SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c
 
-LINKED_SKELS := test_static_linked.skel.h
+LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h
 
 test_static_linked.skel.h-deps := test_static_linked1.o test_static_linked2.o
+linked_funcs.skel.h-deps := linked_funcs1.o linked_funcs2.o
 
 LINKED_BPF_SRCS := $(patsubst %.o,%.c,$(foreach skel,$(LINKED_SKELS),$($(skel)-deps)))
 
diff --git a/tools/testing/selftests/bpf/prog_tests/linked_funcs.c b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c
new file mode 100644
index 000000000000..03bf8ef131ce
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/linked_funcs.c
@@ -0,0 +1,42 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2021 Facebook */
+
+#include <test_progs.h>
+#include <sys/syscall.h>
+#include "linked_funcs.skel.h"
+
+void test_linked_funcs(void)
+{
+	int err;
+	struct linked_funcs *skel;
+
+	skel = linked_funcs__open();
+	if (!ASSERT_OK_PTR(skel, "skel_open"))
+		return;
+
+	skel->rodata->my_tid = syscall(SYS_gettid);
+	skel->rodata->syscall_id = SYS_getpgid;
+
+	err = linked_funcs__load(skel);
+	if (!ASSERT_OK(err, "skel_load"))
+		goto cleanup;
+
+	err = linked_funcs__attach(skel);
+	if (!ASSERT_OK(err, "skel_attach"))
+		goto cleanup;
+
+	/* trigger */
+	syscall(SYS_getpgid);
+
+	ASSERT_EQ(skel->bss->output_val1, 2000 + 2000, "output_val1");
+	ASSERT_EQ(skel->bss->output_ctx1, SYS_getpgid, "output_ctx1");
+	ASSERT_EQ(skel->bss->output_weak1, 42, "output_weak1");
+
+	ASSERT_EQ(skel->bss->output_val2, 2 * 1000 + 2 * (2 * 1000), "output_val2");
+	ASSERT_EQ(skel->bss->output_ctx2, SYS_getpgid, "output_ctx2");
+	/* output_weak2 should never be updated */
+	ASSERT_EQ(skel->bss->output_weak2, 0, "output_weak2");
+
+cleanup:
+	linked_funcs__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/linked_funcs1.c b/tools/testing/selftests/bpf/progs/linked_funcs1.c
new file mode 100644
index 000000000000..cc621d4e4d82
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/linked_funcs1.c
@@ -0,0 +1,73 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2021 Facebook */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+/* weak and shared between two files */
+const volatile int my_tid __weak = 0;
+const volatile long syscall_id __weak = 0;
+
+int output_val1 = 0;
+int output_ctx1 = 0;
+int output_weak1 = 0;
+
+/* same "subprog" name in all files, but it's ok because they all are static */
+static __noinline int subprog(int x)
+{
+	/* but different formula */
+	return x * 1;
+}
+
+/* Global functions can't be void */
+int set_output_val1(int x)
+{
+	output_val1 = x + subprog(x);
+	return x;
+}
+
+/* This function can't be verified as global, as it assumes raw_tp/sys_enter
+ * context and accesses syscall id (second argument). So we mark it as
+ * __hidden, so that libbpf will mark it as static in the final object file,
+ * right before verifying it in the kernel.
+ *
+ * But we don't mark it as __hidden here, rather at extern site. __hidden is
+ * "contaminating" visibility, so it will get propagated from either extern or
+ * actual definition (including from the losing __weak definition).
+ */
+void set_output_ctx1(__u64 *ctx)
+{
+	output_ctx1 = ctx[1]; /* long id, same as in BPF_PROG below */
+}
+
+/* this weak instance should win because it's the first one */
+__weak int set_output_weak(int x)
+{
+	output_weak1 = x;
+	return x;
+}
+
+extern int set_output_val2(int x);
+
+/* here we'll force set_output_ctx2() to be __hidden in the final obj file */
+__hidden extern void set_output_ctx2(__u64 *ctx);
+
+SEC("raw_tp/sys_enter")
+int BPF_PROG(handler1, struct pt_regs *regs, long id)
+{
+	if (my_tid != (u32)bpf_get_current_pid_tgid() || id != syscall_id)
+		return 0;
+
+	set_output_val2(1000);
+	set_output_ctx2(ctx); /* ctx definition is hidden in BPF_PROG macro */
+
+	/* keep input value the same across both files to avoid dependency on
+	 * handler call order; differentiate by output_weak1 vs output_weak2.
+	 */
+	set_output_weak(42);
+
+	return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/linked_funcs2.c b/tools/testing/selftests/bpf/progs/linked_funcs2.c
new file mode 100644
index 000000000000..a5a935a4f748
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/linked_funcs2.c
@@ -0,0 +1,73 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2021 Facebook */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+/* weak and shared between both files */
+const volatile int my_tid __weak = 0;
+const volatile long syscall_id __weak = 0;
+
+int output_val2 = 0;
+int output_ctx2 = 0;
+int output_weak2 = 0; /* should stay zero */
+
+/* same "subprog" name in all files, but it's ok because they all are static */
+static __noinline int subprog(int x)
+{
+	/* but different formula */
+	return x * 2;
+}
+
+/* Global functions can't be void */
+int set_output_val2(int x)
+{
+	output_val2 = 2 * x + 2 * subprog(x);
+	return 2 * x;
+}
+
+/* This function can't be verified as global, as it assumes raw_tp/sys_enter
+ * context and accesses syscall id (second argument). So we mark it as
+ * __hidden, so that libbpf will mark it as static in the final object file,
+ * right before verifying it in the kernel.
+ *
+ * But we don't mark it as __hidden here, rather at extern site. __hidden is
+ * "contaminating" visibility, so it will get propagated from either extern or
+ * actual definition (including from the losing __weak definition).
+ */
+void set_output_ctx2(__u64 *ctx)
+{
+	output_ctx2 = ctx[1]; /* long id, same as in BPF_PROG below */
+}
+
+/* this weak instance should lose, because it will be processed second */
+__weak int set_output_weak(int x)
+{
+	output_weak2 = x;
+	return 2 * x;
+}
+
+extern int set_output_val1(int x);
+
+/* here we'll force set_output_ctx1() to be __hidden in the final obj file */
+__hidden extern void set_output_ctx1(__u64 *ctx);
+
+SEC("raw_tp/sys_enter")
+int BPF_PROG(handler2, struct pt_regs *regs, long id)
+{
+	if (my_tid != (u32)bpf_get_current_pid_tgid() || id != syscall_id)
+		return 0;
+
+	set_output_val1(2000);
+	set_output_ctx1(ctx); /* ctx definition is hidden in BPF_PROG macro */
+
+	/* keep input value the same across both files to avoid dependency on
+	 * handler call order; differentiate by output_weak1 vs output_weak2.
+	 */
+	set_output_weak(42);
+
+	return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";