Explorar el Código

Merge pull request #727 from tamird/build-everywhere

integration-test: avoid reliance on kernel headers
Tamir Duberstein hace 1 año
padre
commit
cb42d028d0

+ 2 - 5
test/integration-test/Cargo.toml

@@ -5,7 +5,7 @@ edition = "2021"
 publish = false
 
 [dependencies]
-anyhow = { workspace = true, default-features = true }
+anyhow = { workspace = true, features = ["std"] }
 assert_matches = { workspace = true }
 aya = { workspace = true }
 aya-log = { workspace = true }
@@ -16,10 +16,7 @@ netns-rs = { workspace = true }
 object = { workspace = true }
 rbpf = { workspace = true }
 test-case = { workspace = true }
-tokio = { workspace = true, default-features = false, features = [
-    "macros",
-    "time",
-] }
+tokio = { workspace = true, features = ["macros", "time"] }
 
 [build-dependencies]
 cargo_metadata = { workspace = true }

+ 4 - 5
test/integration-test/bpf/ext.bpf.c

@@ -1,10 +1,9 @@
-#include <linux/bpf.h>
+// clang-format off
+#include <vmlinux.h>
 #include <bpf/bpf_helpers.h>
+// clang-format on
 
 SEC("xdp")
-int xdp_drop(struct xdp_md *ctx)
-{
-    return XDP_DROP;
-}
+int xdp_drop(struct xdp_md *ctx) { return XDP_DROP; }
 
 char _license[] SEC("license") = "GPL";

+ 4 - 5
test/integration-test/bpf/main.bpf.c

@@ -1,10 +1,9 @@
-#include <linux/bpf.h>
+// clang-format off
+#include <vmlinux.h>
 #include <bpf/bpf_helpers.h>
+// clang-format on
 
 SEC("xdp")
-int xdp_pass(struct xdp_md *ctx)
-{
-    return XDP_PASS;
-}
+int xdp_pass(struct xdp_md *ctx) { return XDP_PASS; }
 
 char _license[] SEC("license") = "GPL";

+ 18 - 18
test/integration-test/bpf/multimap-btf.bpf.c

@@ -1,30 +1,30 @@
-#include <linux/bpf.h>
+// clang-format off
+#include <vmlinux.h>
 #include <bpf/bpf_helpers.h>
+// clang-format on
 
 struct {
-	__uint(type, BPF_MAP_TYPE_ARRAY);
-	__type(key, __u32);
-	__type(value, __u64);
-	__uint(max_entries, 1);
+  __uint(type, BPF_MAP_TYPE_ARRAY);
+  __type(key, __u32);
+  __type(value, __u64);
+  __uint(max_entries, 1);
 } map_1 SEC(".maps");
 
 struct {
-	__uint(type, BPF_MAP_TYPE_ARRAY);
-	__type(key, __u32);
-	__type(value, __u64);
-	__uint(max_entries, 1);
+  __uint(type, BPF_MAP_TYPE_ARRAY);
+  __type(key, __u32);
+  __type(value, __u64);
+  __uint(max_entries, 1);
 } map_2 SEC(".maps");
 
-
 SEC("tracepoint")
