libbpf: add kprobe/uprobe attach API
Add ability to attach to kernel and user probes and retprobes. Implementation depends on perf event support for kprobes/uprobes. Signed-off-by: Andrii Nakryiko <andriin@fb.com> Reviewed-by: Stanislav Fomichev <sdf@google.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
parent
63f2f5ee85
commit
b265002747
@ -4021,6 +4021,175 @@ struct bpf_link *bpf_program__attach_perf_event(struct bpf_program *prog,
|
||||
return (struct bpf_link *)link;
|
||||
}
|
||||
|
||||
/*
|
||||
* this function is expected to parse integer in the range of [0, 2^31-1] from
|
||||
* given file using scanf format string fmt. If actual parsed value is
|
||||
* negative, the result might be indistinguishable from error
|
||||
*/
|
||||
static int parse_uint_from_file(const char *file, const char *fmt)
|
||||
{
|
||||
char buf[STRERR_BUFSIZE];
|
||||
int err, ret;
|
||||
FILE *f;
|
||||
|
||||
f = fopen(file, "r");
|
||||
if (!f) {
|
||||
err = -errno;
|
||||
pr_debug("failed to open '%s': %s\n", file,
|
||||
libbpf_strerror_r(err, buf, sizeof(buf)));
|
||||
return err;
|
||||
}
|
||||
err = fscanf(f, fmt, &ret);
|
||||
if (err != 1) {
|
||||
err = err == EOF ? -EIO : -errno;
|
||||
pr_debug("failed to parse '%s': %s\n", file,
|
||||
libbpf_strerror_r(err, buf, sizeof(buf)));
|
||||
fclose(f);
|
||||
return err;
|
||||
}
|
||||
fclose(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int determine_kprobe_perf_type(void)
|
||||
{
|
||||
const char *file = "/sys/bus/event_source/devices/kprobe/type";
|
||||
|
||||
return parse_uint_from_file(file, "%d\n");
|
||||
}
|
||||
|
||||
static int determine_uprobe_perf_type(void)
|
||||
{
|
||||
const char *file = "/sys/bus/event_source/devices/uprobe/type";
|
||||
|
||||
return parse_uint_from_file(file, "%d\n");
|
||||
}
|
||||
|
||||
static int determine_kprobe_retprobe_bit(void)
|
||||
{
|
||||
const char *file = "/sys/bus/event_source/devices/kprobe/format/retprobe";
|
||||
|
||||
return parse_uint_from_file(file, "config:%d\n");
|
||||
}
|
||||
|
||||
static int determine_uprobe_retprobe_bit(void)
|
||||
{
|
||||
const char *file = "/sys/bus/event_source/devices/uprobe/format/retprobe";
|
||||
|
||||
return parse_uint_from_file(file, "config:%d\n");
|
||||
}
|
||||
|
||||
static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name,
|
||||
uint64_t offset, int pid)
|
||||
{
|
||||
struct perf_event_attr attr = {};
|
||||
char errmsg[STRERR_BUFSIZE];
|
||||
int type, pfd, err;
|
||||
|
||||
type = uprobe ? determine_uprobe_perf_type()
|
||||
: determine_kprobe_perf_type();
|
||||
if (type < 0) {
|
||||
pr_warning("failed to determine %s perf type: %s\n",
|
||||
uprobe ? "uprobe" : "kprobe",
|
||||
libbpf_strerror_r(type, errmsg, sizeof(errmsg)));
|
||||
return type;
|
||||
}
|
||||
if (retprobe) {
|
||||
int bit = uprobe ? determine_uprobe_retprobe_bit()
|
||||
: determine_kprobe_retprobe_bit();
|
||||
|
||||
if (bit < 0) {
|
||||
pr_warning("failed to determine %s retprobe bit: %s\n",
|
||||
uprobe ? "uprobe" : "kprobe",
|
||||
libbpf_strerror_r(bit, errmsg,
|
||||
sizeof(errmsg)));
|
||||
return bit;
|
||||
}
|
||||
attr.config |= 1 << bit;
|
||||
}
|
||||
attr.size = sizeof(attr);
|
||||
attr.type = type;
|
||||
attr.config1 = (uint64_t)(void *)name; /* kprobe_func or uprobe_path */
|
||||
attr.config2 = offset; /* kprobe_addr or probe_offset */
|
||||
|
||||
/* pid filter is meaningful only for uprobes */
|
||||
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_warning("%s perf_event_open() failed: %s\n",
|
||||
uprobe ? "uprobe" : "kprobe",
|
||||
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||
return err;
|
||||
}
|
||||
return pfd;
|
||||
}
|
||||
|
||||
struct bpf_link *bpf_program__attach_kprobe(struct bpf_program *prog,
|
||||
bool retprobe,
|
||||
const char *func_name)
|
||||
{
|
||||
char errmsg[STRERR_BUFSIZE];
|
||||
struct bpf_link *link;
|
||||
int pfd, err;
|
||||
|
||||
pfd = perf_event_open_probe(false /* uprobe */, retprobe, func_name,
|
||||
0 /* offset */, -1 /* pid */);
|
||||
if (pfd < 0) {
|
||||
pr_warning("program '%s': failed to create %s '%s' perf event: %s\n",
|
||||
bpf_program__title(prog, false),
|
||||
retprobe ? "kretprobe" : "kprobe", func_name,
|
||||
libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
|
||||
return ERR_PTR(pfd);
|
||||
}
|
||||
link = bpf_program__attach_perf_event(prog, pfd);
|
||||
if (IS_ERR(link)) {
|
||||
close(pfd);
|
||||
err = PTR_ERR(link);
|
||||
pr_warning("program '%s': failed to attach to %s '%s': %s\n",
|
||||
bpf_program__title(prog, false),
|
||||
retprobe ? "kretprobe" : "kprobe", func_name,
|
||||
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||
return link;
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
||||
struct bpf_link *bpf_program__attach_uprobe(struct bpf_program *prog,
|
||||
bool retprobe, pid_t pid,
|
||||
const char *binary_path,
|
||||
size_t func_offset)
|
||||
{
|
||||
char errmsg[STRERR_BUFSIZE];
|
||||
struct bpf_link *link;
|
||||
int pfd, err;
|
||||
|
||||
pfd = perf_event_open_probe(true /* uprobe */, retprobe,
|
||||
binary_path, func_offset, pid);
|
||||
if (pfd < 0) {
|
||||
pr_warning("program '%s': failed to create %s '%s:0x%zx' perf event: %s\n",
|
||||
bpf_program__title(prog, false),
|
||||
retprobe ? "uretprobe" : "uprobe",
|
||||
binary_path, func_offset,
|
||||
libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
|
||||
return ERR_PTR(pfd);
|
||||
}
|
||||
link = bpf_program__attach_perf_event(prog, pfd);
|
||||
if (IS_ERR(link)) {
|
||||
close(pfd);
|
||||
err = PTR_ERR(link);
|
||||
pr_warning("program '%s': failed to attach to %s '%s:0x%zx': %s\n",
|
||||
bpf_program__title(prog, false),
|
||||
retprobe ? "uretprobe" : "uprobe",
|
||||
binary_path, func_offset,
|
||||
libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
|
||||
return link;
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
||||
enum bpf_perf_event_ret
|
||||
bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
|
||||
void **copy_mem, size_t *copy_size,
|
||||
|
@ -171,6 +171,13 @@ LIBBPF_API int bpf_link__destroy(struct bpf_link *link);
|
||||
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_perf_event(struct bpf_program *prog, int pfd);
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_kprobe(struct bpf_program *prog, bool retprobe,
|
||||
const char *func_name);
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_uprobe(struct bpf_program *prog, bool retprobe,
|
||||
pid_t pid, const char *binary_path,
|
||||
size_t func_offset);
|
||||
|
||||
struct bpf_insn;
|
||||
|
||||
|
@ -169,7 +169,9 @@ LIBBPF_0.0.4 {
|
||||
global:
|
||||
bpf_link__destroy;
|
||||
bpf_object__load_xattr;
|
||||
bpf_program__attach_kprobe;
|
||||
bpf_program__attach_perf_event;
|
||||
bpf_program__attach_uprobe;
|
||||
btf_dump__dump_type;
|
||||
btf_dump__free;
|
||||
btf_dump__new;
|
||||
|
Loading…
Reference in New Issue
Block a user