diff mbox series

[v5,bpf-next,3/5] libbpf: add auto-attach for uprobes based on section name

Message ID 1648654000-21758-4-git-send-email-alan.maguire@oracle.com (mailing list archive)
State Accepted
Commit 7825470420d1d57b3a1e77886c74cb47537155f6
Delegated to: BPF
Headers show
Series libbpf: name-based u[ret]probe attach | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers success CCed 10 of 10 maintainers
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/checkpatch warning WARNING: line length of 83 exceeds 80 columns WARNING: line length of 85 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns WARNING: line length of 93 exceeds 80 columns WARNING: line length of 94 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-PR fail PR summary

Commit Message

Alan Maguire March 30, 2022, 3:26 p.m. UTC
Now that u[ret]probes can use name-based specification, it makes
sense to add support for auto-attach based on SEC() definition.
The format proposed is

        SEC("u[ret]probe/binary:[raw_offset|[function_name[+offset]]")

For example, to trace malloc() in libc:

        SEC("uprobe/libc.so.6:malloc")

...or to trace function foo2 in /usr/bin/foo:

        SEC("uprobe//usr/bin/foo:foo2")

Auto-attach is done for all tasks (pid -1).  prog can be an absolute
path or simply a program/library name; in the latter case, we use
PATH/LD_LIBRARY_PATH to resolve the full path, falling back to
standard locations (/usr/bin:/usr/sbin or /usr/lib64:/usr/lib) if
the file is not found via environment-variable specified locations.

Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
---
 tools/lib/bpf/libbpf.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 72 insertions(+), 2 deletions(-)

Comments

Andrii Nakryiko April 4, 2022, 1:14 a.m. UTC | #1
On Wed, Mar 30, 2022 at 8:27 AM Alan Maguire <alan.maguire@oracle.com> wrote:
>
> Now that u[ret]probes can use name-based specification, it makes
> sense to add support for auto-attach based on SEC() definition.
> The format proposed is
>
>         SEC("u[ret]probe/binary:[raw_offset|[function_name[+offset]]")
>
> For example, to trace malloc() in libc:
>
>         SEC("uprobe/libc.so.6:malloc")
>
> ...or to trace function foo2 in /usr/bin/foo:
>
>         SEC("uprobe//usr/bin/foo:foo2")
>
> Auto-attach is done for all tasks (pid -1).  prog can be an absolute
> path or simply a program/library name; in the latter case, we use
> PATH/LD_LIBRARY_PATH to resolve the full path, falling back to
> standard locations (/usr/bin:/usr/sbin or /usr/lib64:/usr/lib) if
> the file is not found via environment-variable specified locations.
>
> Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> ---
>  tools/lib/bpf/libbpf.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 72 insertions(+), 2 deletions(-)
>

[...]

> +static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link)
> +{
> +       DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
> +       char *func, *probe_name, *func_end;
> +       char *func_name, binary_path[512];
> +       unsigned long long raw_offset;
> +       size_t offset = 0;
> +       int n;
> +
> +       *link = NULL;
> +
> +       opts.retprobe = str_has_pfx(prog->sec_name, "uretprobe/");
> +       if (opts.retprobe)
> +               probe_name = prog->sec_name + sizeof("uretprobe/") - 1;
> +       else
> +               probe_name = prog->sec_name + sizeof("uprobe/") - 1;

I think this will mishandle SEC("uretprobe"), let's fix this in a
follow up (and see a note about uretprobe selftests)

> +
> +       /* handle SEC("u[ret]probe") - format is valid, but auto-attach is impossible. */
> +       if (strlen(probe_name) == 0) {
> +               pr_debug("section '%s' is old-style u[ret]probe/function, cannot auto-attach\n",
> +                        prog->sec_name);

this seems excessive to log this, it's expected situation. The message
itself is also misleading, SEC("uretprobe") isn't old-style, it's
valid and supported case. SEC("uretprobe/something") is an error now,
so that's a different thing (let's improve handling in the follow up).

> +               return 0;
> +       }
> +       snprintf(binary_path, sizeof(binary_path), "%s", probe_name);
> +       /* ':' should be prior to function+offset */
> +       func_name = strrchr(binary_path, ':');
> +       if (!func_name) {
> +               pr_warn("section '%s' missing ':function[+offset]' specification\n",
> +                       prog->sec_name);
> +               return -EINVAL;
> +       }
> +       func_name[0] = '\0';
> +       func_name++;
> +       n = sscanf(func_name, "%m[a-zA-Z0-9_.]+%li", &func, &offset);
> +       if (n < 1) {
> +               pr_warn("uprobe name '%s' is invalid\n", func_name);
> +               return -EINVAL;
> +       }

