build_ebpf.rs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. use std::{
  2. borrow::Cow,
  3. env,
  4. ffi::{OsStr, OsString},
  5. fs,
  6. path::{Path, PathBuf},
  7. process::Command,
  8. };
  9. use anyhow::{bail, Context};
  10. use clap::Parser;
  11. use crate::utils::workspace_root;
  12. #[derive(Debug, Copy, Clone)]
  13. pub enum Architecture {
  14. BpfEl,
  15. BpfEb,
  16. }
  17. impl std::str::FromStr for Architecture {
  18. type Err = String;
  19. fn from_str(s: &str) -> Result<Self, Self::Err> {
  20. Ok(match s {
  21. "bpfel-unknown-none" => Architecture::BpfEl,
  22. "bpfeb-unknown-none" => Architecture::BpfEb,
  23. _ => return Err("invalid target".to_owned()),
  24. })
  25. }
  26. }
  27. impl std::fmt::Display for Architecture {
  28. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  29. f.write_str(match self {
  30. Architecture::BpfEl => "bpfel-unknown-none",
  31. Architecture::BpfEb => "bpfeb-unknown-none",
  32. })
  33. }
  34. }
  35. #[derive(Debug, Parser)]
  36. pub struct BuildEbpfOptions {
  37. /// Set the endianness of the BPF target
  38. #[clap(default_value = "bpfel-unknown-none", long)]
  39. pub target: Architecture,
  40. /// Libbpf dir, required for compiling C code
  41. #[clap(long, action)]
  42. pub libbpf_dir: PathBuf,
  43. }
  44. pub fn build_ebpf(opts: BuildEbpfOptions) -> anyhow::Result<()> {
  45. build_rust_ebpf(&opts)?;
  46. build_c_ebpf(&opts)
  47. }
  48. fn build_rust_ebpf(opts: &BuildEbpfOptions) -> anyhow::Result<()> {
  49. let mut dir = PathBuf::from(workspace_root());
  50. dir.push("test/integration-ebpf");
  51. let target = format!("--target={}", opts.target);
  52. let args = vec![
  53. "+nightly",
  54. "build",
  55. "--release",
  56. "--verbose",
  57. target.as_str(),
  58. "-Z",
  59. "build-std=core",
  60. ];
  61. let status = Command::new("cargo")
  62. .current_dir(&dir)
  63. .args(&args)
  64. .status()
  65. .expect("failed to build bpf program");
  66. assert!(status.success());
  67. Ok(())
  68. }
  69. fn get_libbpf_headers<P: AsRef<Path>>(libbpf_dir: P, include_path: P) -> anyhow::Result<()> {
  70. let dir = include_path.as_ref();
  71. fs::create_dir_all(dir)?;
  72. let mut includedir = OsString::new();
  73. includedir.push("INCLUDEDIR=");
  74. includedir.push(dir.as_os_str());
  75. let status = Command::new("make")
  76. .current_dir(libbpf_dir.as_ref().join("src"))
  77. .arg(includedir)
  78. .arg("install_headers")
  79. .status()
  80. .expect("failed to build get libbpf headers");
  81. assert!(status.success());
  82. Ok(())
  83. }
  84. fn build_c_ebpf(opts: &BuildEbpfOptions) -> anyhow::Result<()> {
  85. let mut src = PathBuf::from(workspace_root());
  86. src.push("test/integration-ebpf/src/bpf");
  87. let mut out_path = PathBuf::from(workspace_root());
  88. out_path.push("target");
  89. out_path.push(opts.target.to_string());
  90. out_path.push("release");
  91. let include_path = out_path.join("include");
  92. get_libbpf_headers(&opts.libbpf_dir, &include_path)?;
  93. let files = fs::read_dir(&src).unwrap();
  94. for file in files {
  95. let p = file.unwrap().path();
  96. if let Some(ext) = p.extension() {
  97. if ext == "c" {
  98. let mut out = PathBuf::from(&out_path);
  99. out.push(p.file_name().unwrap());
  100. out.set_extension("o");
  101. compile_with_clang(&p, &out, &include_path)?;
  102. }
  103. }
  104. }
  105. Ok(())
  106. }
  107. /// Build eBPF programs with clang and libbpf headers.
  108. fn compile_with_clang<P: Clone + AsRef<Path>>(
  109. src: P,
  110. out: P,
  111. include_path: P,
  112. ) -> anyhow::Result<()> {
  113. let clang: Cow<'_, _> = match env::var_os("CLANG") {
  114. Some(val) => val.into(),
  115. None => OsStr::new("/usr/bin/clang").into(),
  116. };
  117. let arch = match env::consts::ARCH {
  118. "x86_64" => "x86",
  119. "aarch64" => "arm64",
  120. arch => arch,
  121. };
  122. let mut cmd = Command::new(clang);
  123. cmd.arg("-I")
  124. .arg(include_path.as_ref())
  125. .arg("-g")
  126. .arg("-O2")
  127. .arg("-target")
  128. .arg("bpf")
  129. .arg("-c")
  130. .arg(format!("-D__TARGET_ARCH_{arch}"))
  131. .arg(src.as_ref().as_os_str())
  132. .arg("-o")
  133. .arg(out.as_ref().as_os_str());
  134. let output = cmd.output().context("Failed to execute clang")?;
  135. if !output.status.success() {
  136. bail!(
  137. "Failed to compile eBPF programs\n \
  138. stdout=\n \
  139. {}\n \
  140. stderr=\n \
  141. {}\n",
  142. String::from_utf8(output.stdout).unwrap(),
  143. String::from_utf8(output.stderr).unwrap()
  144. );
  145. }
  146. Ok(())
  147. }