libbpf: Implement generalized .BTF.ext func/line info adjustment

Complete multi-prog sections and multi sub-prog support in libbpf by properly
adjusting .BTF.ext's line and function information. Mark exposed
btf_ext__reloc_func_info() and btf_ext__reloc_func_info() APIs as deprecated.
These APIs have simplistic assumption that all sub-programs are going to be
appended to all main BPF programs, which doesn't hold in real life. It's
unlikely there are any users of this API, as it's very libbpf
internals-specific.

Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/20200903203542.15944-6-andriin@fb.com
This commit is contained in:
Andrii Nakryiko 2020-09-03 13:35:33 -07:00 committed by Alexei Starovoitov
parent c3c556966d
commit 8505e8709b
3 changed files with 153 additions and 84 deletions

View File

@ -57,14 +57,16 @@ LIBBPF_API struct btf_ext *btf_ext__new(__u8 *data, __u32 size);
LIBBPF_API void btf_ext__free(struct btf_ext *btf_ext);
LIBBPF_API const void *btf_ext__get_raw_data(const struct btf_ext *btf_ext,
__u32 *size);
LIBBPF_API int btf_ext__reloc_func_info(const struct btf *btf,
const struct btf_ext *btf_ext,
const char *sec_name, __u32 insns_cnt,
void **func_info, __u32 *cnt);
LIBBPF_API int btf_ext__reloc_line_info(const struct btf *btf,
const struct btf_ext *btf_ext,
const char *sec_name, __u32 insns_cnt,
void **line_info, __u32 *cnt);
LIBBPF_API LIBBPF_DEPRECATED("btf_ext__reloc_func_info was never meant as a public API and has wrong assumptions embedded in it; it will be removed in the future libbpf versions")
int btf_ext__reloc_func_info(const struct btf *btf,
const struct btf_ext *btf_ext,
const char *sec_name, __u32 insns_cnt,
void **func_info, __u32 *cnt);
LIBBPF_API LIBBPF_DEPRECATED("btf_ext__reloc_line_info was never meant as a public API and has wrong assumptions embedded in it; it will be removed in the future libbpf versions")
int btf_ext__reloc_line_info(const struct btf *btf,
const struct btf_ext *btf_ext,
const char *sec_name, __u32 insns_cnt,
void **line_info, __u32 *cnt);
LIBBPF_API __u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext);
LIBBPF_API __u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext);

View File

