diff mbox series

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

Message ID a12e83308e11b15501aa3b9e927bc94139418ce3.1724976539.git.tony.ambardar@gmail.com (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series libbpf, selftests/bpf: Support cross-endian usage | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-15 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-17 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-18 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17-O2
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18-O2
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-37 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-38 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 7 this patch: 7
netdev/build_tools success Errors and warnings before: 172 this patch: 4
netdev/cc_maintainers success CCed 13 of 13 maintainers
netdev/build_clang success Errors and warnings before: 7 this patch: 7
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 7 this patch: 7
netdev/checkpatch warning CHECK: Please don't use multiple blank lines
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Tony Ambardar Aug. 30, 2024, 7:29 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          | 49 +++++++++++++++++++++++++++------
 tools/lib/bpf/libbpf_internal.h | 11 ++++++++
 2 files changed, 52 insertions(+), 8 deletions(-)

Comments

Andrii Nakryiko Aug. 30, 2024, 9:25 p.m. UTC | #1
On Fri, Aug 30, 2024 at 12:30 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          | 49 +++++++++++++++++++++++++++------
>  tools/lib/bpf/libbpf_internal.h | 11 ++++++++
>  2 files changed, 52 insertions(+), 8 deletions(-)
>

[...]

>
> +       /* 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 preserve outside lifetime of bpf_object_open() */

what does it mean "preserve outside lifetime" ?

> +       obj->byteorder = ehdr->e_ident[EI_DATA];
> +
> +
> +

why so many empty lines?..

>         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));

[...]

