1use std::{collections::HashMap, env, process::Command};
2
3use anyhow::{Context, Result, anyhow, bail};
4use sscanf::sscanf;
5
6lazy_static::lazy_static! {
7 static ref ARCH_MAP: HashMap<&'static str, &'static str> = vec![
10 ("x86", "x86"),
11 ("x86_64", "x86"),
12 ("s390", "s390"),
13 ("s390x", "s390"),
14 ("arm", "arm"),
15 ("aarch64", "arm64"),
16 ("mips", "mips"),
17 ("mips64", "mips"),
18 ("ppc32", "powerpc"),
19 ("ppc64", "powerpc"),
20 ("ppc64le", "powerpc"),
21 ("powerpc64le", "powerpc"),
22 ("sparc", "sparc"),
23 ("sparcv9", "sparc"),
24 ("riscv32", "riscv"),
25 ("riscv64", "riscv"),
26 ("riscv64gc", "riscv"),
27 ("arc", "arc"), ("loongarch64", "loongarch"), ].into_iter().collect();
30}
31#[derive(Debug)]
32#[allow(dead_code)]
33pub struct ClangInfo {
34 pub clang: String,
35 pub ver: String,
36 pub arch: String,
37}
38
39impl ClangInfo {
40 pub fn new() -> Result<ClangInfo> {
41 let mut clang_args = vec!["--version".to_string()];
42
43 if let Ok(target) = env::var("TARGET") {
44 clang_args.push(format!("--target={target}"));
45 }
46
47 let clang = env::var("BPF_CLANG").unwrap_or("clang".into());
48 let output = Command::new(&clang)
49 .args(clang_args)
50 .output()
51 .with_context(|| format!("Failed to run \"{} --version\"", clang))?;
52
53 let stdout = String::from_utf8(output.stdout)?;
54 let (mut ver, mut arch) = (None, None);
55 for line in stdout.lines() {
56 if let Some(v) = sscanf!(
57 Self::skip_clang_version_prefix(line),
58 "clang version {String}"
59 ) {
60 ver = Some(v.split_whitespace().next().unwrap().to_string());
63 continue;
64 }
65 if let Some(v) = sscanf!(line, "Target: {String}") {
66 arch = Some(v.split('-').next().unwrap().to_string());
67 continue;
68 }
69 }
70
71 let (ver, arch) = (
72 ver.ok_or(anyhow!("Failed to read clang version"))?,
73 arch.ok_or(anyhow!("Failed to read clang target arch"))?,
74 );
75
76 if version_compare::compare(&ver, "18") == Ok(version_compare::Cmp::Lt) {
77 bail!(
78 "clang < 18 does not support -mcpu=v4 for BPF ({:?} ver={:?})",
79 clang,
80 ver
81 );
82 }
83
84 Ok(ClangInfo { clang, ver, arch })
85 }
86
87 fn skip_clang_version_prefix(line: &str) -> &str {
88 if let Some(index) = line.find("clang version") {
89 &line[index..]
90 } else {
91 line
92 }
93 }
94
95 pub fn kernel_target(&self) -> Result<String> {
96 match ARCH_MAP.get(self.arch.as_str()) {
98 Some(v) => Ok(v.to_string()),
99 None => Err(anyhow!("CPU arch {} not found in ARCH_MAP", self.arch)),
100 }
101 }
102
103 #[allow(dead_code)] pub fn determine_base_cflags(&self) -> Result<Vec<String>> {
105 let kernel_target = self.kernel_target()?;
106
107 let output = Command::new(&self.clang)
109 .args(["-v", "-E", "-"])
110 .output()
111 .with_context(|| format!("Failed to run \"{} -v -E - < /dev/null", self.clang))?;
112 let stderr = String::from_utf8(output.stderr)?;
113
114 let mut sys_incls = None;
115 for line in stderr.lines() {
116 if line == "#include <...> search starts here:" {
117 sys_incls = Some(vec![]);
118 continue;
119 }
120 if sys_incls.is_none() {
121 continue;
122 }
123 if line == "End of search list." {
124 break;
125 }
126
127 sys_incls.as_mut().unwrap().push(line.trim());
128 }
129 let sys_incls = match sys_incls {
130 Some(v) => v,
131 None => bail!("Failed to find system includes from {:?}", self.clang),
132 };
133
134 let output = Command::new(&self.clang)
136 .args(["-dM", "-E", "-"])
137 .output()
138 .with_context(|| format!("Failed to run \"{} -dM E - < /dev/null", self.clang))?;
139 let stdout = String::from_utf8(output.stdout)?;
140
141 let mut endian = None;
142 for line in stdout.lines() {
143 if let Some(v) = sscanf!(line, "#define __BYTE_ORDER__ {str}") {
144 endian = Some(match v {
145 "__ORDER_LITTLE_ENDIAN__" => "little",
146 "__ORDER_BIG_ENDIAN__" => "big",
147 v => bail!("Unknown __BYTE_ORDER__ {:?}", v),
148 });
149 break;
150 }
151 }
152 let endian = match endian {
153 Some(v) => v,
154 None => bail!("Failed to find __BYTE_ORDER__ from {:?}", self.clang),
155 };
156
157 let mut cflags: Vec<String> = ["-g", "-O2", "-Wall", "-Wno-compare-distinct-pointer-types"]
159 .into_iter()
160 .map(|x| x.into())
161 .collect();
162 cflags.push(format!("-D__TARGET_ARCH_{}", kernel_target));
163 cflags.push("-mcpu=v4".into());
164 cflags.extend(["-mllvm".into(), "-disable-ldsx".into()]);
171 if matches!(
175 version_compare::compare(&self.ver, "22"),
176 Ok(version_compare::Cmp::Eq | version_compare::Cmp::Gt)
177 ) {
178 cflags.extend(["-mllvm".into(), "-disable-gotox".into()]);
179 }
180 cflags.push(format!("-m{endian}-endian"));
181 cflags.append(
182 &mut sys_incls
183 .into_iter()
184 .flat_map(|x| ["-idirafter".into(), x.into()])
185 .collect(),
186 );
187 Ok(cflags)
188 }
189}