I have this feeling that you could have simplified this a bunch with
just one sscanf. Something along the lines of
"%m[^/]/%m[^:]:%m[a-zA-Z0-9_.]+%li". If one argument matched (supposed
to be uprobe or uretprobe), then it is a no-auto-attach case, just
exit. If two matched -- invalid definition (old-style definition you
were reporting erroneously above in pr_debug). If 3 matched -- binary
+ func (or abs offset), if 4 matched - binary + func + offset. That
should cover everything, right?

Please try to do this in a follow up.

> +       if (opts.retprobe && offset != 0) {
> +               free(func);
> +               pr_warn("uretprobes do not support offset specification\n");
> +               return -EINVAL;
> +       }
> +
> +       /* Is func a raw address? */
> +       errno = 0;
> +       raw_offset = strtoull(func, &func_end, 0);
> +       if (!errno && !*func_end) {
> +               free(func);
> +               func = NULL;
> +               offset = (size_t)raw_offset;
> +       }
> +       opts.func_name = func;
> +
> +       *link = bpf_program__attach_uprobe_opts(prog, -1, binary_path, offset, &opts);
> +       free(func);
> +       return 0;

this should have been return libbpf_get_error(*link), fixed it


> +}
> +
>  struct bpf_link *bpf_program__attach_uprobe(const struct bpf_program *prog,
>                                             bool retprobe, pid_t pid,
>                                             const char *binary_path,
> --
> 1.8.3.1
>
Andrii Nakryiko April 4, 2022, 4:46 a.m. UTC | #2
On Sun, Apr 3, 2022 at 6:14 PM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Wed, Mar 30, 2022 at 8:27 AM Alan Maguire <alan.maguire@oracle.com> wrote:
> >
> > Now that u[ret]probes can use name-based specification, it makes
> > sense to add support for auto-attach based on SEC() definition.
> > The format proposed is
> >
> >         SEC("u[ret]probe/binary:[raw_offset|[function_name[+offset]]")
> >
> > For example, to trace malloc() in libc:
> >
> >         SEC("uprobe/libc.so.6:malloc")
> >
> > ...or to trace function foo2 in /usr/bin/foo:
> >
> >         SEC("uprobe//usr/bin/foo:foo2")
> >
> > Auto-attach is done for all tasks (pid -1).  prog can be an absolute
> > path or simply a program/library name; in the latter case, we use
> > PATH/LD_LIBRARY_PATH to resolve the full path, falling back to
> > standard locations (/usr/bin:/usr/sbin or /usr/lib64:/usr/lib) if
> > the file is not found via environment-variable specified locations.
> >
> > Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> > ---
> >  tools/lib/bpf/libbpf.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++--
> >  1 file changed, 72 insertions(+), 2 deletions(-)
> >
>
> [...]
>
> > +static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link)
> > +{
> > +       DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
> > +       char *func, *probe_name, *func_end;
> > +       char *func_name, binary_path[512];
> > +       unsigned long long raw_offset;
> > +       size_t offset = 0;
> > +       int n;
> > +
> > +       *link = NULL;
> > +
> > +       opts.retprobe = str_has_pfx(prog->sec_name, "uretprobe/");
> > +       if (opts.retprobe)
> > +               probe_name = prog->sec_name + sizeof("uretprobe/") - 1;
> > +       else
> > +               probe_name = prog->sec_name + sizeof("uprobe/") - 1;
>
> I think this will mishandle SEC("uretprobe"), let's fix this in a
> follow up (and see a note about uretprobe selftests)

So I actually fixed it up a little bit to avoid test failure on s390x
arch. But now it's a different problem, complaining about not being
able to resolve libc.so.6. CC'ing Ilya, but I was wondering if it's
better to use more generic "libc.so" instead of "libc.so.6"? Have you
tried that?

We should also probably refactor attach_probe.c selftest to be a
collection of subtest, so that we can blacklist only some subtests.
For now I have to blacklist it entirely on s390x.

>
> > +
> > +       /* handle SEC("u[ret]probe") - format is valid, but auto-attach is impossible. */
> > +       if (strlen(probe_name) == 0) {
> > +               pr_debug("section '%s' is old-style u[ret]probe/function, cannot auto-attach\n",
> > +                        prog->sec_name);
>
> this seems excessive to log this, it's expected situation. The message
> itself is also misleading, SEC("uretprobe") isn't old-style, it's
> valid and supported case. SEC("uretprobe/something") is an error now,
> so that's a different thing (let's improve handling in the follow up).
>
> > +               return 0;
> > +       }
> > +       snprintf(binary_path, sizeof(binary_path), "%s", probe_name);
> > +       /* ':' should be prior to function+offset */
> > +       func_name = strrchr(binary_path, ':');
> > +       if (!func_name) {
> > +               pr_warn("section '%s' missing ':function[+offset]' specification\n",
> > +                       prog->sec_name);
> > +               return -EINVAL;
> > +       }
> > +       func_name[0] = '\0';
> > +       func_name++;
> > +       n = sscanf(func_name, "%m[a-zA-Z0-9_.]+%li", &func, &offset);
> > +       if (n < 1) {
> > +               pr_warn("uprobe name '%s' is invalid\n", func_name);
> > +               return -EINVAL;
> > +       }
>
> I have this feeling that you could have simplified this a bunch with
> just one sscanf. Something along the lines of
> "%m[^/]/%m[^:]:%m[a-zA-Z0-9_.]+%li". If one argument matched (supposed
> to be uprobe or uretprobe), then it is a no-auto-attach case, just
> exit. If two matched -- invalid definition (old-style definition you
> were reporting erroneously above in pr_debug). If 3 matched -- binary
> + func (or abs offset), if 4 matched - binary + func + offset. That
> should cover everything, right?
>
> Please try to do this in a follow up.
>
> > +       if (opts.retprobe && offset != 0) {
> > +               free(func);
> > +               pr_warn("uretprobes do not support offset specification\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       /* Is func a raw address? */
> > +       errno = 0;
> > +       raw_offset = strtoull(func, &func_end, 0);
> > +       if (!errno && !*func_end) {
> > +               free(func);
> > +               func = NULL;
> > +               offset = (size_t)raw_offset;
> > +       }
> > +       opts.func_name = func;
> > +
> > +       *link = bpf_program__attach_uprobe_opts(prog, -1, binary_path, offset, &opts);
> > +       free(func);
> > +       return 0;
>
> this should have been return libbpf_get_error(*link), fixed it
>
>
> > +}
> > +
> >  struct bpf_link *bpf_program__attach_uprobe(const struct bpf_program *prog,
> >                                             bool retprobe, pid_t pid,
> >                                             const char *binary_path,
> > --
> > 1.8.3.1
> >
Andrii Nakryiko April 4, 2022, 4:49 a.m. UTC | #3
On Sun, Apr 3, 2022 at 9:46 PM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Sun, Apr 3, 2022 at 6:14 PM Andrii Nakryiko
> <andrii.nakryiko@gmail.com> wrote:
> >
> > On Wed, Mar 30, 2022 at 8:27 AM Alan Maguire <alan.maguire@oracle.com> wrote:
> > >
> > > Now that u[ret]probes can use name-based specification, it makes
> > > sense to add support for auto-attach based on SEC() definition.
> > > The format proposed is
> > >
> > >         SEC("u[ret]probe/binary:[raw_offset|[function_name[+offset]]")
> > >
> > > For example, to trace malloc() in libc:
> > >
> > >         SEC("uprobe/libc.so.6:malloc")
> > >
> > > ...or to trace function foo2 in /usr/bin/foo:
> > >
> > >         SEC("uprobe//usr/bin/foo:foo2")
> > >
> > > Auto-attach is done for all tasks (pid -1).  prog can be an absolute
> > > path or simply a program/library name; in the latter case, we use
> > > PATH/LD_LIBRARY_PATH to resolve the full path, falling back to
> > > standard locations (/usr/bin:/usr/sbin or /usr/lib64:/usr/lib) if
> > > the file is not found via environment-variable specified locations.
> > >
> > > Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> > > ---
> > >  tools/lib/bpf/libbpf.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++--
> > >  1 file changed, 72 insertions(+), 2 deletions(-)
> > >
> >
> > [...]
> >
> > > +static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link)
> > > +{
> > > +       DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
> > > +       char *func, *probe_name, *func_end;
> > > +       char *func_name, binary_path[512];
> > > +       unsigned long long raw_offset;
> > > +       size_t offset = 0;
> > > +       int n;
> > > +
> > > +       *link = NULL;
> > > +
> > > +       opts.retprobe = str_has_pfx(prog->sec_name, "uretprobe/");
> > > +       if (opts.retprobe)
> > > +               probe_name = prog->sec_name + sizeof("uretprobe/") - 1;
> > > +       else
> > > +               probe_name = prog->sec_name + sizeof("uprobe/") - 1;
> >
> > I think this will mishandle SEC("uretprobe"), let's fix this in a
> > follow up (and see a note about uretprobe selftests)
>
> So I actually fixed it up a little bit to avoid test failure on s390x
> arch. But now it's a different problem, complaining about not being
> able to resolve libc.so.6. CC'ing Ilya, but I was wondering if it's
> better to use more generic "libc.so" instead of "libc.so.6"? Have you
> tried that?

See [0] for one such failure log.

  [0] https://github.com/kernel-patches/bpf/runs/5810017263?check_suite_focus=true

>
> We should also probably refactor attach_probe.c selftest to be a
> collection of subtest, so that we can blacklist only some subtests.
> For now I have to blacklist it entirely on s390x.
>
> >
> > > +
> > > +       /* handle SEC("u[ret]probe") - format is valid, but auto-attach is impossible. */
> > > +       if (strlen(probe_name) == 0) {
> > > +               pr_debug("section '%s' is old-style u[ret]probe/function, cannot auto-attach\n",
> > > +                        prog->sec_name);
> >
> > this seems excessive to log this, it's expected situation. The message
> > itself is also misleading, SEC("uretprobe") isn't old-style, it's
> > valid and supported case. SEC("uretprobe/something") is an error now,
> > so that's a different thing (let's improve handling in the follow up).
> >
> > > +               return 0;
> > > +       }
> > > +       snprintf(binary_path, sizeof(binary_path), "%s", probe_name);
> > > +       /* ':' should be prior to function+offset */
> > > +       func_name = strrchr(binary_path, ':');
> > > +       if (!func_name) {
> > > +               pr_warn("section '%s' missing ':function[+offset]' specification\n",
> > > +                       prog->sec_name);
> > > +               return -EINVAL;
> > > +       }
> > > +       func_name[0] = '\0';
> > > +       func_name++;
> > > +       n = sscanf(func_name, "%m[a-zA-Z0-9_.]+%li", &func, &offset);
> > > +       if (n < 1) {
> > > +               pr_warn("uprobe name '%s' is invalid\n", func_name);
> > > +               return -EINVAL;
> > > +       }
> >
> > I have this feeling that you could have simplified this a bunch with
> > just one sscanf. Something along the lines of
> > "%m[^/]/%m[^:]:%m[a-zA-Z0-9_.]+%li". If one argument matched (supposed
> > to be uprobe or uretprobe), then it is a no-auto-attach case, just
> > exit. If two matched -- invalid definition (old-style definition you
> > were reporting erroneously above in pr_debug). If 3 matched -- binary
> > + func (or abs offset), if 4 matched - binary + func + offset. That
> > should cover everything, right?
> >
> > Please try to do this in a follow up.
> >
> > > +       if (opts.retprobe && offset != 0) {
> > > +               free(func);
> > > +               pr_warn("uretprobes do not support offset specification\n");
> > > +               return -EINVAL;
> > > +       }
> > > +
> > > +       /* Is func a raw address? */
> > > +       errno = 0;
> > > +       raw_offset = strtoull(func, &func_end, 0);
> > > +       if (!errno && !*func_end) {
> > > +               free(func);
> > > +               func = NULL;
> > > +               offset = (size_t)raw_offset;
> > > +       }
> > > +       opts.func_name = func;
> > > +
> > > +       *link = bpf_program__attach_uprobe_opts(prog, -1, binary_path, offset, &opts);
> > > +       free(func);
> > > +       return 0;
> >
> > this should have been return libbpf_get_error(*link), fixed it
> >
> >
> > > +}
> > > +
> > >  struct bpf_link *bpf_program__attach_uprobe(const struct bpf_program *prog,
> > >                                             bool retprobe, pid_t pid,
> > >                                             const char *binary_path,
> > > --
> > > 1.8.3.1
> > >
Ilya Leoshkevich April 4, 2022, 9:36 a.m. UTC | #4
On Sun, 2022-04-03 at 21:46 -0700, Andrii Nakryiko wrote:
> On Sun, Apr 3, 2022 at 6:14 PM Andrii Nakryiko
> <andrii.nakryiko@gmail.com> wrote:
> > 
> > On Wed, Mar 30, 2022 at 8:27 AM Alan Maguire
> > <alan.maguire@oracle.com> wrote:
> > > 
> > > Now that u[ret]probes can use name-based specification, it makes
> > > sense to add support for auto-attach based on SEC() definition.
> > > The format proposed is
> > > 
> > >        
> > > SEC("u[ret]probe/binary:[raw_offset|[function_name[+offset]]")
> > > 
> > > For example, to trace malloc() in libc:
> > > 
> > >         SEC("uprobe/libc.so.6:malloc")
> > > 
> > > ...or to trace function foo2 in /usr/bin/foo:
> > > 
> > >         SEC("uprobe//usr/bin/foo:foo2")
> > > 
> > > Auto-attach is done for all tasks (pid -1).  prog can be an
> > > absolute
> > > path or simply a program/library name; in the latter case, we use
> > > PATH/LD_LIBRARY_PATH to resolve the full path, falling back to
> > > standard locations (/usr/bin:/usr/sbin or /usr/lib64:/usr/lib) if
> > > the file is not found via environment-variable specified
> > > locations.
> > > 
> > > Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> > > ---
> > >  tools/lib/bpf/libbpf.c | 74
> > > ++++++++++++++++++++++++++++++++++++++++++++++++--
> > >  1 file changed, 72 insertions(+), 2 deletions(-)
> > > 
> > 
> > [...]
> > 
> > > +static int attach_uprobe(const struct bpf_program *prog, long
> > > cookie, struct bpf_link **link)
> > > +{
> > > +       DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
> > > +       char *func, *probe_name, *func_end;
> > > +       char *func_name, binary_path[512];
> > > +       unsigned long long raw_offset;
> > > +       size_t offset = 0;
> > > +       int n;
> > > +
> > > +       *link = NULL;
> > > +
> > > +       opts.retprobe = str_has_pfx(prog->sec_name,
> > > "uretprobe/");
> > > +       if (opts.retprobe)
> > > +               probe_name = prog->sec_name +
> > > sizeof("uretprobe/") - 1;
> > > +       else
> > > +               probe_name = prog->sec_name + sizeof("uprobe/") -
> > > 1;
> > 
> > I think this will mishandle SEC("uretprobe"), let's fix this in a
> > follow up (and see a note about uretprobe selftests)
> 
> So I actually fixed it up a little bit to avoid test failure on s390x
> arch. But now it's a different problem, complaining about not being
> able to resolve libc.so.6. CC'ing Ilya, but I was wondering if it's
> better to use more generic "libc.so" instead of "libc.so.6"? Have you
> tried that?

I believe it's a Debian-specific issue (our s390x CI image is Debian).
libc is still called libc.so.6, but it's located in
/lib/s390x-linux-gnu.
This must also be an issue on Intel and other architectures.
I'll send a patch.
Alan Maguire April 4, 2022, 9:43 p.m. UTC | #5
On Mon, 4 Apr 2022, Ilya Leoshkevich wrote:

> On Sun, 2022-04-03 at 21:46 -0700, Andrii Nakryiko wrote:
> > On Sun, Apr 3, 2022 at 6:14 PM Andrii Nakryiko
> > <andrii.nakryiko@gmail.com> wrote:
> > > 
> > > On Wed, Mar 30, 2022 at 8:27 AM Alan Maguire
> > > <alan.maguire@oracle.com> wrote:
> > > > 
> > > > Now that u[ret]probes can use name-based specification, it makes
> > > > sense to add support for auto-attach based on SEC() definition.
> > > > The format proposed is
> > > > 
> > > >        
> > > > SEC("u[ret]probe/binary:[raw_offset|[function_name[+offset]]")
> > > > 
> > > > For example, to trace malloc() in libc:
> > > > 
> > > >         SEC("uprobe/libc.so.6:malloc")
> > > > 
> > > > ...or to trace function foo2 in /usr/bin/foo:
> > > > 
> > > >         SEC("uprobe//usr/bin/foo:foo2")
> > > > 
> > > > Auto-attach is done for all tasks (pid -1).  prog can be an
> > > > absolute
> > > > path or simply a program/library name; in the latter case, we use
> > > > PATH/LD_LIBRARY_PATH to resolve the full path, falling back to
> > > > standard locations (/usr/bin:/usr/sbin or /usr/lib64:/usr/lib) if
> > > > the file is not found via environment-variable specified
> > > > locations.
> > > > 
> > > > Signed-off-by: Alan Maguire <alan.maguire@oracle.com>
> > > > ---
> > > >  tools/lib/bpf/libbpf.c | 74
> > > > ++++++++++++++++++++++++++++++++++++++++++++++++--
> > > >  1 file changed, 72 insertions(+), 2 deletions(-)
> > > > 
> > > 
> > > [...]
> > > 
> > > > +static int attach_uprobe(const struct bpf_program *prog, long
> > > > cookie, struct bpf_link **link)
> > > > +{
> > > > +       DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
> > > > +       char *func, *probe_name, *func_end;
> > > > +       char *func_name, binary_path[512];
> > > > +       unsigned long long raw_offset;
> > > > +       size_t offset = 0;
> > > > +       int n;
> > > > +
> > > > +       *link = NULL;
> > > > +
> > > > +       opts.retprobe = str_has_pfx(prog->sec_name,
> > > > "uretprobe/");
> > > > +       if (opts.retprobe)
> > > > +               probe_name = prog->sec_name +
> > > > sizeof("uretprobe/") - 1;
> > > > +       else
> > > > +               probe_name = prog->sec_name + sizeof("uprobe/") -
> > > > 1;
> > > 
> > > I think this will mishandle SEC("uretprobe"), let's fix this in a
> > > follow up (and see a note about uretprobe selftests)
> > 
> > So I actually fixed it up a little bit to avoid test failure on s390x
> > arch. But now it's a different problem, complaining about not being

Thanks for doing all the fix-ups Andrii, and to Ilya for the Debian/s390 
and selftests fixups!

> > able to resolve libc.so.6. CC'ing Ilya, but I was wondering if it's
> > better to use more generic "libc.so" instead of "libc.so.6"? Have you
> > tried that?
> 

I looked at that, and unfortunately it's tricky because on some platforms
libc.so is a text GNU ld config file - here's what it looks like on my 
system:

$ cat /usr/lib64/libc.so
/* GNU ld script
   Use the shared library, but some functions are only in
   the static library, so try that secondarily.  */
OUTPUT_FORMAT(elf64-x86-64)
GROUP ( /lib64/libc.so.6 /usr/lib64/libc_nonshared.a  AS_NEEDED ( 
/lib64/ld-linux-x86-64.so.2 ) )

I tried the dlopen()/dlinfo() trick with libc.so, thinking we might be 
able to tap into native linking mechanisms such that it would parse 
that file, but it doesn't work for dlopen()ing libc.so unfortunately; 
it needed the .6 suffix.
 
> I believe it's a Debian-specific issue (our s390x CI image is Debian).
> libc is still called libc.so.6, but it's located in
> /lib/s390x-linux-gnu.
> This must also be an issue on Intel and other architectures.
> I'll send a patch.
> 

Thanks!
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index eda724c..38b1c91 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -8630,6 +8630,7 @@  int bpf_program__set_log_buf(struct bpf_program *prog, char *log_buf, size_t log
 }
 
 static int attach_kprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link);
+static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link);
 static int attach_tp(const struct bpf_program *prog, long cookie, struct bpf_link **link);
 static int attach_raw_tp(const struct bpf_program *prog, long cookie, struct bpf_link **link);
 static int attach_trace(const struct bpf_program *prog, long cookie, struct bpf_link **link);
@@ -8642,9 +8643,9 @@  int bpf_program__set_log_buf(struct bpf_program *prog, char *log_buf, size_t log
 	SEC_DEF("sk_reuseport/migrate",	SK_REUSEPORT, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 	SEC_DEF("sk_reuseport",		SK_REUSEPORT, BPF_SK_REUSEPORT_SELECT, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 	SEC_DEF("kprobe/",		KPROBE,	0, SEC_NONE, attach_kprobe),
-	SEC_DEF("uprobe/",		KPROBE,	0, SEC_NONE),
+	SEC_DEF("uprobe+",		KPROBE,	0, SEC_NONE, attach_uprobe),
 	SEC_DEF("kretprobe/",		KPROBE, 0, SEC_NONE, attach_kprobe),
-	SEC_DEF("uretprobe/",		KPROBE, 0, SEC_NONE),
+	SEC_DEF("uretprobe+",		KPROBE, 0, SEC_NONE, attach_uprobe),
 	SEC_DEF("kprobe.multi/",	KPROBE,	BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi),
 	SEC_DEF("kretprobe.multi/",	KPROBE,	BPF_TRACE_KPROBE_MULTI, SEC_NONE, attach_kprobe_multi),
 	SEC_DEF("tc",			SCHED_CLS, 0, SEC_NONE),
@@ -10843,6 +10844,75 @@  static int resolve_full_path(const char *file, char *result, size_t result_sz)
 
 }
 
+/* Format of u[ret]probe section definition supporting auto-attach:
+ * u[ret]probe/binary:function[+offset]
+ *
+ * binary can be an absolute/relative path or a filename; the latter is resolved to a
+ * full binary path via bpf_program__attach_uprobe_opts.
+ *
+ * Specifying uprobe+ ensures we carry out strict matching; either "uprobe" must be
+ * specified (and auto-attach is not possible) or the above format is specified for
+ * auto-attach.
+ */
+static int attach_uprobe(const struct bpf_program *prog, long cookie, struct bpf_link **link)
+{
+	DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts);
+	char *func, *probe_name, *func_end;
+	char *func_name, binary_path[512];
+	unsigned long long raw_offset;
+	size_t offset = 0;
+	int n;
+
+	*link = NULL;
+
+	opts.retprobe = str_has_pfx(prog->sec_name, "uretprobe/");
+	if (opts.retprobe)
+		probe_name = prog->sec_name + sizeof("uretprobe/") - 1;
+	else
+		probe_name = prog->sec_name + sizeof("uprobe/") - 1;
+
+	/* handle SEC("u[ret]probe") - format is valid, but auto-attach is impossible. */
+	if (strlen(probe_name) == 0) {
+		pr_debug("section '%s' is old-style u[ret]probe/function, cannot auto-attach\n",
+			 prog->sec_name);
+		return 0;
+	}
+	snprintf(binary_path, sizeof(binary_path), "%s", probe_name);
+	/* ':' should be prior to function+offset */
+	func_name = strrchr(binary_path, ':');
+	if (!func_name) {
+		pr_warn("section '%s' missing ':function[+offset]' specification\n",
+			prog->sec_name);
+		return -EINVAL;
+	}
+	func_name[0] = '\0';
+	func_name++;
+	n = sscanf(func_name, "%m[a-zA-Z0-9_.]+%li", &func, &offset);
+	if (n < 1) {
+		pr_warn("uprobe name '%s' is invalid\n", func_name);
+		return -EINVAL;
+	}
+	if (opts.retprobe && offset != 0) {
+		free(func);
+		pr_warn("uretprobes do not support offset specification\n");
+		return -EINVAL;
+	}
+
+	/* Is func a raw address? */
+	errno = 0;
+	raw_offset = strtoull(func, &func_end, 0);
+	if (!errno && !*func_end) {
+		free(func);
+		func = NULL;
+		offset = (size_t)raw_offset;
+	}
+	opts.func_name = func;
+
+	*link = bpf_program__attach_uprobe_opts(prog, -1, binary_path, offset, &opts);
+	free(func);
+	return 0;
+}
+
 struct bpf_link *bpf_program__attach_uprobe(const struct bpf_program *prog,
 					    bool retprobe, pid_t pid,
 					    const char *binary_path,