On Tue, Sep 3, 2024 at 12:33 AM Tony Ambardar <[email protected]> wrote:
>
> 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 <[email protected]>
> ---
> tools/lib/bpf/libbpf.c | 52 +++++++++++++++++++++++++++------
> tools/lib/bpf/libbpf_internal.h | 11 +++++++
> 2 files changed, 54 insertions(+), 9 deletions(-)
>
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 0226d3b50709..46f41ea5e74d 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
> @@ -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,21 @@ 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);
Does it fit in 100 characters? If yes, it stays on single line. That's
the rule for all code, don't wrap lines unnecessarily.
> +}
> +
> static const struct btf_member *
> find_member_by_offset(const struct btf_type *t, __u32 bit_offset)
> {
> @@ -1506,6 +1523,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 +1589,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 +1627,15 @@ static int bpf_object__elf_init(struct bpf_object
> *obj)
> 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
> @@ -3953,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
> */
> @@ -7992,7 +8020,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);
> @@ -8498,8 +8525,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);
> diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h
> index a8531195acd4..eda7a0cc2b8c 100644
> --- a/tools/lib/bpf/libbpf_internal.h
> +++ b/tools/lib/bpf/libbpf_internal.h
> @@ -11,6 +11,7 @@
>
> #include <stdlib.h>
> #include <limits.h>
> +#include <byteswap.h>
> #include <errno.h>
> #include <linux/err.h>
> #include <fcntl.h>
> @@ -616,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).
> --
> 2.34.1
>