>         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);
> @@ -8500,6 +8529,10 @@ static int bpf_object_load(struct bpf_object *obj, int extra_log_level, const ch
>
>         if (obj->gen_loader)
>                 bpf_gen__init(obj->gen_loader, extra_log_level, obj->nr_programs, obj->nr_maps);

nit: add {} around if, both sides should either have or not have {}

> +       else if (!is_native_endianness(obj)) {
> +               pr_warn("object '%s' is not native endianness\n", obj->name);

"object '%s': load is not supported in non-native endianness\n"


> +               return libbpf_err(-LIBBPF_ERRNO__ENDIAN);
> +       }
>
>         err = bpf_object_prepare_token(obj);
>         err = err ? : bpf_object__probe_loading(obj);

[...]
Eduard Zingerman Aug. 31, 2024, 1:16 a.m. UTC | #2
On Fri, 2024-08-30 at 00:29 -0700, Tony Ambardar wrote:

[...]

> @@ -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("prog '%s': converted %zu insns to native byte order\n",
> +			 prog->name, prog->insns_cnt);

Nit: pr_debug already printed available programs at this point,
     maybe move this call outside of both loops?

> +	}
> +}
> +
>  static const struct btf_member *
>  find_member_by_offset(const struct btf_type *t, __u32 bit_offset)
>  {

[...]
Eduard Zingerman Aug. 31, 2024, 1:26 a.m. UTC | #3
On Fri, 2024-08-30 at 14:25 -0700, Andrii Nakryiko wrote:

[...]

> >         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);
> > @@ -8500,6 +8529,10 @@ static int bpf_object_load(struct bpf_object *obj, int extra_log_level, const ch
> > 
> >         if (obj->gen_loader)
> >                 bpf_gen__init(obj->gen_loader, extra_log_level, obj->nr_programs, obj->nr_maps);
> 
> nit: add {} around if, both sides should either have or not have {}
> 
> > +       else if (!is_native_endianness(obj)) {
> > +               pr_warn("object '%s' is not native endianness\n", obj->name);
> 
> "object '%s': load is not supported in non-native endianness\n"
> 
> 
> > +               return libbpf_err(-LIBBPF_ERRNO__ENDIAN);
> > +       }

Silly question:
  why load is allowed to proceed for non-native endianness when obj->gen_loader is set?
Tony Ambardar Sept. 1, 2024, 6:03 a.m. UTC | #4
On Fri, Aug 30, 2024 at 02:25:54PM -0700, Andrii Nakryiko wrote:
> On Fri, Aug 30, 2024 at 12:30 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          | 49 +++++++++++++++++++++++++++------
> >  tools/lib/bpf/libbpf_internal.h | 11 ++++++++
> >  2 files changed, 52 insertions(+), 8 deletions(-)
> >
> 
> [...]
> 
> >
> > +       /* 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 preserve outside lifetime of bpf_object_open() */
> 
> what does it mean "preserve outside lifetime" ?

bpf_object_open() freed ELF data on exit but didn't zero obj->efile.ehdr,
leading to unpredictable use-after-free problems in is_native_endianness().
This is part of the fix but should be clearer e.g. "save after ELF data
freed...". Will update.

> 
> > +       obj->byteorder = ehdr->e_ident[EI_DATA];
> > +
> > +
> > +
> 
> why so many empty lines?..

I'm blind? Fixed, thanks.

> 
> >         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));
> 
> [...]
> 
> >         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);
> > @@ -8500,6 +8529,10 @@ static int bpf_object_load(struct bpf_object *obj, int extra_log_level, const ch
> >
> >         if (obj->gen_loader)
> >                 bpf_gen__init(obj->gen_loader, extra_log_level, obj->nr_programs, obj->nr_maps);
> 
> nit: add {} around if, both sides should either have or not have {}
> 

OK, done.

> > +       else if (!is_native_endianness(obj)) {
> > +               pr_warn("object '%s' is not native endianness\n", obj->name);
> 
> "object '%s': load is not supported in non-native endianness\n"

Clearer, will update.

> 
> 
> > +               return libbpf_err(-LIBBPF_ERRNO__ENDIAN);
> > +       }
> >
> >         err = bpf_object_prepare_token(obj);
> >         err = err ? : bpf_object__probe_loading(obj);
> 
> [...]
Tony Ambardar Sept. 1, 2024, 6:04 a.m. UTC | #5
On Fri, Aug 30, 2024 at 06:16:25PM -0700, Eduard Zingerman wrote:
> On Fri, 2024-08-30 at 00:29 -0700, Tony Ambardar wrote:
> 
> [...]
> 
> > @@ -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("prog '%s': converted %zu insns to native byte order\n",
> > +			 prog->name, prog->insns_cnt);
> 
> Nit: pr_debug already printed available programs at this point,
>      maybe move this call outside of both loops?
> 

Good point. Will update to summarize # of programs converted instead.

> > +	}
> > +}
> > +
> >  static const struct btf_member *
> >  find_member_by_offset(const struct btf_type *t, __u32 bit_offset)
> >  {
> 
> [...]
>
Tony Ambardar Sept. 1, 2024, 6:05 a.m. UTC | #6
On Fri, Aug 30, 2024 at 06:26:10PM -0700, Eduard Zingerman wrote:
> On Fri, 2024-08-30 at 14:25 -0700, Andrii Nakryiko wrote:
> 
> [...]
> 
> > >         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);
> > > @@ -8500,6 +8529,10 @@ static int bpf_object_load(struct bpf_object *obj, int extra_log_level, const ch
> > > 
> > >         if (obj->gen_loader)
> > >                 bpf_gen__init(obj->gen_loader, extra_log_level, obj->nr_programs, obj->nr_maps);
> > 
> > nit: add {} around if, both sides should either have or not have {}
> > 
> > > +       else if (!is_native_endianness(obj)) {
> > > +               pr_warn("object '%s' is not native endianness\n", obj->name);
> > 
> > "object '%s': load is not supported in non-native endianness\n"
> > 
> > 
> > > +               return libbpf_err(-LIBBPF_ERRNO__ENDIAN);
> > > +       }
> 
> Silly question:
>   why load is allowed to proceed for non-native endianness when obj->gen_loader is set?
> 

Not silly, had similar questions. Having obj->gen_loader set means "light
skeleton" is being generated, where it tries to eliminate dependency on
libbpf by skeleton code. In this mode, the code doesn't load anything but
instead tracks "what would libbpf do" so it can later write a pure BPF
loader program. Alexei will correct me or elaborate as needed I hope.

Unconditionally blocking on non-native endianness would break light skel.
diff mbox series

Patch

diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 0226d3b50709..aa52870b1967 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("prog '%s': converted %zu insns to native byte order\n",
+			 prog->name, prog->insns_cnt);
+	}
+}
+
 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,18 @@  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 preserve outside lifetime of bpf_object_open() */
+	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 +1629,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 +3979,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 +8022,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);
@@ -8500,6 +8529,10 @@  static int bpf_object_load(struct bpf_object *obj, int extra_log_level, const ch
 
 	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' is not native endianness\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 81d375015c2b..f32e3e8378a5 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>
@@ -621,6 +622,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).