Make relocations tests actually pass

pull/467/head
Matteo Nardi 2 years ago
parent 702f77b565
commit 27f22f205d

@ -1,5 +1,6 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use std::{process::Command, thread::sleep, time::Duration}; use std::{path::PathBuf, process::Command, thread::sleep, time::Duration};
use tempfile::TempDir;
use aya::{maps::Array, programs::TracePoint, BpfLoader, Btf, Endianness}; use aya::{maps::Array, programs::TracePoint, BpfLoader, Btf, Endianness};
@ -51,7 +52,7 @@ fn relocate_enum() {
.build() .build()
.unwrap(); .unwrap();
assert_eq!(test.run().unwrap(), 4); assert_eq!(test.run().unwrap(), 4);
assert_eq!(test.run_no_btf().unwrap(), 0); assert_eq!(test.run_no_btf().unwrap(), 1);
} }
#[integration_test] #[integration_test]
@ -61,16 +62,20 @@ fn relocate_pointer() {
struct foo {}; struct foo {};
struct bar { struct foo *f; }; struct bar { struct foo *f; };
"#, "#,
target_btf: r#""#, target_btf: r#"
struct foo {};
struct bar { struct foo *f; };
"#,
relocation_code: r#" relocation_code: r#"
__u8 memory[] = {0, 0, 0, 0, 0, 0, 0, 42}; __u8 memory[] = {42, 0, 0, 0, 0, 0, 0, 0};
struct foo *f = BPF_CORE_READ((struct bar *)&memory, f); struct foo *f = BPF_CORE_READ((struct bar *)&memory, f);
__u32 value = (f == (struct foo *) 42); __u32 value = ((__u64) f);
"#, "#,
} }
.build() .build()
.unwrap(); .unwrap();
assert_eq!(test.run().unwrap(), 1); assert_eq!(test.run().unwrap(), 42);
assert_eq!(test.run_no_btf().unwrap(), 42);
} }
/// Utility code for running relocation tests: /// Utility code for running relocation tests:
@ -105,57 +110,37 @@ impl RelocationTest {
fn build_ebpf(&self) -> Result<Vec<u8>> { fn build_ebpf(&self) -> Result<Vec<u8>> {
let local_definition = self.local_definition; let local_definition = self.local_definition;
let relocation_code = self.relocation_code; let relocation_code = self.relocation_code;
let tmp_dir = tempfile::tempdir().context("Error making temp dir")?; let (_tmp_dir, compiled_file) = compile(&format!(
let ebpf_file = tmp_dir.path().join("ebpf_program.c"); r#"
std::fs::write( #include <linux/bpf.h>
&ebpf_file,
format!( #include <bpf/bpf_core_read.h>
r#" #include <bpf/bpf_helpers.h>
#include <linux/bpf.h> #include <bpf/bpf_tracing.h>
#include <bpf/bpf_core_read.h> {local_definition}
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h> struct {{
__uint(type, BPF_MAP_TYPE_ARRAY);
{local_definition} __type(key, __u32);
__type(value, __u32);
struct {{ __uint(max_entries, 1);
__uint(type, BPF_MAP_TYPE_ARRAY); }} output_map SEC(".maps");
__type(key, __u32);
__type(value, __u32); SEC("tracepoint/bpf_prog") int bpf_prog(void *ctx) {{
__uint(max_entries, 1); __u32 key = 0;
}} output_map SEC(".maps"); {relocation_code}
bpf_map_update_elem(&output_map, &key, &value, BPF_ANY);
SEC("tracepoint/bpf_prog") int bpf_prog(void *ctx) {{ return 0;
__u32 key = 0; }}
{relocation_code}
bpf_map_update_elem(&output_map, &key, &value, BPF_ANY); char _license[] SEC("license") = "GPL";
return 0; "#
}} ))
.context("Failed to compile eBPF program")?;
char _license[] SEC("license") = "GPL"; let bytecode =
"# std::fs::read(compiled_file).context("Error reading compiled eBPF program")?;
), Ok(bytecode)
)
.context("Writing bpf program failed")?;
Command::new("clang")
.current_dir(tmp_dir.path())
.args(["-c", "-g", "-O2", "-target", "bpf"])
// NOTE: these tests depend on libbpf, LIBBPF_INCLUDE must point its headers.
// This is set automatically by the integration-test xtask.
.args([
"-I",
&std::env::var("LIBBPF_INCLUDE").context("LIBBPF_INCLUDE not set")?,
])
.arg(&ebpf_file)
.status()
.context("Failed to run clang")?
.success()
.then_some(())
.context("Failed to compile eBPF program")?;
let ebpf = std::fs::read(ebpf_file.with_extension("o"))
.context("Error reading compiled eBPF program")?;
Ok(ebpf)
} }
/// - Generate the target BTF source with a mock main() /// - Generate the target BTF source with a mock main()
@ -163,36 +148,33 @@ impl RelocationTest {
/// - Extract the BTF with pahole /// - Extract the BTF with pahole
fn build_btf(&self) -> Result<Btf> { fn build_btf(&self) -> Result<Btf> {
let target_btf = self.target_btf; let target_btf = self.target_btf;
let tmp_dir = tempfile::tempdir().context("Error making temp dir")?; let relocation_code = self.relocation_code;
// BTF files can be generated and inspected with these commands: // BTF files can be generated and inspected with these commands:
// $ clang -c -g -O2 -target bpf target.c // $ clang -c -g -O2 -target bpf target.c
// $ pahole --btf_encode_detached=target.btf -V target.o // $ pahole --btf_encode_detached=target.btf -V target.o
// $ bpftool btf dump file ./target.btf format c // $ bpftool btf dump file ./target.btf format c
let btf_file = tmp_dir.path().join("target_btf.c"); let (tmp_dir, compiled_file) = compile(&format!(
std::fs::write( r#"
&btf_file, #include <linux/bpf.h>
format!(
r#" #include <bpf/bpf_core_read.h>
#include <linux/types.h> #include <bpf/bpf_helpers.h>
{target_btf} #include <bpf/bpf_tracing.h>
int main() {{ return 0; }}
"# {target_btf}
), int main() {{
) // This is needed to make sure to emit BTF for the defined types,
.context("Writing bpf program failed")?; // it could be dead code eliminated if we don't.
Command::new("clang") {relocation_code};
.current_dir(tmp_dir.path()) return value;
.args(["-c", "-g", "-O2", "-target", "bpf"]) }}
.arg(&btf_file) "#
.status() ))
.context("Failed to run clang")? .context("Failed to compile BTF")?;
.success()
.then_some(())
.context("Failed to compile BTF")?;
Command::new("pahole") Command::new("pahole")
.current_dir(tmp_dir.path()) .current_dir(tmp_dir.path())
.arg("--btf_encode_detached=target.btf") .arg("--btf_encode_detached=target.btf")
.arg(btf_file.with_extension("o")) .arg(compiled_file)
.status() .status()
.context("Failed to run pahole")? .context("Failed to run pahole")?
.success() .success()
@ -204,6 +186,30 @@ impl RelocationTest {
} }
} }
/// 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")?;
Command::new("clang")
.current_dir(&tmp_dir)
.args(["-c", "-g", "-O2", "-target", "bpf"])
// NOTE: these tests depend on libbpf, LIBBPF_INCLUDE must point its headers.
// This is set automatically by the integration-test xtask.
.args([
"-I",
&std::env::var("LIBBPF_INCLUDE").context("LIBBPF_INCLUDE not set")?,
])
.arg(&source)
.status()
.context("Failed to run clang")?
.success()
.then_some(())
.context("Failed to compile eBPF source")?;
Ok((tmp_dir, source.with_extension("o")))
}
struct RelocationTestRunner { struct RelocationTestRunner {
ebpf: Vec<u8>, ebpf: Vec<u8>,
btf: Btf, btf: Btf,
@ -212,18 +218,21 @@ struct RelocationTestRunner {
impl RelocationTestRunner { impl RelocationTestRunner {
/// Run test and return the output value /// Run test and return the output value
fn run(&self) -> Result<u32> { fn run(&self) -> Result<u32> {
self.run_internal(true) self.run_internal(true).context("Error running with BTF")
} }
/// Run without loading btf /// Run without loading btf
fn run_no_btf(&self) -> Result<u32> { fn run_no_btf(&self) -> Result<u32> {
self.run_internal(false) self.run_internal(false)
.context("Error running without BTF")
} }
fn run_internal(&self, with_relocations: bool) -> Result<u32> { fn run_internal(&self, with_relocations: bool) -> Result<u32> {
let mut loader = BpfLoader::new(); let mut loader = BpfLoader::new();
if with_relocations { if with_relocations {
loader.btf(Some(&self.btf)); loader.btf(Some(&self.btf));
} else {
loader.btf(None);
} }
let mut bpf = loader.load(&self.ebpf).context("Loading eBPF failed")?; let mut bpf = loader.load(&self.ebpf).context("Loading eBPF failed")?;
let program: &mut TracePoint = bpf let program: &mut TracePoint = bpf

Loading…
Cancel
Save