4
0

build_ebpf.rs 4.4 KB

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