libbpf: Support opening bpf objects of either endianness
authorTony Ambardar <tony.ambardar@gmail.com>
Mon, 16 Sep 2024 08:37:44 +0000 (01:37 -0700)
committerAlexei Starovoitov <ast@kernel.org>
Fri, 4 Oct 2024 00:47:36 +0000 (17:47 -0700)
Allow bpf_object__open() to access files of either endianness, and convert
included BPF programs to native byte-order in-memory for introspection.
Loading BPF objects of non-native byte-order is still disallowed however.

Signed-off-by: Tony Ambardar <tony.ambardar@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/26353c1a1887a54400e1acd6c138fa90c99cdd40.1726475448.git.tony.ambardar@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/lib/bpf/libbpf.c
tools/lib/bpf/libbpf_internal.h

index a7cbb30605da85bc7e847f29aacd3594be4455b4..041228674bfa9849ae680bba4c51fb6e3c7befd4 100644 (file)
@@ -694,6 +694,8 @@ struct bpf_object {
        /* Information when doing ELF related work. Only valid if efile.elf is not NULL */
        struct elf_state efile;
 
+       unsigned char byteorder;
+
        struct btf *btf;
        struct btf_ext *btf_ext;
 
@@ -940,6 +942,20 @@ bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data,
        return 0;
 }
 
+static void bpf_object_bswap_progs(struct bpf_object *obj)
+{
+       struct bpf_program *prog = obj->programs;
+       struct bpf_insn *insn;
+       int p, i;
+
+       for (p = 0; p < obj->nr_programs; p++, prog++) {
+               insn = prog->insns;
+               for (i = 0; i < prog->insns_cnt; i++, insn++)
+                       bpf_insn_bswap(insn);
+       }
+       pr_debug("converted %zu BPF programs to native byte order\n", obj->nr_programs);
+}
+
 static const struct btf_member *
 find_member_by_offset(const struct btf_type *t, __u32 bit_offset)
 {
@@ -1506,6 +1522,7 @@ static void bpf_object__elf_finish(struct bpf_object *obj)
 
        elf_end(obj->efile.elf);
        obj->efile.elf = NULL;
+       obj->efile.ehdr = NULL;
        obj->efile.symbols = NULL;
        obj->efile.arena_data = NULL;
 
@@ -1571,6 +1588,16 @@ static int bpf_object__elf_init(struct bpf_object *obj)
                goto errout;
        }
 
+       /* Validate ELF object endianness... */
+       if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
+           ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
+               err = -LIBBPF_ERRNO__ENDIAN;
+               pr_warn("elf: '%s' has unknown byte order\n", obj->path);
+               goto errout;
+       }
+       /* and save after bpf_object_open() frees ELF data */
+       obj->byteorder = ehdr->e_ident[EI_DATA];
+
        if (elf_getshdrstrndx(elf, &obj->efile.shstrndx)) {
                pr_warn("elf: failed to get section names section index for %s: %s\n",
                        obj->path, elf_errmsg(-1));
@@ -1599,19 +1626,15 @@ errout:
        return err;
 }
 
-static int bpf_object__check_endianness(struct bpf_object *obj)
+static bool is_native_endianness(struct bpf_object *obj)
 {
 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-       if (obj->efile.ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
-               return 0;
+       return obj->byteorder == ELFDATA2LSB;
 #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-       if (obj->efile.ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
-               return 0;
+       return obj->byteorder == ELFDATA2MSB;
 #else
 # error "Unrecognized __BYTE_ORDER__"
 #endif
-       pr_warn("elf: endianness mismatch in %s.\n", obj->path);
-       return -LIBBPF_ERRNO__ENDIAN;
 }
 
 static int
@@ -3954,6 +3977,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
                return -LIBBPF_ERRNO__FORMAT;
        }
 
+       /* change BPF program insns to native endianness for introspection */
+       if (!is_native_endianness(obj))
+               bpf_object_bswap_progs(obj);
+
        /* sort BPF programs by section name and in-section instruction offset
         * for faster search
         */
@@ -7997,7 +8024,6 @@ static struct bpf_object *bpf_object_open(const char *path, const void *obj_buf,
        }
 
        err = bpf_object__elf_init(obj);
-       err = err ? : bpf_object__check_endianness(obj);
        err = err ? : bpf_object__elf_collect(obj);
        err = err ? : bpf_object__collect_externs(obj);
        err = err ? : bpf_object_fixup_btf(obj);
@@ -8503,8 +8529,15 @@ static int bpf_object_load(struct bpf_object *obj, int extra_log_level, const ch
                return libbpf_err(-EINVAL);
        }
 
-       if (obj->gen_loader)
+       /* Disallow kernel loading programs of non-native endianness but
+        * permit cross-endian creation of "light skeleton".
+        */
+       if (obj->gen_loader) {
                bpf_gen__init(obj->gen_loader, extra_log_level, obj->nr_programs, obj->nr_maps);
+       } else if (!is_native_endianness(obj)) {
+               pr_warn("object '%s': loading non-native endianness is unsupported\n", obj->name);
+               return libbpf_err(-LIBBPF_ERRNO__ENDIAN);
+       }
 
        err = bpf_object_prepare_token(obj);
        err = err ? : bpf_object__probe_loading(obj);
index 182529360c84c5fec7c130abbb10eda45279e418..de498e2dd6b0bba440fd0d009ffe593e778946e9 100644 (file)
@@ -617,6 +617,16 @@ static inline bool is_ldimm64_insn(struct bpf_insn *insn)
        return insn->code == (BPF_LD | BPF_IMM | BPF_DW);
 }
 
+static inline void bpf_insn_bswap(struct bpf_insn *insn)
+{
+       __u8 tmp_reg = insn->dst_reg;
+
+       insn->dst_reg = insn->src_reg;
+       insn->src_reg = tmp_reg;
+       insn->off = bswap_16(insn->off);
+       insn->imm = bswap_32(insn->imm);
+}
+
 /* Unconditionally dup FD, ensuring it doesn't use [0, 2] range.
  * Original FD is not closed or altered in any other way.
  * Preserves original FD value, if it's invalid (negative).