@ -4241,75 +4241,6 @@ err_out:
return err;
}
static int
check_btf_ext_reloc_err(struct bpf_program *prog, int err,
void *btf_prog_info, const char *info_name)
{
if (err != -ENOENT) {
pr_warn("Error in loading %s for sec %s.\n",
info_name, prog->section_name);
return err;
}
/* err == -ENOENT (i.e. prog->section_name not found in btf_ext) */
if (btf_prog_info) {
/*
* Some info has already been found but has problem
* in the last btf_ext reloc. Must have to error out.
*/
pr_warn("Error in relocating %s for sec %s.\n",
info_name, prog->section_name);
return err;
}
/* Have problem loading the very first info. Ignore the rest. */
pr_warn("Cannot find %s for main program sec %s. Ignore all %s.\n",
info_name, prog->section_name, info_name);
return 0;
}
static int
bpf_program_reloc_btf_ext(struct bpf_program *prog, struct bpf_object *obj,
const char *section_name, __u32 insn_offset)
{
int err;
if (!insn_offset || prog->func_info) {
/*
* !insn_offset => main program
*
* For sub prog, the main program's func_info has to
* be loaded first (i.e. prog->func_info != NULL)
*/
err = btf_ext__reloc_func_info(obj->btf, obj->btf_ext,
section_name, insn_offset,
&prog->func_info,
&prog->func_info_cnt);
if (err)
return check_btf_ext_reloc_err(prog, err,
prog->func_info,
"bpf_func_info");
prog->func_info_rec_size = btf_ext__func_info_rec_size(obj->btf_ext);
}
if (!insn_offset || prog->line_info) {
err = btf_ext__reloc_line_info(obj->btf, obj->btf_ext,
section_name, insn_offset,
&prog->line_info,
&prog->line_info_cnt);
if (err)
return check_btf_ext_reloc_err(prog, err,
prog->line_info,
"bpf_line_info");
prog->line_info_rec_size = btf_ext__line_info_rec_size(obj->btf_ext);
}
return 0;
}
#define BPF_CORE_SPEC_MAX_LEN 64
/* represents BPF CO-RE field or array element accessor */
@ -5855,6 +5786,147 @@ bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog)
return 0;
}
static int adjust_prog_btf_ext_info(const struct bpf_object *obj,
const struct bpf_program *prog,
const struct btf_ext_info *ext_info,
void **prog_info, __u32 *prog_rec_cnt,
__u32 *prog_rec_sz)
{
void *copy_start = NULL, *copy_end = NULL;
void *rec, *rec_end, *new_prog_info;
const struct btf_ext_info_sec *sec;
size_t old_sz, new_sz;
const char *sec_name;
int i, off_adj;
for_each_btf_ext_sec(ext_info, sec) {
sec_name = btf__name_by_offset(obj->btf, sec->sec_name_off);
if (!sec_name)
return -EINVAL;
if (strcmp(sec_name, prog->section_name) != 0)
continue;
for_each_btf_ext_rec(ext_info, sec, i, rec) {
__u32 insn_off = *(__u32 *)rec / BPF_INSN_SZ;
if (insn_off < prog->sec_insn_off)
continue;
if (insn_off >= prog->sec_insn_off + prog->sec_insn_cnt)
break;
if (!copy_start)
copy_start = rec;
copy_end = rec + ext_info->rec_size;
}
if (!copy_start)
return -ENOENT;
/* append func/line info of a given (sub-)program to the main
* program func/line info
*/
old_sz = (*prog_rec_cnt) * ext_info->rec_size;
new_sz = old_sz + (copy_end - copy_start);
new_prog_info = realloc(*prog_info, new_sz);
if (!new_prog_info)
return -ENOMEM;
*prog_info = new_prog_info;
*prog_rec_cnt = new_sz / ext_info->rec_size;
memcpy(new_prog_info + old_sz, copy_start, copy_end - copy_start);
/* Kernel instruction offsets are in units of 8-byte
* instructions, while .BTF.ext instruction offsets generated
* by Clang are in units of bytes. So convert Clang offsets
* into kernel offsets and adjust offset according to program
* relocated position.
*/
off_adj = prog->sub_insn_off - prog->sec_insn_off;
rec = new_prog_info + old_sz;
rec_end = new_prog_info + new_sz;
for (; rec < rec_end; rec += ext_info->rec_size) {
__u32 *insn_off = rec;
*insn_off = *insn_off / BPF_INSN_SZ + off_adj;
}
*prog_rec_sz = ext_info->rec_size;
return 0;
}
return -ENOENT;
}
static int
reloc_prog_func_and_line_info(const struct bpf_object *obj,
struct bpf_program *main_prog,
const struct bpf_program *prog)
{
int err;
/* no .BTF.ext relocation if .BTF.ext is missing or kernel doesn't
* supprot func/line info
*/
if (!obj->btf_ext || !kernel_supports(FEAT_BTF_FUNC))
return 0;
/* only attempt func info relocation if main program's func_info
* relocation was successful
*/
if (main_prog != prog && !main_prog->func_info)
goto line_info;
err = adjust_prog_btf_ext_info(obj, prog, &obj->btf_ext->func_info,
&main_prog->func_info,
&main_prog->func_info_cnt,
&main_prog->func_info_rec_size);
if (err) {
if (err != -ENOENT) {
pr_warn("prog '%s': error relocating .BTF.ext function info: %d\n",
prog->name, err);
return err;
}
if (main_prog->func_info) {
/*
* Some info has already been found but has problem
* in the last btf_ext reloc. Must have to error out.
*/
pr_warn("prog '%s': missing .BTF.ext function info.\n", prog->name);
return err;
}
/* Have problem loading the very first info. Ignore the rest. */
pr_warn("prog '%s': missing .BTF.ext function info for the main program, skipping all of .BTF.ext func info.\n",
prog->name);
}
line_info:
/* don't relocate line info if main program's relocation failed */
if (main_prog != prog && !main_prog->line_info)
return 0;
err = adjust_prog_btf_ext_info(obj, prog, &obj->btf_ext->line_info,
&main_prog->line_info,
&main_prog->line_info_cnt,
&main_prog->line_info_rec_size);
if (err) {
if (err != -ENOENT) {
pr_warn("prog '%s': error relocating .BTF.ext line info: %d\n",
prog->name, err);
return err;
}
if (main_prog->line_info) {
/*
* Some info has already been found but has problem
* in the last btf_ext reloc. Must have to error out.
*/
pr_warn("prog '%s': missing .BTF.ext line info.\n", prog->name);
return err;
}
/* Have problem loading the very first info. Ignore the rest. */
pr_warn("prog '%s': missing .BTF.ext line info for the main program, skipping all of .BTF.ext line info.\n",
prog->name);
}
return 0;
}
static int cmp_relo_by_insn_idx(const void *key, const void *elem)
{
size_t insn_idx = *(const size_t *)key;
@ -6064,13 +6136,6 @@ bpf_object__relocate_calls(struct bpf_object *obj, struct bpf_program *prog)
struct bpf_program *subprog;
int i, j, err;
if (obj->btf_ext) {
err = bpf_program_reloc_btf_ext(prog, obj,
prog->section_name, 0);
if (err)
return err;
}
/* mark all subprogs as not relocated (yet) within the context of
* current main program
*/

View File

@ -15,6 +15,8 @@
#define LIBBPF_API __attribute__((visibility("default")))
#endif
#define LIBBPF_DEPRECATED(msg) __attribute__((deprecated(msg)))
/* Helper macro to declare and initialize libbpf options struct
*
* This dance with uninitialized declaration, followed by memset to zero,