From c76d5a99508ac18ac7e57590e5c3a5e8920d0cd9 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Tue, 11 Jul 2023 16:06:47 -0400 Subject: [PATCH] integration-test: compile without touching disk --- test/integration-test/Cargo.toml | 1 - .../integration-test/tests/btf_relocations.rs | 107 ++++++++++++------ 2 files changed, 71 insertions(+), 37 deletions(-) diff --git a/test/integration-test/Cargo.toml b/test/integration-test/Cargo.toml index c4b81fe9..b4d1dd10 100644 --- a/test/integration-test/Cargo.toml +++ b/test/integration-test/Cargo.toml @@ -18,7 +18,6 @@ object = { version = "0.31", default-features = false, features = [ "elf", ] } rbpf = "0.2.0" -tempfile = "3.3.0" tokio = { version = "1.24", default-features = false, features = ["time"] } [build-dependencies] diff --git a/test/integration-test/tests/btf_relocations.rs b/test/integration-test/tests/btf_relocations.rs index 37a138bf..75f36223 100644 --- a/test/integration-test/tests/btf_relocations.rs +++ b/test/integration-test/tests/btf_relocations.rs @@ -1,6 +1,9 @@ -use anyhow::{bail, Context as _, Result}; -use std::{path::PathBuf, process::Command, thread::sleep, time::Duration}; -use tempfile::TempDir; +use anyhow::{anyhow, bail, Context as _, Result}; +use std::{ + process::{Child, ChildStdout, Command, Stdio}, + thread::sleep, + time::Duration, +}; use aya::{maps::Array, programs::TracePoint, util::KernelVersion, BpfLoader, Btf, Endianness}; @@ -215,9 +218,15 @@ impl RelocationTest { /// - Generate the source eBPF filling a template /// - Compile it with clang fn build_ebpf(&self) -> Result> { - let local_definition = self.local_definition; - let relocation_code = self.relocation_code; - let (_tmp_dir, compiled_file) = compile(&format!( + use std::io::Read as _; + + let Self { + local_definition, + relocation_code, + .. + } = self; + + let mut stdout = compile(&format!( r#" #include @@ -250,23 +259,29 @@ impl RelocationTest { char _license[] __attribute__((section("license"), used)) = "GPL"; "# )) - .context("Failed to compile eBPF program")?; - let bytecode = - std::fs::read(compiled_file).context("Error reading compiled eBPF program")?; - Ok(bytecode) + .context("failed to compile eBPF program")?; + let mut output = Vec::new(); + stdout.read_to_end(&mut output)?; + Ok(output) } /// - Generate the target BTF source with a mock main() /// - Compile it with clang /// - Extract the BTF with llvm-objcopy fn build_btf(&self) -> Result { - let target_btf = self.target_btf; - let relocation_code = self.relocation_code; + use std::io::Read as _; + + let Self { + target_btf, + relocation_code, + .. + } = self; + // BTF files can be generated and inspected with these commands: // $ clang -c -g -O2 -target bpf target.c // $ pahole --btf_encode_detached=target.btf -V target.o // $ bpftool btf dump file ./target.btf format c - let (tmp_dir, compiled_file) = compile(&format!( + let stdout = compile(&format!( r#" #include @@ -280,14 +295,20 @@ impl RelocationTest { }} "# )) - .context("Failed to compile BTF")?; + .context("failed to compile BTF")?; + let mut cmd = Command::new("llvm-objcopy"); - cmd.current_dir(tmp_dir.path()) - .args(["--dump-section", ".BTF=target.btf"]) - .arg(compiled_file); - let status = cmd - .status() - .with_context(|| format!("Failed to run {cmd:?}"))?; + cmd.args(["--dump-section", ".BTF=-", "-"]) + .stdin(stdout) + .stdout(Stdio::piped()); + let mut child = cmd + .spawn() + .with_context(|| format!("failed to spawn {cmd:?}"))?; + let Child { stdout, .. } = &mut child; + let mut stdout = stdout.take().ok_or(anyhow!("failed to open stdout"))?; + let status = child + .wait() + .with_context(|| format!("failed to wait for {cmd:?}"))?; match status.code() { Some(code) => match code { 0 => {} @@ -295,25 +316,39 @@ impl RelocationTest { }, None => bail!("{cmd:?} terminated by signal"), } - let btf = Btf::parse_file(tmp_dir.path().join("target.btf"), Endianness::default()) - .context("Error parsing generated BTF")?; - Ok(btf) + + let mut output = Vec::new(); + stdout.read_to_end(&mut output)?; + + Btf::parse(output.as_slice(), Endianness::default()) + .context("failed to parse generated BTF") } } -/// Compile an eBPF program and return the path of the compiled object. -/// Also returns a TempDir handler, dropping it will clear the created dicretory. -fn compile(source_code: &str) -> Result<(TempDir, PathBuf)> { - let tmp_dir = tempfile::tempdir().context("Error making temp dir")?; - let source = tmp_dir.path().join("source.c"); - std::fs::write(&source, source_code).context("Writing bpf program failed")?; +/// Compile an eBPF program and return its bytes. +fn compile(source_code: &str) -> Result { + use std::io::Write as _; + let mut cmd = Command::new("clang"); - cmd.current_dir(&tmp_dir) - .args(["-c", "-g", "-O2", "-target", "bpf"]) - .arg(&source); - let status = cmd - .status() - .with_context(|| format!("Failed to run {cmd:?}"))?; + cmd.args([ + "-c", "-g", "-O2", "-target", "bpf", "-x", "c", "-", "-o", "-", + ]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()); + let mut child = cmd + .spawn() + .with_context(|| format!("failed to spawn {cmd:?}"))?; + let Child { stdin, stdout, .. } = &mut child; + { + let mut stdin = stdin.take().ok_or(anyhow!("failed to open stdin"))?; + stdin + .write_all(source_code.as_bytes()) + .context("failed to write to stdin")?; + } + let stdout = stdout.take().ok_or(anyhow!("failed to open stdout"))?; + let status = child + .wait() + .with_context(|| format!("failed to wait for {cmd:?}"))?; match status.code() { Some(code) => match code { 0 => {} @@ -321,7 +356,7 @@ fn compile(source_code: &str) -> Result<(TempDir, PathBuf)> { }, None => bail!("{cmd:?} terminated by signal"), } - Ok((tmp_dir, source.with_extension("o"))) + Ok(stdout) } struct RelocationTestRunner {