-int bpf_prog(void *ctx)
-{
-	__u32 key = 0;
-	__u64 twenty_four = 24;
-	__u64 forty_two = 42;
-    bpf_map_update_elem(&map_1, &key, &twenty_four, BPF_ANY);
-    bpf_map_update_elem(&map_2, &key, &forty_two, BPF_ANY);
-	return 0;
+int bpf_prog(void *ctx) {
+  __u32 key = 0;
+  __u64 twenty_four = 24;
+  __u64 forty_two = 42;
+  bpf_map_update_elem(&map_1, &key, &twenty_four, BPF_ANY);
+  bpf_map_update_elem(&map_2, &key, &forty_two, BPF_ANY);
+  return 0;
 }
 
 char _license[] SEC("license") = "GPL";

+ 65 - 18
test/integration-test/bpf/reloc.bpf.c

@@ -1,10 +1,10 @@
 // clang-format off
-#include <linux/bpf.h>
+#include <vmlinux.h>
 #include <bpf/bpf_helpers.h>
 #include <bpf/bpf_core_read.h>
 // clang-format on
 
-char _license[] __attribute__((section("license"), used)) = "GPL";
+char _license[] SEC("license") = "GPL";
 
 struct {
   __uint(type, BPF_MAP_TYPE_ARRAY);
@@ -19,12 +19,17 @@ long set_output(__u64 value) {
 }
 
 struct relocated_struct_with_scalars {
+#ifndef TARGET
   __u8 a;
+#endif
   __u8 b;
   __u8 c;
+#ifdef TARGET
+  __u8 d;
+#endif
 };
 
-__attribute__((noinline)) int field_global() {
+__noinline int field_global() {
   struct relocated_struct_with_scalars s = {1, 2, 3};
   return set_output(__builtin_preserve_access_index(s.b));
 }
@@ -32,11 +37,16 @@ __attribute__((noinline)) int field_global() {
 SEC("uprobe") int field(void *ctx) { return field_global(); }
 
 struct relocated_struct_with_pointer {
+#ifndef TARGET
   struct relocated_struct_with_pointer *first;
+#endif
   struct relocated_struct_with_pointer *second;
+#ifdef TARGET
+  struct relocated_struct_with_pointer *first;
+#endif
 };
 
-__attribute__((noinline)) int pointer_global() {
+__noinline int pointer_global() {
   struct relocated_struct_with_pointer s = {
       (struct relocated_struct_with_pointer *)42,
       (struct relocated_struct_with_pointer *)21,
@@ -46,49 +56,86 @@ __attribute__((noinline)) int pointer_global() {
 
 SEC("uprobe") int pointer(void *ctx) { return pointer_global(); }
 
-__attribute__((noinline)) int struct_flavors_global() {
+__noinline int struct_flavors_global() {
   struct relocated_struct_with_scalars s = {1, 2, 3};
+#ifndef TARGET
   if (bpf_core_field_exists(s.a)) {
     return set_output(__builtin_preserve_access_index(s.a));
+#else
+  if (bpf_core_field_exists(s.d)) {
+    return set_output(__builtin_preserve_access_index(s.d));
+#endif
   } else {
-    return set_output(__builtin_preserve_access_index(s.b));
+    return set_output(__builtin_preserve_access_index(s.c));
   }
 }
 
 SEC("uprobe") int struct_flavors(void *ctx) { return struct_flavors_global(); }
 
-enum relocated_enum_unsigned_32 { U32 = 0xAAAAAAAA };
+enum relocated_enum_unsigned_32 {
+  U32_VAL =
+#ifndef TARGET
+      0xAAAAAAAA
+#else
+      0xBBBBBBBB
+#endif
+};
 
-__attribute__((noinline)) int enum_unsigned_32_global() {
-  return set_output(bpf_core_enum_value(enum relocated_enum_unsigned_32, U32));
+__noinline int enum_unsigned_32_global() {
+  return set_output(
+      bpf_core_enum_value(enum relocated_enum_unsigned_32, U32_VAL));
 }
 
 SEC("uprobe") int enum_unsigned_32(void *ctx) {
   return enum_unsigned_32_global();
 }
 
-enum relocated_enum_signed_32 { S32 = -0x7AAAAAAA };
+enum relocated_enum_signed_32 {
+  S32_VAL =
+#ifndef TARGET
+      -0x7AAAAAAA
+#else
+      -0x7BBBBBBB
+#endif
+};
 
-__attribute__((noinline)) int enum_signed_32_global() {
-  return set_output(bpf_core_enum_value(enum relocated_enum_signed_32, S32));
+__noinline int enum_signed_32_global() {
+  return set_output(
+      bpf_core_enum_value(enum relocated_enum_signed_32, S32_VAL));
 }
 
 SEC("uprobe") int enum_signed_32(void *ctx) { return enum_signed_32_global(); }
 
-enum relocated_enum_unsigned_64 { U64 = 0xAAAAAAAABBBBBBBB };
+enum relocated_enum_unsigned_64 {
+  U64_VAL =
+#ifndef TARGET
+      0xAAAAAAAABBBBBBBB
+#else
+      0xCCCCCCCCDDDDDDDD
+#endif
+};
 
-__attribute__((noinline)) int enum_unsigned_64_global() {
-  return set_output(bpf_core_enum_value(enum relocated_enum_unsigned_64, U64));
+__noinline int enum_unsigned_64_global() {
+  return set_output(
+      bpf_core_enum_value(enum relocated_enum_unsigned_64, U64_VAL));
 }
 
 SEC("uprobe") int enum_unsigned_64(void *ctx) {
   return enum_unsigned_64_global();
 }
 
-enum relocated_enum_signed_64 { u64 = -0xAAAAAAABBBBBBBB };
+enum relocated_enum_signed_64 {
+  S64_VAL =
+#ifndef TARGET
+      -0xAAAAAAABBBBBBBB
+#else
+      -0xCCCCCCCDDDDDDDD
+#endif
+};
 
-__attribute__((noinline)) int enum_signed_64_global() {
-  return set_output(bpf_core_enum_value(enum relocated_enum_signed_64, u64));
+__noinline int enum_signed_64_global() {
+  return set_output(
+      bpf_core_enum_value(enum relocated_enum_signed_64, S64_VAL));
 }
 
 SEC("uprobe") int enum_signed_64(void *ctx) { return enum_signed_64_global(); }

+ 0 - 77
test/integration-test/bpf/reloc.btf.c

@@ -1,77 +0,0 @@
-// clang-format off
-#include <linux/bpf.h>
-#include <bpf/bpf_helpers.h>
-#include <bpf/bpf_core_read.h>
-// clang-format on
-
-#include <stdlib.h>
-
-long set_output(__u64 value) { exit((int)value); }
-
-struct relocated_struct_with_scalars {
-  __u8 b;
-  __u8 c;
-  __u8 d;
-};
-
-__attribute__((noinline)) int field_global() {
-  struct relocated_struct_with_scalars s = {1, 2, 3};
-  return set_output(__builtin_preserve_access_index(s.b));
-}
-
-struct relocated_struct_with_pointer {
-  struct relocated_struct_with_pointer *second;
-  struct relocated_struct_with_pointer *first;
-};
-
-__attribute__((noinline)) int pointer_global() {
-  struct relocated_struct_with_pointer s = {
-      (struct relocated_struct_with_pointer *)42,
-      (struct relocated_struct_with_pointer *)21,
-  };
-  return set_output((__u64)__builtin_preserve_access_index(s.first));
-}
-
-__attribute__((noinline)) int struct_flavors_global() {
-  struct relocated_struct_with_scalars s = {1, 2, 3};
-  if (bpf_core_field_exists(s.b)) {
-    return set_output(__builtin_preserve_access_index(s.b));
-  } else {
-    return set_output(__builtin_preserve_access_index(s.c));
-  }
-}
-
-enum relocated_enum_unsigned_32 { U32 = 0xBBBBBBBB };
-
-__attribute__((noinline)) int enum_unsigned_32_global() {
-  return set_output(bpf_core_enum_value(enum relocated_enum_unsigned_32, U32));
-}
-
-enum relocated_enum_signed_32 { S32 = -0x7BBBBBBB };
-
-__attribute__((noinline)) int enum_signed_32_global() {
-  return set_output(bpf_core_enum_value(enum relocated_enum_signed_32, S32));
-}
-
-enum relocated_enum_unsigned_64 { U64 = 0xCCCCCCCCDDDDDDDD };
-
-__attribute__((noinline)) int enum_unsigned_64_global() {
-  return set_output(bpf_core_enum_value(enum relocated_enum_unsigned_64, U64));
-}
-
-enum relocated_enum_signed_64 { u64 = -0xCCCCCCCDDDDDDDD };
-
-__attribute__((noinline)) int enum_signed_64_global() {
-  return set_output(bpf_core_enum_value(enum relocated_enum_signed_64, u64));
-}
-
-// Avoids dead code elimination by the compiler.
-int main() {
-  field_global();
-  pointer_global();
-  struct_flavors_global();
-  enum_unsigned_32_global();
-  enum_signed_32_global();
-  enum_unsigned_64_global();
-  enum_signed_64_global();
-}

+ 14 - 17
test/integration-test/bpf/text_64_64_reloc.c

@@ -1,28 +1,25 @@
-#include <linux/bpf.h>
+// clang-format off
+#include <vmlinux.h>
 #include <bpf/bpf_helpers.h>
+// clang-format on
 
 char _license[] SEC("license") = "GPL";
 
 struct {
-        __uint(type, BPF_MAP_TYPE_ARRAY);
-        __type(key, __u32);
-        __type(value, __u64);
-        __uint(max_entries, 2);
+  __uint(type, BPF_MAP_TYPE_ARRAY);
+  __type(key, __u32);
+  __type(value, __u64);
+  __uint(max_entries, 2);
 } RESULTS SEC(".maps");
 
-static __u64
-inc_cb(void *map, __u32 *key, void *val,
-	       void *data)
-{
-	__u64 *value = val;
-	*value += 1;
-	return 0;
+static __u64 inc_cb(void *map, __u32 *key, void *val, void *data) {
+  __u64 *value = val;
+  *value += 1;
+  return 0;
 }
 
 SEC("uprobe/test_text_64_64_reloc")
-int test_text_64_64_reloc(struct pt_regs *ctx)
-{
-	bpf_for_each_map_elem(&RESULTS, inc_cb, NULL, 0);
-    return 0;
+int test_text_64_64_reloc(struct pt_regs *ctx) {
+  bpf_for_each_map_elem(&RESULTS, inc_cb, NULL, 0);
+  return 0;
 }
-

+ 70 - 75
test/integration-test/build.rs

@@ -4,7 +4,7 @@ use std::{
     fs,
     io::{BufRead as _, BufReader},
     path::PathBuf,
-    process::{Child, Command, Stdio},
+    process::{Child, Command, Output, Stdio},
 };
 
 use cargo_metadata::{
@@ -64,20 +64,14 @@ fn main() {
         panic!("unsupported endian={:?}", endian)
     };
 
-    const C_BPF: &[(&str, &str)] = &[
-        ("ext.bpf.c", "ext.bpf.o"),
-        ("main.bpf.c", "main.bpf.o"),
-        ("multimap-btf.bpf.c", "multimap-btf.bpf.o"),
-        ("reloc.bpf.c", "reloc.bpf.o"),
-        ("text_64_64_reloc.c", "text_64_64_reloc.o"),
+    const C_BPF: &[(&str, bool)] = &[
+        ("ext.bpf.c", false),
+        ("main.bpf.c", false),
+        ("multimap-btf.bpf.c", false),
+        ("reloc.bpf.c", true),
+        ("text_64_64_reloc.c", false),
     ];
 
-    let c_bpf = C_BPF.iter().map(|(src, dst)| (src, out_dir.join(dst)));
-
-    const C_BTF: &[(&str, &str)] = &[("reloc.btf.c", "reloc.btf.o")];
-
-    let c_btf = C_BTF.iter().map(|(src, dst)| (src, out_dir.join(dst)));
-
     if build_integration_bpf {
         let libbpf_dir = manifest_dir
             .parent()
@@ -116,65 +110,67 @@ fn main() {
             target_arch.push(arch);
         };
 
-        for (src, dst) in c_bpf {
-            let src = bpf_dir.join(src);
-            println!("cargo:rerun-if-changed={}", src.to_str().unwrap());
+        // NB: libbpf's documentation suggests that vmlinux.h be generated by running `bpftool btf
+        // dump file /sys/kernel/btf/vmlinux format c`; this allows CO-RE to work.
+        //
+        // However in our tests we do not make use of kernel data structures, and so any vmlinux.h
+        // which defines the constants we need (e.g. `__u8`, `__u64`, `BPF_MAP_TYPE_ARRAY`,
+        // `BPF_ANY`, `XDP_PASS`, `XDP_DROP`, etc.) will suffice. Since we already have a libbpf
+        // submodule which happens to include such a file, we use it.
+        let libbpf_vmlinux_dir = libbpf_dir.join(".github/actions/build-selftests");
 
-            exec(
-                Command::new("clang")
-                    .arg("-I")
-                    .arg(&libbpf_headers_dir)
-                    .args(["-g", "-O2", "-target", target, "-c"])
-                    .arg(&target_arch)
-                    .arg(src)
-                    .arg("-o")
-                    .arg(dst),
-            )
-            .unwrap();
-        }
+        let clang = || {
+            let mut cmd = Command::new("clang");
+            cmd.arg("-nostdlibinc")
+                .arg("-I")
+                .arg(&libbpf_headers_dir)
+                .arg("-I")
+                .arg(&libbpf_vmlinux_dir)
+                .args(["-g", "-O2", "-target", target, "-c"])
+                .arg(&target_arch);
+            cmd
+        };
 
-        for (src, dst) in c_btf {
+        for (src, build_btf) in C_BPF {
+            let dst = out_dir.join(src).with_extension("o");
             let src = bpf_dir.join(src);
             println!("cargo:rerun-if-changed={}", src.to_str().unwrap());
 
-            let mut cmd = Command::new("clang");
-            cmd.arg("-I")
-                .arg(&libbpf_headers_dir)
-                .args(["-g", "-target", target, "-c"])
-                .arg(&target_arch)
-                .arg(src)
-                .args(["-o", "-"]);
-
-            let mut child = cmd
-                .stdout(Stdio::piped())
-                .spawn()
-                .unwrap_or_else(|err| panic!("failed to spawn {cmd:?}: {err}"));
-
-            let Child { stdout, .. } = &mut child;
-            let stdout = stdout.take().unwrap();
-
-            let mut output = OsString::new();
-            output.push(".BTF=");
-            output.push(dst);
-            exec(
-                // NB: objcopy doesn't support reading from stdin, so we have to use llvm-objcopy.
-                Command::new("llvm-objcopy")
-                    .arg("--dump-section")
-                    .arg(output)
-                    .arg("-")
-                    .stdin(stdout),
-            )
-            .unwrap();
-
-            let status = child
-                .wait()
-                .unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}"));
-            match status.code() {
-                Some(code) => match code {
-                    0 => {}
-                    code => panic!("{cmd:?} exited with status code {code}"),
-                },
-                None => panic!("{cmd:?} terminated by signal"),
+            exec(clang().arg(&src).arg("-o").arg(&dst)).unwrap();
+
+            if *build_btf {
+                let mut cmd = clang();
+                let mut child = cmd
+                    .arg("-DTARGET")
+                    .arg(&src)
+                    .args(["-o", "-"])
+                    .stdout(Stdio::piped())
+                    .spawn()
+                    .unwrap_or_else(|err| panic!("failed to spawn {cmd:?}: {err}"));
+
+                let Child { stdout, .. } = &mut child;
+                let stdout = stdout.take().unwrap();
+
+                let dst = dst.with_extension("target.o");
+
+                let mut output = OsString::new();
+                output.push(".BTF=");
+                output.push(dst);
+                exec(
+                    // NB: objcopy doesn't support reading from stdin, so we have to use llvm-objcopy.
+                    Command::new("llvm-objcopy")
+                        .arg("--dump-section")
+                        .arg(output)
+                        .arg("-")
+                        .stdin(stdout),
+                )
+                .unwrap();
+
+                let output = child
+                    .wait_with_output()
+                    .unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}"));
+                let Output { status, .. } = &output;
+                assert_eq!(status.code(), Some(0), "{cmd:?} failed: {output:?}");
             }
         }
 
@@ -257,13 +253,7 @@ fn main() {
         let status = child
             .wait()
             .unwrap_or_else(|err| panic!("failed to wait for {cmd:?}: {err}"));
-        match status.code() {
-            Some(code) => match code {
-                0 => {}
-                code => panic!("{cmd:?} exited with status code {code}"),
-            },
-            None => panic!("{cmd:?} terminated by signal"),
-        }
+        assert_eq!(status.code(), Some(0), "{cmd:?} failed: {status:?}");
 
         stderr.join().map_err(std::panic::resume_unwind).unwrap();
 
@@ -273,8 +263,13 @@ fn main() {
                 .unwrap_or_else(|err| panic!("failed to copy {binary:?} to {dst:?}: {err}"));
         }
     } else {
-        for (_src, dst) in c_bpf.chain(c_btf) {
+        for (src, build_btf) in C_BPF {
+            let dst = out_dir.join(src).with_extension("o");
             fs::write(&dst, []).unwrap_or_else(|err| panic!("failed to create {dst:?}: {err}"));
+            if *build_btf {
+                let dst = dst.with_extension("target.o");
+                fs::write(&dst, []).unwrap_or_else(|err| panic!("failed to create {dst:?}: {err}"));
+            }
         }
 
         let Package { targets, .. } = integration_ebpf_package;

+ 2 - 1
test/integration-test/src/lib.rs

@@ -5,7 +5,8 @@ pub const MAIN: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/main.b
 pub const MULTIMAP_BTF: &[u8] =
     include_bytes_aligned!(concat!(env!("OUT_DIR"), "/multimap-btf.bpf.o"));
 pub const RELOC_BPF: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/reloc.bpf.o"));
-pub const RELOC_BTF: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/reloc.btf.o"));
+pub const RELOC_BTF: &[u8] =
+    include_bytes_aligned!(concat!(env!("OUT_DIR"), "/reloc.bpf.target.o"));
 pub const TEXT_64_64_RELOC: &[u8] =
     include_bytes_aligned!(concat!(env!("OUT_DIR"), "/text_64_64_reloc.o"));
 

+ 9 - 9
test/integration-test/src/tests/btf_relocations.rs

@@ -2,20 +2,20 @@ use test_case::test_case;
 
 use aya::{maps::Array, programs::UProbe, util::KernelVersion, BpfLoader, Btf, Endianness};
 
-#[test_case("field", false, None, 2)]
-#[test_case("field", true, None, 1)]
+#[test_case("enum_signed_32", false, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0x7AAAAAAAi32 as u64)]
+#[test_case("enum_signed_32", true, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0x7BBBBBBBi32 as u64)]
+#[test_case("enum_signed_64", false, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0xAAAAAAABBBBBBBBi64 as u64)]
+#[test_case("enum_signed_64", true, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0xCCCCCCCDDDDDDDDi64 as u64)]
 #[test_case("enum_unsigned_32", false, None, 0xAAAAAAAA)]
 #[test_case("enum_unsigned_32", true, None, 0xBBBBBBBB)]
+#[test_case("enum_unsigned_64", false, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), 0xAAAAAAAABBBBBBBB)]
+#[test_case("enum_unsigned_64", true, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), 0xCCCCCCCCDDDDDDDD)]
+#[test_case("field", false, None, 2)]
+#[test_case("field", true, None, 1)]
 #[test_case("pointer", false, None, 42)]
 #[test_case("pointer", true, None, 21)]
 #[test_case("struct_flavors", false, None, 1)]
-#[test_case("struct_flavors", true, None, 1)]
-#[test_case("enum_signed_32", false, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0x7AAAAAAAi32 as u64)]
-#[test_case("enum_signed_32", true, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0x7BBBBBBBi32 as u64)]
-#[test_case("enum_unsigned_64", false, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), 0xAAAAAAAABBBBBBBB)]
-#[test_case("enum_unsigned_64", true, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), 0xCCCCCCCCDDDDDDDD)]
-#[test_case("enum_signed_64", false, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0xAAAAAAABBBBBBBBi64 as u64)]
-#[test_case("enum_signed_64", true, Some((KernelVersion::new(6, 0, 0), "https://github.com/torvalds/linux/commit/6089fb3")), -0xCCCCCCCDDDDDDDDi64 as u64)]
+#[test_case("struct_flavors", true, None, 2)]
 fn relocation_tests(
     program: &str,
     with_relocations: bool,

+ 2 - 2
xtask/Cargo.toml

@@ -6,10 +6,10 @@ edition = "2021"
 publish = false
 
 [dependencies]
-anyhow = { workspace = true, default-features = true }
+anyhow = { workspace = true, features = ["std"] }
 aya-tool = { workspace = true }
 cargo_metadata = { workspace = true }
-clap = { workspace = true, default-features = true, features = ["derive"] }
+clap = { workspace = true, features = ["derive"] }
 dialoguer = { workspace = true }
 diff = { workspace = true }
 indoc = { workspace = true }

+ 4 - 7
xtask/src/lib.rs

@@ -1,4 +1,4 @@
-use anyhow::{anyhow, Context as _, Result};
+use anyhow::{bail, Context as _, Result};
 use std::{
     fs,
     path::{Path, PathBuf},
@@ -13,13 +13,10 @@ pub fn exec(cmd: &mut Command) -> Result<()> {
     let status = cmd
         .status()
         .with_context(|| format!("failed to run {cmd:?}"))?;
-    match status.code() {
-        Some(code) => match code {
-            0 => Ok(()),
-            code => Err(anyhow!("{cmd:?} exited with code {code}")),
-        },
-        None => Err(anyhow!("{cmd:?} terminated by signal")),
+    if status.code() != Some(0) {
+        bail!("{cmd:?} failed: {status:?}")
     }
+    Ok(())
 }
 
 // Create a symlink in the out directory to work around the fact that cargo ignores anything

+ 1 - 1
xtask/src/public_api.rs

@@ -62,7 +62,7 @@ pub fn public_api(options: Options, metadata: Metadata) -> Result<()> {
         });
 
     if !errors.is_empty() {
-        bail!("public API errors:\n{errors}");
+        bail!("public API errors:\n{errors}")
     }
     Ok(())
 }

+ 8 - 19
xtask/src/run.rs

@@ -6,7 +6,7 @@ use std::{
     process::{Child, Command, Stdio},
 };
 
-use anyhow::{bail, Context as _, Result};
+use anyhow::{anyhow, bail, Context as _, Result};
 use cargo_metadata::{Artifact, CompilerMessage, Message, Target};
 use clap::Parser;
 use xtask::AYA_BUILD_INTEGRATION_BPF;
@@ -77,14 +77,9 @@ pub fn build(opts: BuildOptions) -> Result<Vec<(String, PathBuf)>> {
     let status = child
         .wait()
         .with_context(|| format!("failed to wait for {cmd:?}"))?;
-    match status.code() {
-        Some(code) => match code {
-            0 => {}
-            code => bail!("{cmd:?} exited with status code {code}"),
-        },
-        None => bail!("{cmd:?} terminated by signal"),
+    if status.code() != Some(0) {
+        bail!("{cmd:?} failed: {status:?}")
     }
-
     Ok(executables)
 }
 
@@ -98,7 +93,7 @@ pub fn run(opts: Options) -> Result<()> {
 
     let binaries = build(build_options).context("error while building userspace application")?;
     let mut args = runner.trim().split_terminator(' ');
-    let runner = args.next().ok_or(anyhow::anyhow!("no first argument"))?;
+    let runner = args.next().ok_or(anyhow!("no first argument"))?;
     let args = args.collect::<Vec<_>>();
 
     let mut failures = String::new();
@@ -110,24 +105,18 @@ pub fn run(opts: Options) -> Result<()> {
             .args(run_args.iter())
             .arg("--test-threads=1");
 
-        println!("{} running {cmd:?}", name);
+        println!("{name} running {cmd:?}");
 
         let status = cmd
             .status()
             .with_context(|| format!("failed to run {cmd:?}"))?;
-        match status.code() {
-            Some(code) => match code {
-                0 => {}
-                code => writeln!(&mut failures, "{} exited with status code {code}", name)
-                    .context("String write failed")?,
-            },
-            None => writeln!(&mut failures, "{} terminated by signal", name)
-                .context("String write failed")?,
+        if status.code() != Some(0) {
+            writeln!(&mut failures, "{name} failed: {status:?}").context("String write failed")?
         }
     }
     if failures.is_empty() {
         Ok(())
     } else {
-        Err(anyhow::anyhow!("failures:\n{}", failures))
+        Err(anyhow!("failures:\n{}", failures))
     }
 }