diff mbox series

[bpf-next,v5,5/8] libbpf: Support opening bpf objects of either endianness

Message ID 6f2af9efc5b64d5c4669d48927393b97b0232497.1725347944.git.tony.ambardar@gmail.com (mailing list archive)
State New
Headers show
Series libbpf, selftests/bpf: Support cross-endian usage | expand

Commit Message

Tony Ambardar Sept. 3, 2024, 7:33 a.m. UTC
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>
---
 tools/lib/bpf/libbpf.c          | 52 +++++++++++++++++++++++++++------
 tools/lib/bpf/libbpf_internal.h | 11 +++++++
 2 files changed, 54 insertions(+), 9 deletions(-)

Comments

Andrii Nakryiko Sept. 4, 2024, 7:50 p.m. UTC | #1
On Tue, Sep 3, 2024 at 12:33 AM Tony Ambardar <tony.ambardar@gmail.com> 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 <tony.ambardar@gmail.com>
> ---
>  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
>
diff mbox series

Patch

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);
+}
+
 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).