[RFC PATCH 17/22] perf bpf: relocation programs.

Wang Nan
2015-04-30T10:59:25Z

If an eBPF program access a map, LLVM generates a relocated load
instruction. To enable the usage of that map, relocation must be done
by replacing original instructions by map loading instructions.

This patch do that work by utilizing previous stored relocation section
information in 'struct bpf_obj'.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
---
tools/perf/util/bpf-loader.c | 97 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+)

diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index 34ccc10..fe623df 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -515,6 +515,17 @@ bpf_find_prog_by_name(struct bpf_obj *obj, const char *name)
return NULL;
}

+static struct bpf_perf_prog *
+bpf_find_prog_by_idx(struct bpf_obj *obj, int idx)
+{
+ struct bpf_perf_prog *prog;
+
+ list_for_each_entry(prog, &obj->progs_list, list)
+ if (prog->idx == idx)
+ return prog;
+ return NULL;
+}
+
/*
* Use config_str to config program. If prog is NULL, find a
* prog based on config_str. config_str should not be NULL.
@@ -746,6 +757,90 @@ bpf_obj_create_maps(struct bpf_obj *obj)
return 0;
}

+static int
+bpf_perf_prog_relocate(struct bpf_obj *obj,
+ GElf_Shdr *shdr,
+ Elf_Data *data,
+ struct bpf_perf_prog *prog)
+{
+ int i, nrels;
+
+ pr_debug("bpf: relocating: %s\n", prog->name);
+ nrels = shdr->sh_size / shdr->sh_entsize;
+
+ for (i = 0; i < nrels; i++) {
+ GElf_Sym sym;
+ GElf_Rel rel;
+ unsigned int insn_idx;
+ struct bpf_insn *insns = prog->insns;
+ size_t map_idx;
+
+ if (!gelf_getrel(data, i, &rel)) {
+ pr_err("bpf: relocation: failed to get %d reloc\n", i);
+ return -EINVAL;
+ }
+
+ insn_idx = rel.r_offset / sizeof(struct bpf_insn);
+ pr_debug("bpf: relocation: insn_idx=%u\n", insn_idx);
+
+ if (!gelf_getsym(obj->elf.symbols,
+ GELF_R_SYM(rel.r_info),
+ &sym)) {
+ pr_err("bpf: relocation: symbol %"PRIx64" not found\n",
+ GELF_R_SYM(rel.r_info));
+ return -EINVAL;
+ }
+
+ if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
+ pr_err("bpf: relocation: invalid relo for "
+ "insns[%d].code 0x%x\n",
+ insn_idx, insns[insn_idx].code);
+ return -EINVAL;
+ }
+
+ map_idx = sym.st_value / sizeof(struct bpf_map_def);
+ if (map_idx >= obj->nr_maps) {
+ pr_err("bpf relocation: map_idx %d large than %d\n",
+ (int)map_idx, (int)obj->nr_maps - 1);
+ return -EINVAL;
+ }
+
+ insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
+ insns[insn_idx].imm = obj->maps_fds[map_idx];
+ }
+ return 0;
+}
+
+static int
+bpf_obj_relocate(struct bpf_obj *obj)
+{
+ int i, err;
+
+ for (i = 0; i < obj->elf.nr_reloc; i++) {
+ GElf_Shdr *shdr = &obj->elf.reloc[i].shdr;
+ Elf_Data *data = obj->elf.reloc[i].data;
+ int idx = shdr->sh_info;
+ struct bpf_perf_prog *prog;
+
+ if (shdr->sh_type != SHT_REL) {
+ pr_err("bpf: internal error\n");
+ return -EINVAL;
+ }
+
+ prog = bpf_find_prog_by_idx(obj, idx);
+ if (!prog) {
+ pr_err("bpf: relocation failed: no %d section\n",
+ idx);
+ return -ENOENT;
+ }
+
+ err = bpf_perf_prog_relocate(obj, shdr, data, prog);
+ if (err)
+ return -EINVAL;
+ }
+ return 0;
+}
+
int bpf__load(const char *path)
{
struct bpf_obj *obj;
@@ -776,6 +871,8 @@ int bpf__load(const char *path)
goto out;
if ((err = bpf_obj_create_maps(obj)))
goto out;
+ if ((err = bpf_obj_relocate(obj)))
+ goto out;

list_add(&obj->list, &bpf_obj_list);
return 0;
--
1.8.3.4