mirror of https://github.com/aya-rs/aya
Merge pull request #644 from aya-rs/build-script
Steps toward hermetic integration testsreviewable/pr629/r18
commit
7def6d7218
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "libbpf"]
|
||||||
|
path = libbpf
|
||||||
|
url = https://github.com/libbpf/libbpf
|
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"rust-analyzer.checkOnSave.allTargets": false,
|
"rust-analyzer.check.allTargets": true,
|
||||||
"rust-analyzer.checkOnSave.command": "clippy"
|
"rust-analyzer.check.command": "clippy"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
[build]
|
|
||||||
target-dir = "../target"
|
|
||||||
target = "bpfel-unknown-none"
|
|
||||||
|
|
||||||
[unstable]
|
|
||||||
build-std = ["core"]
|
|
@ -0,0 +1 @@
|
|||||||
|
Subproject commit a2258003f21d9d52afd48aa64787b65ef80bd355
|
@ -1,6 +0,0 @@
|
|||||||
[build]
|
|
||||||
target-dir = "../../target"
|
|
||||||
target = "bpfel-unknown-none"
|
|
||||||
|
|
||||||
[unstable]
|
|
||||||
build-std = ["core"]
|
|
@ -1,2 +0,0 @@
|
|||||||
[toolchain]
|
|
||||||
channel="nightly"
|
|
@ -1 +0,0 @@
|
|||||||
../../rustfmt.toml
|
|
@ -0,0 +1,196 @@
|
|||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
ffi::OsString,
|
||||||
|
fmt::Write as _,
|
||||||
|
fs,
|
||||||
|
io::BufReader,
|
||||||
|
path::PathBuf,
|
||||||
|
process::{Child, Command, Stdio},
|
||||||
|
};
|
||||||
|
|
||||||
|
use cargo_metadata::{
|
||||||
|
Artifact, CompilerMessage, Message, Metadata, MetadataCommand, Package, Target,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
const AYA_BUILD_INTEGRATION_BPF: &str = "AYA_BUILD_INTEGRATION_BPF";
|
||||||
|
|
||||||
|
println!("cargo:rerun-if-env-changed={}", AYA_BUILD_INTEGRATION_BPF);
|
||||||
|
|
||||||
|
let build_integration_bpf = match env::var_os(AYA_BUILD_INTEGRATION_BPF) {
|
||||||
|
None => false,
|
||||||
|
Some(s) => {
|
||||||
|
let s = s.to_str().unwrap();
|
||||||
|
s.parse::<bool>().unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let manifest_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap();
|
||||||
|
let manifest_dir = PathBuf::from(manifest_dir);
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
let out_dir = PathBuf::from(out_dir);
|
||||||
|
|
||||||
|
let endian = env::var_os("CARGO_CFG_TARGET_ENDIAN").unwrap();
|
||||||
|
let target = if endian == "big" {
|
||||||
|
"bpfeb"
|
||||||
|
} else if endian == "little" {
|
||||||
|
"bpfel"
|
||||||
|
} else {
|
||||||
|
panic!("unsupported endian={:?}", endian)
|
||||||
|
};
|
||||||
|
|
||||||
|
const C_BPF_PROBES: &[(&str, &str)] = &[
|
||||||
|
("ext.bpf.c", "ext.bpf.o"),
|
||||||
|
("main.bpf.c", "main.bpf.o"),
|
||||||
|
("multimap-btf.bpf.c", "multimap-btf.bpf.o"),
|
||||||
|
("text_64_64_reloc.c", "text_64_64_reloc.o"),
|
||||||
|
];
|
||||||
|
|
||||||
|
let c_bpf_probes = C_BPF_PROBES
|
||||||
|
.iter()
|
||||||
|
.map(|(src, dst)| (src, out_dir.join(dst)));
|
||||||
|
|
||||||
|
if build_integration_bpf {
|
||||||
|
let libbpf_dir = manifest_dir
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.parent()
|
||||||
|
.unwrap()
|
||||||
|
.join("libbpf");
|
||||||
|
|
||||||
|
let libbpf_headers_dir = out_dir.join("libbpf_headers");
|
||||||
|
|
||||||
|
let mut includedir = OsString::new();
|
||||||
|
includedir.push("INCLUDEDIR=");
|
||||||
|
includedir.push(&libbpf_headers_dir);
|
||||||
|
|
||||||
|
let mut cmd = Command::new("make");
|
||||||
|
cmd.arg("-C")
|
||||||
|
.arg(libbpf_dir.join("src"))
|
||||||
|
.arg(includedir)
|
||||||
|
.arg("install_headers");
|
||||||
|
let status = cmd
|
||||||
|
.status()
|
||||||
|
.unwrap_or_else(|err| panic!("failed to run {cmd:?}: {err}"));
|
||||||
|
match status.code() {
|
||||||
|
Some(code) => match code {
|
||||||
|
0 => {}
|
||||||
|
code => panic!("{cmd:?} exited with code {code}"),
|
||||||
|
},
|
||||||
|
None => panic!("{cmd:?} terminated by signal"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let bpf_dir = manifest_dir.join("bpf");
|
||||||
|
|
||||||
|
let mut target_arch = OsString::new();
|
||||||
|
target_arch.push("-D__TARGET_ARCH_");
|
||||||
|
|
||||||
|
let arch = env::var_os("CARGO_CFG_TARGET_ARCH").unwrap();
|
||||||
|
if arch == "x86_64" {
|
||||||
|
target_arch.push("x86");
|
||||||
|
} else if arch == "aarch64" {
|
||||||
|
target_arch.push("arm64");
|
||||||
|
} else {
|
||||||
|
target_arch.push(arch);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (src, dst) in c_bpf_probes {
|
||||||
|
let src = bpf_dir.join(src);
|
||||||
|
let mut cmd = Command::new("clang");
|
||||||
|
cmd.arg("-I")
|
||||||
|
.arg(&libbpf_headers_dir)
|
||||||
|
.args(["-g", "-O2", "-target", target, "-c"])
|
||||||
|
.arg(&target_arch)
|
||||||
|
.arg(src)
|
||||||
|
.arg("-o")
|
||||||
|
.arg(dst);
|
||||||
|
let status = cmd
|
||||||
|
.status()
|
||||||
|
.unwrap_or_else(|err| panic!("failed to run {cmd:?}: {err}"));
|
||||||
|
match status.code() {
|
||||||
|
Some(code) => match code {
|
||||||
|
0 => {}
|
||||||
|
code => panic!("{cmd:?} exited with code {code}"),
|
||||||
|
},
|
||||||
|
None => panic!("{cmd:?} terminated by signal"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let ebpf_dir = manifest_dir.parent().unwrap().join("integration-ebpf");
|
||||||
|
let target = format!("{target}-unknown-none");
|
||||||
|
|
||||||
|
let mut cmd = Command::new("cargo");
|
||||||
|
cmd.current_dir(&ebpf_dir).args([
|
||||||
|
"build",
|
||||||
|
"-Z",
|
||||||
|
"build-std=core",
|
||||||
|
"--release",
|
||||||
|
"--message-format=json",
|
||||||
|
"--target",
|
||||||
|
&target,
|
||||||
|
]);
|
||||||
|
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 reader = BufReader::new(stdout);
|
||||||
|
let mut executables = Vec::new();
|
||||||
|
let mut compiler_messages = String::new();
|
||||||
|
for message in Message::parse_stream(reader) {
|
||||||
|
#[allow(clippy::collapsible_match)]
|
||||||
|
match message.expect("valid JSON") {
|
||||||
|
Message::CompilerArtifact(Artifact {
|
||||||
|
executable,
|
||||||
|
target: Target { name, .. },
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
if let Some(executable) = executable {
|
||||||
|
executables.push((name, executable.into_std_path_buf()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message::CompilerMessage(CompilerMessage { message, .. }) => {
|
||||||
|
writeln!(&mut compiler_messages, "{message}").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}:\n{compiler_messages}"),
|
||||||
|
},
|
||||||
|
None => panic!("{cmd:?} terminated by signal"),
|
||||||
|
}
|
||||||
|
|
||||||
|
for (name, binary) in executables {
|
||||||
|
let dst = out_dir.join(name);
|
||||||
|
let _: u64 = fs::copy(&binary, &dst)
|
||||||
|
.unwrap_or_else(|err| panic!("failed to copy {binary:?} to {dst:?}: {err}"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (_src, dst) in c_bpf_probes {
|
||||||
|
fs::write(&dst, []).unwrap_or_else(|err| panic!("failed to create {dst:?}: {err}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let Metadata { packages, .. } = MetadataCommand::new().no_deps().exec().unwrap();
|
||||||
|
for Package { name, targets, .. } in packages {
|
||||||
|
if name != "integration-ebpf" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for Target { name, kind, .. } in targets {
|
||||||
|
if kind != ["bin"] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let dst = out_dir.join(name);
|
||||||
|
fs::write(&dst, []).unwrap_or_else(|err| panic!("failed to create {dst:?}: {err}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1 +1,20 @@
|
|||||||
|
use aya::include_bytes_aligned;
|
||||||
|
|
||||||
|
pub const EXT: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/ext.bpf.o"));
|
||||||
|
pub const MAIN: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/main.bpf.o"));
|
||||||
|
pub const MULTIMAP_BTF: &[u8] =
|
||||||
|
include_bytes_aligned!(concat!(env!("OUT_DIR"), "/multimap-btf.bpf.o"));
|
||||||
|
pub const TEXT_64_64_RELOC: &[u8] =
|
||||||
|
include_bytes_aligned!(concat!(env!("OUT_DIR"), "/text_64_64_reloc.o"));
|
||||||
|
|
||||||
|
pub const LOG: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/log"));
|
||||||
|
pub const MAP_TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/map_test"));
|
||||||
|
pub const NAME_TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/name_test"));
|
||||||
|
pub const PASS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/pass"));
|
||||||
|
pub const TEST: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/test"));
|
||||||
|
pub const RELOCATIONS: &[u8] = include_bytes_aligned!(concat!(env!("OUT_DIR"), "/relocations"));
|
||||||
|
pub const BPF_PROBE_READ: &[u8] =
|
||||||
|
include_bytes_aligned!(concat!(env!("OUT_DIR"), "/bpf_probe_read"));
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests;
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
mod bpf_probe_read;
|
||||||
|
mod btf_relocations;
|
||||||
|
mod elf;
|
||||||
|
mod load;
|
||||||
|
mod log;
|
||||||
|
mod rbpf;
|
||||||
|
mod relocations;
|
||||||
|
mod smoke;
|
@ -1,10 +1,8 @@
|
|||||||
use aya::include_bytes_aligned;
|
|
||||||
use object::{Object, ObjectSymbol};
|
use object::{Object, ObjectSymbol};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_maps() {
|
fn test_maps() {
|
||||||
let bytes = include_bytes_aligned!("../../../target/bpfel-unknown-none/release/map_test");
|
let obj_file = object::File::parse(crate::MAP_TEST).unwrap();
|
||||||
let obj_file = object::File::parse(bytes).unwrap();
|
|
||||||
if obj_file.section_by_name("maps").is_none() {
|
if obj_file.section_by_name("maps").is_none() {
|
||||||
panic!("No 'maps' ELF section");
|
panic!("No 'maps' ELF section");
|
||||||
}
|
}
|
@ -1,138 +0,0 @@
|
|||||||
use std::{
|
|
||||||
borrow::Cow,
|
|
||||||
env,
|
|
||||||
ffi::{OsStr, OsString},
|
|
||||||
fs,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
process::Command,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use clap::Parser;
|
|
||||||
|
|
||||||
use crate::utils::{exec, workspace_root};
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum Architecture {
|
|
||||||
BpfEl,
|
|
||||||
BpfEb,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::str::FromStr for Architecture {
|
|
||||||
type Err = &'static str;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
Ok(match s {
|
|
||||||
"bpfel-unknown-none" => Architecture::BpfEl,
|
|
||||||
"bpfeb-unknown-none" => Architecture::BpfEb,
|
|
||||||
_ => return Err("invalid target"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for Architecture {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.write_str(match self {
|
|
||||||
Architecture::BpfEl => "bpfel-unknown-none",
|
|
||||||
Architecture::BpfEb => "bpfeb-unknown-none",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
|
||||||
pub struct BuildEbpfOptions {
|
|
||||||
/// Set the endianness of the BPF target
|
|
||||||
#[clap(default_value = "bpfel-unknown-none", long)]
|
|
||||||
pub target: Architecture,
|
|
||||||
/// Libbpf dir, required for compiling C code
|
|
||||||
#[clap(long, action)]
|
|
||||||
pub libbpf_dir: PathBuf,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_ebpf(opts: BuildEbpfOptions) -> Result<()> {
|
|
||||||
build_rust_ebpf(&opts)?;
|
|
||||||
build_c_ebpf(&opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_rust_ebpf(opts: &BuildEbpfOptions) -> Result<()> {
|
|
||||||
let BuildEbpfOptions {
|
|
||||||
target,
|
|
||||||
libbpf_dir: _,
|
|
||||||
} = opts;
|
|
||||||
|
|
||||||
let mut dir = PathBuf::from(workspace_root());
|
|
||||||
dir.push("test/integration-ebpf");
|
|
||||||
|
|
||||||
exec(
|
|
||||||
Command::new("cargo")
|
|
||||||
.current_dir(&dir)
|
|
||||||
.args(["+nightly", "build", "--release", "--target"])
|
|
||||||
.arg(target.to_string())
|
|
||||||
.args(["-Z", "build-std=core"])
|
|
||||||
.current_dir(&dir),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_libbpf_headers(libbpf_dir: &Path, include_path: &Path) -> Result<()> {
|
|
||||||
fs::create_dir_all(include_path)?;
|
|
||||||
let mut includedir = OsString::new();
|
|
||||||
includedir.push("INCLUDEDIR=");
|
|
||||||
includedir.push(include_path);
|
|
||||||
exec(
|
|
||||||
Command::new("make")
|
|
||||||
.current_dir(libbpf_dir.join("src"))
|
|
||||||
.arg(includedir)
|
|
||||||
.arg("install_headers"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_c_ebpf(opts: &BuildEbpfOptions) -> Result<()> {
|
|
||||||
let BuildEbpfOptions { target, libbpf_dir } = opts;
|
|
||||||
|
|
||||||
let mut src = PathBuf::from(workspace_root());
|
|
||||||
src.push("test/integration-ebpf/src/bpf");
|
|
||||||
|
|
||||||
let mut out_path = PathBuf::from(workspace_root());
|
|
||||||
out_path.push("target");
|
|
||||||
out_path.push(target.to_string());
|
|
||||||
out_path.push("release");
|
|
||||||
|
|
||||||
let include_path = out_path.join("include");
|
|
||||||
get_libbpf_headers(libbpf_dir, &include_path)?;
|
|
||||||
let files = fs::read_dir(&src).unwrap();
|
|
||||||
for file in files {
|
|
||||||
let p = file.unwrap().path();
|
|
||||||
if let Some(ext) = p.extension() {
|
|
||||||
if ext == "c" {
|
|
||||||
let mut out = PathBuf::from(&out_path);
|
|
||||||
out.push(p.file_name().unwrap());
|
|
||||||
out.set_extension("o");
|
|
||||||
compile_with_clang(&p, &out, &include_path)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build eBPF programs with clang and libbpf headers.
|
|
||||||
fn compile_with_clang(src: &Path, out: &Path, include_path: &Path) -> Result<()> {
|
|
||||||
let clang: Cow<'_, _> = match env::var_os("CLANG") {
|
|
||||||
Some(val) => val.into(),
|
|
||||||
None => OsStr::new("/usr/bin/clang").into(),
|
|
||||||
};
|
|
||||||
let arch = match env::consts::ARCH {
|
|
||||||
"x86_64" => "x86",
|
|
||||||
"aarch64" => "arm64",
|
|
||||||
arch => arch,
|
|
||||||
};
|
|
||||||
exec(
|
|
||||||
Command::new(clang)
|
|
||||||
.arg("-I")
|
|
||||||
.arg(include_path)
|
|
||||||
.args(["-g", "-O2", "-target", "bpf", "-c"])
|
|
||||||
.arg(format!("-D__TARGET_ARCH_{arch}"))
|
|
||||||
.arg(src)
|
|
||||||
.arg("-o")
|
|
||||||
.arg(out),
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
use std::{cell::OnceCell, process::Command};
|
|
||||||
|
|
||||||
use anyhow::{bail, Context as _, Result};
|
|
||||||
|
|
||||||
pub fn workspace_root() -> &'static str {
|
|
||||||
static mut WORKSPACE_ROOT: OnceCell<String> = OnceCell::new();
|
|
||||||
unsafe { &mut WORKSPACE_ROOT }.get_or_init(|| {
|
|
||||||
let cmd = cargo_metadata::MetadataCommand::new();
|
|
||||||
cmd.exec().unwrap().workspace_root.to_string()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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 => bail!("{cmd:?} exited with code {code}"),
|
|
||||||
},
|
|
||||||
None => bail!("{cmd:?} terminated by signal"),
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue