Skip to content

Commit cc10623

Browse files
anakryikoAlexei Starovoitov
authored and
Alexei Starovoitov
committed
libbpf: Add legacy uprobe attaching support
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 <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 46ed5fc commit cc10623

File tree

1 file changed

+122
-8
lines changed

1 file changed

+122
-8
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 122 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9021,6 +9021,7 @@ struct bpf_link_perf {
90219021
};
90229022

90239023
static int remove_kprobe_event_legacy(const char *probe_name, bool retprobe);
9024+
static int remove_uprobe_event_legacy(const char *probe_name, bool retprobe);
90249025

90259026
static int bpf_link_perf_detach(struct bpf_link *link)
90269027
{
@@ -9034,11 +9035,14 @@ static int bpf_link_perf_detach(struct bpf_link *link)
90349035
close(perf_link->perf_event_fd);
90359036
close(link->fd);
90369037

9037-
/* legacy kprobe needs to be removed after perf event fd closure */
9038+
/* legacy uprobe/kprobe needs to be removed after perf event fd closure */
90389039
if (perf_link->legacy_probe_name) {
90399040
if (perf_link->legacy_is_kprobe) {
90409041
err = remove_kprobe_event_legacy(perf_link->legacy_probe_name,
90419042
perf_link->legacy_is_retprobe);
9043+
} else {
9044+
err = remove_uprobe_event_legacy(perf_link->legacy_probe_name,
9045+
perf_link->legacy_is_retprobe);
90429046
}
90439047
}
90449048

@@ -9450,17 +9454,96 @@ static struct bpf_link *attach_kprobe(const struct bpf_program *prog)
94509454
return link;
94519455
}
94529456

9457+
static void gen_uprobe_legacy_event_name(char *buf, size_t buf_sz,
9458+
const char *binary_path, uint64_t offset)
9459+
{
9460+
int i;
9461+
9462+
snprintf(buf, buf_sz, "libbpf_%u_%s_0x%zx", getpid(), binary_path, (size_t)offset);
9463+
9464+
/* sanitize binary_path in the probe name */
9465+
for (i = 0; buf[i]; i++) {
9466+
if (!isalnum(buf[i]))
9467+
buf[i] = '_';
9468+
}
9469+
}
9470+
9471+
static inline int add_uprobe_event_legacy(const char *probe_name, bool retprobe,
9472+
const char *binary_path, size_t offset)
9473+
{
9474+
const char *file = "/sys/kernel/debug/tracing/uprobe_events";
9475+
9476+
return append_to_file(file, "%c:%s/%s %s:0x%zx",
9477+
retprobe ? 'r' : 'p',
9478+
retprobe ? "uretprobes" : "uprobes",
9479+
probe_name, binary_path, offset);
9480+
}
9481+
9482+
static inline int remove_uprobe_event_legacy(const char *probe_name, bool retprobe)
9483+
{
9484+
const char *file = "/sys/kernel/debug/tracing/uprobe_events";
9485+
9486+
return append_to_file(file, "-:%s/%s", retprobe ? "uretprobes" : "uprobes", probe_name);
9487+
}
9488+
9489+
static int determine_uprobe_perf_type_legacy(const char *probe_name, bool retprobe)
9490+
{
9491+
char file[512];
9492+
9493+
snprintf(file, sizeof(file),
9494+
"/sys/kernel/debug/tracing/events/%s/%s/id",
9495+
retprobe ? "uretprobes" : "uprobes", probe_name);
9496+
9497+
return parse_uint_from_file(file, "%d\n");
9498+
}
9499+
9500+
static int perf_event_uprobe_open_legacy(const char *probe_name, bool retprobe,
9501+
const char *binary_path, size_t offset, int pid)
9502+
{
9503+
struct perf_event_attr attr;
9504+
int type, pfd, err;
9505+
9506+
err = add_uprobe_event_legacy(probe_name, retprobe, binary_path, offset);
9507+
if (err < 0) {
9508+
pr_warn("failed to add legacy uprobe event for %s:0x%zx: %d\n",
9509+
binary_path, (size_t)offset, err);
9510+
return err;
9511+
}
9512+
type = determine_uprobe_perf_type_legacy(probe_name, retprobe);
9513+
if (type < 0) {
9514+
pr_warn("failed to determine legacy uprobe event id for %s:0x%zx: %d\n",
9515+
binary_path, offset, err);
9516+
return type;
9517+
}
9518+
9519+
memset(&attr, 0, sizeof(attr));
9520+
attr.size = sizeof(attr);
9521+
attr.config = type;
9522+
attr.type = PERF_TYPE_TRACEPOINT;
9523+
9524+
pfd = syscall(__NR_perf_event_open, &attr,
9525+
pid < 0 ? -1 : pid, /* pid */
9526+
pid == -1 ? 0 : -1, /* cpu */
9527+
-1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
9528+
if (pfd < 0) {
9529+
err = -errno;
9530+
pr_warn("legacy uprobe perf_event_open() failed: %d\n", err);
9531+
return err;
9532+
}
9533+
return pfd;
9534+
}
9535+
94539536
LIBBPF_API struct bpf_link *
94549537
bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
94559538
const char *binary_path, size_t func_offset,
94569539
const struct bpf_uprobe_opts *opts)
94579540
{
94589541
DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts);
9459-
char errmsg[STRERR_BUFSIZE];
9542+
char errmsg[STRERR_BUFSIZE], *legacy_probe = NULL;
94609543
struct bpf_link *link;
94619544
size_t ref_ctr_off;
94629545
int pfd, err;
9463-
bool retprobe;
9546+
bool retprobe, legacy;
94649547

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

