Skip to content

Commit

Permalink
libbpf: Add legacy uprobe attaching support
Browse files Browse the repository at this point in the history
Similarly to recently added legacy kprobe attach interface support
through tracefs, support attaching uprobes using the legacy interface if
host kernel doesn't support newer FD-based interface.

For uprobes event name consists of "libbpf_" prefix, PID, sanitized
binary path and offset within that binary. Structuraly the code is
aligned with kprobe logic refactoring in previous patch. struct
bpf_link_perf is re-used and all the same legacy_probe_name and
legacy_is_retprobe fields are used to ensure proper cleanup on
bpf_link__destroy().

Users should be aware, though, that on old kernels which don't support
FD-based interface for kprobe/uprobe attachment, if the application
crashes before bpf_link__destroy() is called, uprobe legacy
events will be left in tracefs. This is the same limitation as with
legacy kprobe interfaces.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20210921210036.1545557-5-andrii@kernel.org
  • Loading branch information
Andrii Nakryiko authored and Alexei Starovoitov committed Sep 22, 2021
1 parent 46ed5fc commit cc10623
Showing 1 changed file with 122 additions and 8 deletions.
130 changes: 122 additions & 8 deletions tools/lib/bpf/libbpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -9021,6 +9021,7 @@ struct bpf_link_perf {
};

static int remove_kprobe_event_legacy(const char *probe_name, bool retprobe);
static int remove_uprobe_event_legacy(const char *probe_name, bool retprobe);

static int bpf_link_perf_detach(struct bpf_link *link)
{
Expand All @@ -9034,11 +9035,14 @@ static int bpf_link_perf_detach(struct bpf_link *link)
close(perf_link->perf_event_fd);
close(link->fd);

/* legacy kprobe needs to be removed after perf event fd closure */
/* legacy uprobe/kprobe needs to be removed after perf event fd closure */
if (perf_link->legacy_probe_name) {
if (perf_link->legacy_is_kprobe) {
err = remove_kprobe_event_legacy(perf_link->legacy_probe_name,
perf_link->legacy_is_retprobe);
} else {
err = remove_uprobe_event_legacy(perf_link->legacy_probe_name,
perf_link->legacy_is_retprobe);
}
}

Expand Down Expand Up @@ -9450,17 +9454,96 @@ static struct bpf_link *attach_kprobe(const struct bpf_program *prog)
return link;
}

static void gen_uprobe_legacy_event_name(char *buf, size_t buf_sz,
const char *binary_path, uint64_t offset)
{
int i;

snprintf(buf, buf_sz, "libbpf_%u_%s_0x%zx", getpid(), binary_path, (size_t)offset);

/* sanitize binary_path in the probe name */
for (i = 0; buf[i]; i++) {
if (!isalnum(buf[i]))
buf[i] = '_';
}
}

static inline int add_uprobe_event_legacy(const char *probe_name, bool retprobe,
const char *binary_path, size_t offset)
{
const char *file = "/sys/kernel/debug/tracing/uprobe_events";

return append_to_file(file, "%c:%s/%s %s:0x%zx",
retprobe ? 'r' : 'p',
retprobe ? "uretprobes" : "uprobes",
probe_name, binary_path, offset);
}

static inline int remove_uprobe_event_legacy(const char *probe_name, bool retprobe)
{
const char *file = "/sys/kernel/debug/tracing/uprobe_events";

return append_to_file(file, "-:%s/%s", retprobe ? "uretprobes" : "uprobes", probe_name);
}

static int determine_uprobe_perf_type_legacy(const char *probe_name, bool retprobe)
{
char file[512];

snprintf(file, sizeof(file),
"/sys/kernel/debug/tracing/events/%s/%s/id",
retprobe ? "uretprobes" : "uprobes", probe_name);

return parse_uint_from_file(file, "%d\n");
}

static int perf_event_uprobe_open_legacy(const char *probe_name, bool retprobe,
const char *binary_path, size_t offset, int pid)
{
struct perf_event_attr attr;
int type, pfd, err;

err = add_uprobe_event_legacy(probe_name, retprobe, binary_path, offset);
if (err < 0) {
pr_warn("failed to add legacy uprobe event for %s:0x%zx: %d\n",
binary_path, (size_t)offset, err);
return err;
}
type = determine_uprobe_perf_type_legacy(probe_name, retprobe);
if (type < 0) {
pr_warn("failed to determine legacy uprobe event id for %s:0x%zx: %d\n",
binary_path, offset, err);
return type;
}

memset(&attr, 0, sizeof(attr));
attr.size = sizeof(attr);
attr.config = type;
attr.type = PERF_TYPE_TRACEPOINT;

pfd = syscall(__NR_perf_event_open, &attr,
pid < 0 ? -1 : pid, /* pid */
pid == -1 ? 0 : -1, /* cpu */
-1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
if (pfd < 0) {
err = -errno;
pr_warn("legacy uprobe perf_event_open() failed: %d\n", err);
return err;
}
return pfd;
}

LIBBPF_API struct bpf_link *
bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
const char *binary_path, size_t func_offset,
const struct bpf_uprobe_opts *opts)
{
DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts);
char errmsg[STRERR_BUFSIZE];
char errmsg[STRERR_BUFSIZE], *legacy_probe = NULL;
struct bpf_link *link;
size_t ref_ctr_off;
int pfd, err;
bool retprobe;
bool retprobe, legacy;

if (!OPTS_VALID(opts, bpf_uprobe_opts))
return libbpf_err_ptr(-EINVAL);
Expand All @@ -9469,15 +9552,35 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
ref_ctr_off = OPTS_GET(opts, ref_ctr_offset, 0);
pe_opts.bpf_cookie = OPTS_GET(opts, bpf_cookie, 0);

pfd = perf_event_open_probe(true /* uprobe */, retprobe, binary_path,
func_offset, pid, ref_ctr_off);
legacy = determine_uprobe_perf_type() < 0;
if (!legacy) {
pfd = perf_event_open_probe(true /* uprobe */, retprobe, binary_path,
func_offset, pid, ref_ctr_off);
} else {
char probe_name[512];

if (ref_ctr_off)
return libbpf_err_ptr(-EINVAL);

gen_uprobe_legacy_event_name(probe_name, sizeof(probe_name),
binary_path, func_offset);

legacy_probe = strdup(probe_name);
if (!legacy_probe)
return libbpf_err_ptr(-ENOMEM);

pfd = perf_event_uprobe_open_legacy(legacy_probe, retprobe,
binary_path, func_offset, pid);
}
if (pfd < 0) {
err = -errno;
pr_warn("prog '%s': failed to create %s '%s:0x%zx' perf event: %s\n",
prog->name, retprobe ? "uretprobe" : "uprobe",
binary_path, func_offset,
libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
return libbpf_err_ptr(pfd);
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
goto err_out;
}

link = bpf_program__attach_perf_event_opts(prog, pfd, &pe_opts);
err = libbpf_get_error(link);
if (err) {
Expand All @@ -9486,9 +9589,20 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
prog->name, retprobe ? "uretprobe" : "uprobe",
binary_path, func_offset,
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
return libbpf_err_ptr(err);
goto err_out;
}
if (legacy) {
struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link);

perf_link->legacy_probe_name = legacy_probe;
perf_link->legacy_is_kprobe = false;
perf_link->legacy_is_retprobe = retprobe;
}
return link;
err_out:
free(legacy_probe);
return libbpf_err_ptr(err);

}

struct bpf_link *bpf_program__attach_uprobe(const struct bpf_program *prog,
Expand Down

0 comments on commit cc10623

Please sign in to comment.