Message ID | 62a2ef41629ad5ef7db48d720959527462e1beca.1724216108.git.tony.ambardar@gmail.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | libbpf, selftests/bpf: Support cross-endian usage | expand |
On Wed, Aug 21, 2024 at 2:10 AM Tony Ambardar <tony.ambardar@gmail.com> wrote: > > > +static inline void bpf_insn_bswap(struct bpf_insn *insn) > +{ > + /* dst_reg & src_reg nibbles */ > + __u8 *regs = (__u8 *)insn + offsetofend(struct bpf_insn, code); > + > + *regs = (*regs >> 4) | (*regs << 4); > + insn->off = bswap_16(insn->off); > + insn->imm = bswap_32(insn->imm); > +} This is really great! Thank you for working on it. This idea was brought up a couple times, since folks want to compile bpf prog once, embed it into their user space binary, and auto adjust to target endianness. Cross compilation isn't important to them, but the ability to embed a single .o instead of two .o-s is a big win. It's great that the above insn, elf and btf adjustments are working. Since endianness is encoded in elf what's the point of extra btf_ext__endianness libbpf api? Aren't elf and btf.ext suppose to be in the same endianness all the time?
On Wed, Aug 21, 2024 at 06:55:58PM -0700, Alexei Starovoitov wrote: > On Wed, Aug 21, 2024 at 2:10 AM Tony Ambardar <tony.ambardar@gmail.com> wrote: > > > > > > +static inline void bpf_insn_bswap(struct bpf_insn *insn) > > +{ > > + /* dst_reg & src_reg nibbles */ > > + __u8 *regs = (__u8 *)insn + offsetofend(struct bpf_insn, code); > > + > > + *regs = (*regs >> 4) | (*regs << 4); > > + insn->off = bswap_16(insn->off); > > + insn->imm = bswap_32(insn->imm); > > +} > > This is really great! > Thank you for working on it. Happy to help! The endian restrictions were a long-time annoyance for me. > > This idea was brought up a couple times, since folks want to compile > bpf prog once, embed it into their user space binary, > and auto adjust to target endianness. > Cross compilation isn't important to them, > but the ability to embed a single .o instead of two .o-s is a big win. Ah, interesting use case. I hadn't really considered that or tested it. I suppose .symtab and .rel* have ELF types so OK, .strtab doesn't matter, and now we have BTF/BTF.ext converters, so why not? Something like light skeleton might be a problem though, because the data blob is heterogeneous and would be hard to convert byte-order after writing. > > It's great that the above insn, elf and btf adjustments are working. > Since endianness is encoded in elf what's the point of > extra btf_ext__endianness libbpf api? > Aren't elf and btf.ext suppose to be in the same endianness all the time? I implemented BTF.ext following the BTF endianness API example, which handles raw BTF, in-memory, and not just ELF object files. With BTF, we have API clients like pahole, but only internal usage so far for BTF.ext, and no notion of "raw" BTF.ext. I suppose exposing an API for btf_ext__endianness isn't strictly needed right now, but I can imagine BTF-processing clients using it. What are your thoughts, Andrii? BTW, I just fixed a bug in my light skeleton code that made test_progs 'map_ptr' fail, so will be sending out a v2 patch. Currently, I have only 2 unexpected test failures on s390x: subtest_userns:PASS:socketpair 0 nsec subtest_userns:PASS:fork 0 nsec recvfd:PASS:recvmsg 0 nsec recvfd:PASS:cmsg_null 0 nsec recvfd:PASS:cmsg_len 0 nsec recvfd:PASS:cmsg_level 0 nsec recvfd:PASS:cmsg_type 0 nsec parent:PASS:recv_bpffs_fd 0 nsec materialize_bpffs_fd:PASS:fs_cfg_cmds 0 nsec materialize_bpffs_fd:PASS:fs_cfg_maps 0 nsec materialize_bpffs_fd:PASS:fs_cfg_progs 0 nsec materialize_bpffs_fd:PASS:fs_cfg_attachs 0 nsec parent:PASS:materialize_bpffs_fd 0 nsec sendfd:PASS:sendmsg 0 nsec parent:PASS:send_mnt_fd 0 nsec recvfd:PASS:recvmsg 0 nsec recvfd:PASS:cmsg_null 0 nsec recvfd:PASS:cmsg_len 0 nsec recvfd:PASS:cmsg_level 0 nsec recvfd:PASS:cmsg_type 0 nsec parent:PASS:recv_token_fd 0 nsec parent:FAIL:waitpid_child unexpected error: 22 (errno 3) #402/9 token/obj_priv_implicit_token_envvar:FAIL and libbpf: prog 'on_event': BPF program load failed: Bad address libbpf: prog 'on_event': -- BEGIN PROG LOAD LOG -- The sequence of 8193 jumps is too complex. verification time 2633000 usec stack depth 360 processed 116096 insns (limit 1000000) max_states_per_insn 1 total_states 5061 peak_states 5061 mark_read 2540 -- END PROG LOAD LOG -- libbpf: prog 'on_event': failed to load: -14 libbpf: failed to load object 'pyperf600.bpf.o' scale_test:FAIL:expect_success unexpected error: -14 (errno 14) #525 verif_scale_pyperf600:FAIL I'd appreciate any thoughts on troubleshooting these, and will continue looking into them. Cheers, Tony
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 8a0a0c1e37e1..a542031f4f73 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c @@ -940,6 +940,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("prog '%s': converted %zu insns to native byteorder\n", + prog->name, prog->insns_cnt); + } +} + static const struct btf_member * find_member_by_offset(const struct btf_type *t, __u32 bit_offset) { @@ -1610,7 +1625,6 @@ static int bpf_object__check_endianness(struct bpf_object *obj) #else # error "Unrecognized __BYTE_ORDER__" #endif - pr_warn("elf: endianness mismatch in %s.\n", obj->path); return -LIBBPF_ERRNO__ENDIAN; } @@ -3953,6 +3967,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 (bpf_object__check_endianness(obj)) + bpf_object_bswap_progs(obj); + /* sort BPF programs by section name and in-section instruction offset * for faster search */ @@ -7993,7 +8011,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); diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h index 6b0270c83537..f53daa601c6f 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> @@ -590,6 +591,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) +{ + /* dst_reg & src_reg nibbles */ + __u8 *regs = (__u8 *)insn + offsetofend(struct bpf_insn, code); + + *regs = (*regs >> 4) | (*regs << 4); + 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. Signed-off-by: Tony Ambardar <tony.ambardar@gmail.com> --- tools/lib/bpf/libbpf.c | 21 +++++++++++++++++++-- tools/lib/bpf/libbpf_internal.h | 11 +++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-)