mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 11:19:37 +00:00
![Andrii Nakryiko](/assets/img/avatar_default.png)
Implement changes to error reporting for high-level libbpf APIs to make them less surprising and less error-prone to users: - in all the cases when error happens, errno is set to an appropriate error value; - in libbpf 1.0 mode, all pointer-returning APIs return NULL on error and error code is communicated through errno; this applies both to APIs that already returned NULL before (so now they communicate more detailed error codes), as well as for many APIs that used ERR_PTR() macro and encoded error numbers as fake pointers. - in legacy (default) mode, those APIs that were returning ERR_PTR(err), continue doing so, but still set errno. With these changes, errno can be always used to extract actual error, regardless of legacy or libbpf 1.0 modes. This is utilized internally in libbpf in places where libbpf uses it's own high-level APIs. libbpf_get_error() is adapted to handle both cases completely transparently to end-users (and is used by libbpf consistently as well). More context, justification, and discussion can be found in "Libbpf: the road to v1.0" document ([0]). [0] https://docs.google.com/document/d/1UyjTZuPFWiPFyKk1tV5an11_iaRuec6U-ZESZ54nNTY Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: John Fastabend <john.fastabend@gmail.com> Acked-by: Toke Høiland-Jørgensen <toke@redhat.com> Link: https://lore.kernel.org/bpf/20210525035935.1461796-5-andrii@kernel.org
246 lines
6.1 KiB
C
246 lines
6.1 KiB
C
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
|
|
/* Copyright (c) 2018 Facebook */
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <linux/err.h>
|
|
#include <linux/bpf.h>
|
|
#include "libbpf.h"
|
|
#include "libbpf_internal.h"
|
|
|
|
struct bpf_prog_linfo {
|
|
void *raw_linfo;
|
|
void *raw_jited_linfo;
|
|
__u32 *nr_jited_linfo_per_func;
|
|
__u32 *jited_linfo_func_idx;
|
|
__u32 nr_linfo;
|
|
__u32 nr_jited_func;
|
|
__u32 rec_size;
|
|
__u32 jited_rec_size;
|
|
};
|
|
|
|
static int dissect_jited_func(struct bpf_prog_linfo *prog_linfo,
|
|
const __u64 *ksym_func, const __u32 *ksym_len)
|
|
{
|
|
__u32 nr_jited_func, nr_linfo;
|
|
const void *raw_jited_linfo;
|
|
const __u64 *jited_linfo;
|
|
__u64 last_jited_linfo;
|
|
/*
|
|
* Index to raw_jited_linfo:
|
|
* i: Index for searching the next ksym_func
|
|
* prev_i: Index to the last found ksym_func
|
|
*/
|
|
__u32 i, prev_i;
|
|
__u32 f; /* Index to ksym_func */
|
|
|
|
raw_jited_linfo = prog_linfo->raw_jited_linfo;
|
|
jited_linfo = raw_jited_linfo;
|
|
if (ksym_func[0] != *jited_linfo)
|
|
goto errout;
|
|
|
|
prog_linfo->jited_linfo_func_idx[0] = 0;
|
|
nr_jited_func = prog_linfo->nr_jited_func;
|
|
nr_linfo = prog_linfo->nr_linfo;
|
|
|
|
for (prev_i = 0, i = 1, f = 1;
|
|
i < nr_linfo && f < nr_jited_func;
|
|
i++) {
|
|
raw_jited_linfo += prog_linfo->jited_rec_size;
|
|
last_jited_linfo = *jited_linfo;
|
|
jited_linfo = raw_jited_linfo;
|
|
|
|
if (ksym_func[f] == *jited_linfo) {
|
|
prog_linfo->jited_linfo_func_idx[f] = i;
|
|
|
|
/* Sanity check */
|
|
if (last_jited_linfo - ksym_func[f - 1] + 1 >
|
|
ksym_len[f - 1])
|
|
goto errout;
|
|
|
|
prog_linfo->nr_jited_linfo_per_func[f - 1] =
|
|
i - prev_i;
|
|
prev_i = i;
|
|
|
|
/*
|
|
* The ksym_func[f] is found in jited_linfo.
|
|
* Look for the next one.
|
|
*/
|
|
f++;
|
|
} else if (*jited_linfo <= last_jited_linfo) {
|
|
/* Ensure the addr is increasing _within_ a func */
|
|
goto errout;
|
|
}
|
|
}
|
|
|
|
if (f != nr_jited_func)
|
|
goto errout;
|
|
|
|
prog_linfo->nr_jited_linfo_per_func[nr_jited_func - 1] =
|
|
nr_linfo - prev_i;
|
|
|
|
return 0;
|
|
|
|
errout:
|
|
return -EINVAL;
|
|
}
|
|
|
|
void bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo)
|
|
{
|
|
if (!prog_linfo)
|
|
return;
|
|
|
|
free(prog_linfo->raw_linfo);
|
|
free(prog_linfo->raw_jited_linfo);
|
|
free(prog_linfo->nr_jited_linfo_per_func);
|
|
free(prog_linfo->jited_linfo_func_idx);
|
|
free(prog_linfo);
|
|
}
|
|
|
|
struct bpf_prog_linfo *bpf_prog_linfo__new(const struct bpf_prog_info *info)
|
|
{
|
|
struct bpf_prog_linfo *prog_linfo;
|
|
__u32 nr_linfo, nr_jited_func;
|
|
__u64 data_sz;
|
|
|
|
nr_linfo = info->nr_line_info;
|
|
|
|
if (!nr_linfo)
|
|
return errno = EINVAL, NULL;
|
|
|
|
/*
|
|
* The min size that bpf_prog_linfo has to access for
|
|
* searching purpose.
|
|
*/
|
|
if (info->line_info_rec_size <
|
|
offsetof(struct bpf_line_info, file_name_off))
|
|
return errno = EINVAL, NULL;
|
|
|
|
prog_linfo = calloc(1, sizeof(*prog_linfo));
|
|
if (!prog_linfo)
|
|
return errno = ENOMEM, NULL;
|
|
|
|
/* Copy xlated line_info */
|
|
prog_linfo->nr_linfo = nr_linfo;
|
|
prog_linfo->rec_size = info->line_info_rec_size;
|
|
data_sz = (__u64)nr_linfo * prog_linfo->rec_size;
|
|
prog_linfo->raw_linfo = malloc(data_sz);
|
|
if (!prog_linfo->raw_linfo)
|
|
goto err_free;
|
|
memcpy(prog_linfo->raw_linfo, (void *)(long)info->line_info, data_sz);
|
|
|
|
nr_jited_func = info->nr_jited_ksyms;
|
|
if (!nr_jited_func ||
|
|
!info->jited_line_info ||
|
|
info->nr_jited_line_info != nr_linfo ||
|
|
info->jited_line_info_rec_size < sizeof(__u64) ||
|
|
info->nr_jited_func_lens != nr_jited_func ||
|
|
!info->jited_ksyms ||
|
|
!info->jited_func_lens)
|
|
/* Not enough info to provide jited_line_info */
|
|
return prog_linfo;
|
|
|
|
/* Copy jited_line_info */
|
|
prog_linfo->nr_jited_func = nr_jited_func;
|
|
prog_linfo->jited_rec_size = info->jited_line_info_rec_size;
|
|
data_sz = (__u64)nr_linfo * prog_linfo->jited_rec_size;
|
|
prog_linfo->raw_jited_linfo = malloc(data_sz);
|
|
if (!prog_linfo->raw_jited_linfo)
|
|
goto err_free;
|
|
memcpy(prog_linfo->raw_jited_linfo,
|
|
(void *)(long)info->jited_line_info, data_sz);
|
|
|
|
/* Number of jited_line_info per jited func */
|
|
prog_linfo->nr_jited_linfo_per_func = malloc(nr_jited_func *
|
|
sizeof(__u32));
|
|
if (!prog_linfo->nr_jited_linfo_per_func)
|
|
goto err_free;
|
|
|
|
/*
|
|
* For each jited func,
|
|
* the start idx to the "linfo" and "jited_linfo" array,
|
|
*/
|
|
prog_linfo->jited_linfo_func_idx = malloc(nr_jited_func *
|
|
sizeof(__u32));
|
|
if (!prog_linfo->jited_linfo_func_idx)
|
|
goto err_free;
|
|
|
|
if (dissect_jited_func(prog_linfo,
|
|
(__u64 *)(long)info->jited_ksyms,
|
|
(__u32 *)(long)info->jited_func_lens))
|
|
goto err_free;
|
|
|
|
return prog_linfo;
|
|
|
|
err_free:
|
|
bpf_prog_linfo__free(prog_linfo);
|
|
return errno = EINVAL, NULL;
|
|
}
|
|
|
|
const struct bpf_line_info *
|
|
bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
|
|
__u64 addr, __u32 func_idx, __u32 nr_skip)
|
|
{
|
|
__u32 jited_rec_size, rec_size, nr_linfo, start, i;
|
|
const void *raw_jited_linfo, *raw_linfo;
|
|
const __u64 *jited_linfo;
|
|
|
|
if (func_idx >= prog_linfo->nr_jited_func)
|
|
return errno = ENOENT, NULL;
|
|
|
|
nr_linfo = prog_linfo->nr_jited_linfo_per_func[func_idx];
|
|
if (nr_skip >= nr_linfo)
|
|
return errno = ENOENT, NULL;
|
|
|
|
start = prog_linfo->jited_linfo_func_idx[func_idx] + nr_skip;
|
|
jited_rec_size = prog_linfo->jited_rec_size;
|
|
raw_jited_linfo = prog_linfo->raw_jited_linfo +
|
|
(start * jited_rec_size);
|
|
jited_linfo = raw_jited_linfo;
|
|
if (addr < *jited_linfo)
|
|
return errno = ENOENT, NULL;
|
|
|
|
nr_linfo -= nr_skip;
|
|
rec_size = prog_linfo->rec_size;
|
|
raw_linfo = prog_linfo->raw_linfo + (start * rec_size);
|
|
for (i = 0; i < nr_linfo; i++) {
|
|
if (addr < *jited_linfo)
|
|
break;
|
|
|
|
raw_linfo += rec_size;
|
|
raw_jited_linfo += jited_rec_size;
|
|
jited_linfo = raw_jited_linfo;
|
|
}
|
|
|
|
return raw_linfo - rec_size;
|
|
}
|
|
|
|
const struct bpf_line_info *
|
|
bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
|
|
__u32 insn_off, __u32 nr_skip)
|
|
{
|
|
const struct bpf_line_info *linfo;
|
|
__u32 rec_size, nr_linfo, i;
|
|
const void *raw_linfo;
|
|
|
|
nr_linfo = prog_linfo->nr_linfo;
|
|
if (nr_skip >= nr_linfo)
|
|
return errno = ENOENT, NULL;
|
|
|
|
rec_size = prog_linfo->rec_size;
|
|
raw_linfo = prog_linfo->raw_linfo + (nr_skip * rec_size);
|
|
linfo = raw_linfo;
|
|
if (insn_off < linfo->insn_off)
|
|
return errno = ENOENT, NULL;
|
|
|
|
nr_linfo -= nr_skip;
|
|
for (i = 0; i < nr_linfo; i++) {
|
|
if (insn_off < linfo->insn_off)
|
|
break;
|
|
|
|
raw_linfo += rec_size;
|
|
linfo = raw_linfo;
|
|
}
|
|
|
|
return raw_linfo - rec_size;
|
|
}
|