The result of the CO-RE relocations can be useful for some use cases like BTFGen[0]. This commit adds a new ‘record_core_relos’ option to save the result of such relocations and a couple of functions to access them. [0]: https://github.com/kinvolk/btfgen/ Signed-off-by: Mauricio Vásquez <mauricio@xxxxxxxxxx> Signed-off-by: Rafael David Tinoco <rafael.tinoco@xxxxxxxxxxx> Signed-off-by: Lorenzo Fontana <lorenzo.fontana@xxxxxxxxxx> Signed-off-by: Leonardo Di Donato <leonardo.didonato@xxxxxxxxxx> --- tools/lib/bpf/libbpf.c | 63 ++++++++++++++++++++++++++++++++++++++- tools/lib/bpf/libbpf.h | 49 +++++++++++++++++++++++++++++- tools/lib/bpf/libbpf.map | 2 ++ tools/lib/bpf/relo_core.c | 28 +++++++++++++++-- tools/lib/bpf/relo_core.h | 21 ++----------- 5 files changed, 140 insertions(+), 23 deletions(-) diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index f50f9428bb03..a5da977a9f5d 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -306,6 +306,10 @@ struct bpf_program { struct reloc_desc *reloc_desc; int nr_reloc; + + struct bpf_core_relo_result *core_relos; + int nr_core_relos; + int log_level; struct { @@ -519,6 +523,9 @@ struct bpf_object { bool has_subcalls; bool has_rodata; + /* Record CO-RE relocations for the different programs in prog->core_relos */ + bool record_core_relos; + struct bpf_gen *gen_loader; /* Information when doing ELF related work. Only valid if efile.elf is not NULL */ @@ -614,8 +621,10 @@ static void bpf_program__exit(struct bpf_program *prog) zfree(&prog->pin_name); zfree(&prog->insns); zfree(&prog->reloc_desc); + zfree(&prog->core_relos); prog->nr_reloc = 0; + prog->nr_core_relos = 0; prog->insns_cnt = 0; prog->sec_idx = -1; } @@ -5408,6 +5417,7 @@ static int bpf_core_apply_relo(struct bpf_program *prog, struct hashmap *cand_cache) { const void *type_key = u32_as_hash_key(relo->type_id); + struct bpf_core_relo_result *core_relo = NULL; struct bpf_core_cand_list *cands = NULL; const char *prog_name = prog->name; const struct btf_type *local_type; @@ -5459,7 +5469,18 @@ static int bpf_core_apply_relo(struct bpf_program *prog, } } - return bpf_core_apply_relo_insn(prog_name, insn, insn_idx, relo, relo_idx, local_btf, cands); + if (prog->obj->record_core_relos) { + prog->core_relos = libbpf_reallocarray(prog->core_relos, + sizeof(*prog->core_relos), + prog->nr_core_relos + 1); + if (!prog->core_relos) + return -ENOMEM; + + core_relo = &prog->core_relos[prog->nr_core_relos++]; + } + + return bpf_core_apply_relo_insn(prog_name, insn, insn_idx, relo, relo_idx, local_btf, cands, + core_relo); } static int @@ -5815,6 +5836,28 @@ static int append_subprog_relos(struct bpf_program *main_prog, struct bpf_progra return 0; } +static int append_subprog_core_relos(struct bpf_program *main_prog, struct bpf_program *subprog) +{ + int new_cnt = main_prog->nr_core_relos + subprog->nr_core_relos; + struct bpf_core_relo_result *relos; + int i; + + if (main_prog == subprog) + return 0; + relos = libbpf_reallocarray(main_prog->core_relos, new_cnt, sizeof(*relos)); + if (!relos) + return -ENOMEM; + memcpy(relos + main_prog->nr_core_relos, subprog->core_relos, + sizeof(*relos) * subprog->nr_core_relos); + + for (i = main_prog->nr_core_relos; i < new_cnt; i++) + relos[i].insn_idx += subprog->sub_insn_off; + + main_prog->core_relos = relos; + main_prog->nr_core_relos = new_cnt; + return 0; +} + static int bpf_object__reloc_code(struct bpf_object *obj, struct bpf_program *main_prog, struct bpf_program *prog) @@ -5918,6 +5961,11 @@ bpf_object__reloc_code(struct bpf_object *obj, struct bpf_program *main_prog, err = append_subprog_relos(main_prog, subprog); if (err) return err; + + err = append_subprog_core_relos(main_prog, subprog); + if (err) + return err; + err = bpf_object__reloc_code(obj, main_prog, subprog); if (err) return err; @@ -6168,6 +6216,7 @@ bpf_object__finish_relocate(struct bpf_object *obj) insn[0].src_reg = BPF_PSEUDO_MAP_VALUE; insn[0].imm = obj->maps[obj->kconfig_map_idx].fd; } + break; default: break; } @@ -6845,6 +6894,8 @@ __bpf_object__open(const char *path, const void *obj_buf, size_t obj_buf_sz, } } + obj->record_core_relos = OPTS_GET(opts, record_core_relos, false); + err = bpf_object__elf_init(obj); err = err ? : bpf_object__check_endianness(obj); err = err ? : bpf_object__elf_collect(obj); @@ -8253,6 +8304,16 @@ size_t bpf_program__insn_cnt(const struct bpf_program *prog) return prog->insns_cnt; } +const struct bpf_core_relo_result *bpf_program__core_relos(struct bpf_program *prog) +{ + return prog->core_relos; +} + +int bpf_program__core_relos_cnt(struct bpf_program *prog) +{ + return prog->nr_core_relos; +} + int bpf_program__set_prep(struct bpf_program *prog, int nr_instances, bpf_program_prep_t prep) { diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index d206b4400a4d..47c8ac41f61b 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h @@ -111,8 +111,12 @@ struct bpf_object_open_opts { * struct_ops, etc) will need actual kernel BTF at /sys/kernel/btf/vmlinux. */ struct btf *btf_custom; + /* Keep track of CO-RE relocation results. This information can be retrieved + * with bpf_program__core_relos() after the object is prepared. + */ + bool record_core_relos; }; -#define bpf_object_open_opts__last_field btf_custom +#define bpf_object_open_opts__last_field record_core_relos LIBBPF_API struct bpf_object *bpf_object__open(const char *path); LIBBPF_API struct bpf_object * @@ -286,6 +290,49 @@ LIBBPF_API int bpf_program__pin(struct bpf_program *prog, const char *path); LIBBPF_API int bpf_program__unpin(struct bpf_program *prog, const char *path); LIBBPF_API void bpf_program__unload(struct bpf_program *prog); +/* bpf_core_relo_kind encodes which aspect of captured field/type/enum value + * has to be adjusted by relocations. + */ +enum bpf_core_relo_kind { + BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */ + BPF_FIELD_BYTE_SIZE = 1, /* field size in bytes */ + BPF_FIELD_EXISTS = 2, /* field existence in target kernel */ + BPF_FIELD_SIGNED = 3, /* field signedness (0 - unsigned, 1 - signed) */ + BPF_FIELD_LSHIFT_U64 = 4, /* bitfield-specific left bitshift */ + BPF_FIELD_RSHIFT_U64 = 5, /* bitfield-specific right bitshift */ + BPF_TYPE_ID_LOCAL = 6, /* type ID in local BPF object */ + BPF_TYPE_ID_TARGET = 7, /* type ID in target kernel */ + BPF_TYPE_EXISTS = 8, /* type existence in target kernel */ + BPF_TYPE_SIZE = 9, /* type size in bytes */ + BPF_ENUMVAL_EXISTS = 10, /* enum value existence in target kernel */ + BPF_ENUMVAL_VALUE = 11, /* enum value integer value */ +}; + +#define BPF_CORE_SPEC_MAX_LEN 64 + +struct bpf_core_relo_spec { + const struct btf *btf; + __u32 root_type_id; + /* accessor spec */ + int spec[BPF_CORE_SPEC_MAX_LEN]; + int spec_len; +}; + +struct bpf_core_relo_result { + struct bpf_core_relo_spec local_spec, targ_spec; + int insn_idx; + enum bpf_core_relo_kind relo_kind; + /* true if libbpf wasn't able to perform the relocation */ + bool poison; + /* original value in the instruction */ + __u32 orig_val; + /* new value that the instruction needs to be patched up to */ + __u32 new_val; +}; + +LIBBPF_API const struct bpf_core_relo_result *bpf_program__core_relos(struct bpf_program *prog); +LIBBPF_API int bpf_program__core_relos_cnt(struct bpf_program *prog); + struct bpf_link; LIBBPF_API struct bpf_link *bpf_link__open(const char *path); diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 459b41228933..4aeb5db9c4e3 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map @@ -416,4 +416,6 @@ LIBBPF_0.6.0 { perf_buffer__new_raw_deprecated; btf__save_raw; bpf_object__prepare; + bpf_program__core_relos; + bpf_program__core_relos_cnt; } LIBBPF_0.5.0; diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c index b5b8956a1be8..11b04c5961a1 100644 --- a/tools/lib/bpf/relo_core.c +++ b/tools/lib/bpf/relo_core.c @@ -13,8 +13,6 @@ #include "str_error.h" #include "libbpf_internal.h" -#define BPF_CORE_SPEC_MAX_LEN 64 - /* represents BPF CO-RE field or array element accessor */ struct bpf_core_accessor { __u32 type_id; /* struct/union type or array element type */ @@ -1092,6 +1090,18 @@ static void bpf_core_dump_spec(int level, const struct bpf_core_spec *spec) } } +static void copy_core_spec(const struct bpf_core_spec *src, struct bpf_core_relo_spec *dst) +{ + int i; + + dst->root_type_id = src->root_type_id; + dst->btf = src->btf; + dst->spec_len = src->raw_len; + + for (i = 0; i < src->raw_len; i++) + dst->spec[i] = src->raw_spec[i]; +} + /* * CO-RE relocate single instruction. * @@ -1147,7 +1157,8 @@ int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn, const struct bpf_core_relo *relo, int relo_idx, const struct btf *local_btf, - struct bpf_core_cand_list *cands) + struct bpf_core_cand_list *cands, + struct bpf_core_relo_result *core_relo) { struct bpf_core_spec local_spec, cand_spec, targ_spec = {}; struct bpf_core_relo_res cand_res, targ_res; @@ -1291,5 +1302,16 @@ int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn, return -EINVAL; } + if (core_relo) { + copy_core_spec(&local_spec, &core_relo->local_spec); + copy_core_spec(&targ_spec, &core_relo->targ_spec); + + core_relo->insn_idx = insn_idx; + core_relo->poison = targ_res.poison; + core_relo->relo_kind = targ_spec.relo_kind; + core_relo->orig_val = targ_res.orig_val; + core_relo->new_val = targ_res.new_val; + } + return 0; } diff --git a/tools/lib/bpf/relo_core.h b/tools/lib/bpf/relo_core.h index 3b9f8f18346c..89d7c4c31ccd 100644 --- a/tools/lib/bpf/relo_core.h +++ b/tools/lib/bpf/relo_core.h @@ -4,23 +4,7 @@ #ifndef __RELO_CORE_H #define __RELO_CORE_H -/* bpf_core_relo_kind encodes which aspect of captured field/type/enum value - * has to be adjusted by relocations. - */ -enum bpf_core_relo_kind { - BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */ - BPF_FIELD_BYTE_SIZE = 1, /* field size in bytes */ - BPF_FIELD_EXISTS = 2, /* field existence in target kernel */ - BPF_FIELD_SIGNED = 3, /* field signedness (0 - unsigned, 1 - signed) */ - BPF_FIELD_LSHIFT_U64 = 4, /* bitfield-specific left bitshift */ - BPF_FIELD_RSHIFT_U64 = 5, /* bitfield-specific right bitshift */ - BPF_TYPE_ID_LOCAL = 6, /* type ID in local BPF object */ - BPF_TYPE_ID_TARGET = 7, /* type ID in target kernel */ - BPF_TYPE_EXISTS = 8, /* type existence in target kernel */ - BPF_TYPE_SIZE = 9, /* type size in bytes */ - BPF_ENUMVAL_EXISTS = 10, /* enum value existence in target kernel */ - BPF_ENUMVAL_VALUE = 11, /* enum value integer value */ -}; +#include "libbpf.h" /* The minimum bpf_core_relo checked by the loader * @@ -92,7 +76,8 @@ int bpf_core_apply_relo_insn(const char *prog_name, struct bpf_insn *insn, int insn_idx, const struct bpf_core_relo *relo, int relo_idx, const struct btf *local_btf, - struct bpf_core_cand_list *cands); + struct bpf_core_cand_list *cands, + struct bpf_core_relo_result *core_relo); int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id, const struct btf *targ_btf, __u32 targ_id); -- 2.25.1