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 |
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 --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).
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(-)