9472-
pfd = perf_event_open_probe(true /* uprobe */, retprobe, binary_path,
9473-
func_offset, pid, ref_ctr_off);
9555+
legacy = determine_uprobe_perf_type() < 0;
9556+
if (!legacy) {
9557+
pfd = perf_event_open_probe(true /* uprobe */, retprobe, binary_path,
9558+
func_offset, pid, ref_ctr_off);
9559+
} else {
9560+
char probe_name[512];
9561+
9562+
if (ref_ctr_off)
9563+
return libbpf_err_ptr(-EINVAL);
9564+
9565+
gen_uprobe_legacy_event_name(probe_name, sizeof(probe_name),
9566+
binary_path, func_offset);
9567+
9568+
legacy_probe = strdup(probe_name);
9569+
if (!legacy_probe)
9570+
return libbpf_err_ptr(-ENOMEM);
9571+
9572+
pfd = perf_event_uprobe_open_legacy(legacy_probe, retprobe,
9573+
binary_path, func_offset, pid);
9574+
}
94749575
if (pfd < 0) {
9576+
err = -errno;
94759577
pr_warn("prog '%s': failed to create %s '%s:0x%zx' perf event: %s\n",
94769578
prog->name, retprobe ? "uretprobe" : "uprobe",
94779579
binary_path, func_offset,
9478-
libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
9479-
return libbpf_err_ptr(pfd);
9580+
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
9581+
goto err_out;
94809582
}
9583+
94819584
link = bpf_program__attach_perf_event_opts(prog, pfd, &pe_opts);
94829585
err = libbpf_get_error(link);
94839586
if (err) {
@@ -9486,9 +9589,20 @@ bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
94869589
prog->name, retprobe ? "uretprobe" : "uprobe",
94879590
binary_path, func_offset,
94889591
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
9489-
return libbpf_err_ptr(err);
9592+
goto err_out;
9593+
}
9594+
if (legacy) {
9595+
struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link);
9596+
9597+
perf_link->legacy_probe_name = legacy_probe;
9598+
perf_link->legacy_is_kprobe = false;
9599+
perf_link->legacy_is_retprobe = retprobe;
94909600
}
94919601
return link;
9602+
err_out:
9603+
free(legacy_probe);
9604+
return libbpf_err_ptr(err);
9605+
94929606
}
94939607

94949608
struct bpf_link *bpf_program__attach_uprobe(const struct bpf_program *prog,

0 commit comments

Comments
 (0)