1use std::{collections::HashMap, env, process::Command};
2
3use anyhow::{anyhow, bail, Context, Result};
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 Ok(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 Ok(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, "16") == Ok(version_compare::Cmp::Lt) {
77 bail!(
78 "clang < 16 loses high 32 bits of 64 bit enums when compiling BPF ({:?} ver={:?})",
79 &clang,
80 &ver
81 );
82 }
83 if version_compare::compare(&ver, "17") == Ok(version_compare::Cmp::Lt) {
84 println!(
85 "cargo:warning=clang >= 17 recommended ({:?} ver={:?})",
86 &clang, &ver
87 );
88 }
89
90 Ok(ClangInfo { clang, ver, arch })
91 }
92
93 fn skip_clang_version_prefix(line: &str) -> &str {
94 if let Some(index) = line.find("clang version") {
95 &line[index..]
96 } else {
97 line
98 }
99 }
100
101 pub fn kernel_target(&self) -> Result<String> {
102 match ARCH_MAP.get(self.arch.as_str()) {
104 Some(v) => Ok(v.to_string()),
105 None => Err(anyhow!("CPU arch {} not found in ARCH_MAP", self.arch)),
106 }
107 }
108
109 #[allow(dead_code)] pub fn determine_base_cflags(&self) -> Result<Vec<String>> {
111 let kernel_target = self.kernel_target()?;
112
113 let output = Command::new(&self.clang)
115 .args(["-v", "-E", "-"])
116 .output()
117 .with_context(|| format!("Failed to run \"{} -v -E - < /dev/null", self.clang))?;
118 let stderr = String::from_utf8(output.stderr)?;
119
120 let mut sys_incls = None;
121 for line in stderr.lines() {
122 if line == "#include <...> search starts here:" {
123 sys_incls = Some(vec![]);
124 continue;
125 }
126 if sys_incls.is_none() {
127 continue;
128 }
129 if line == "End of search list." {
130 break;
131 }
132
133 sys_incls.as_mut().unwrap().push(line.trim());
134 }
135 let sys_incls = match sys_incls {
136 Some(v) => v,
137 None => bail!("Failed to find system includes from {:?}", self.clang),
138 };
139
140 let output = Command::new(&self.clang)
142 .args(["-dM", "-E", "-"])
143 .output()
144 .with_context(|| format!("Failed to run \"{} -dM E - < /dev/null", self.clang))?;
145 let stdout = String::from_utf8(output.stdout)?;
146
147 let mut endian = None;
148 for line in stdout.lines() {
149 if let Ok(v) = sscanf!(line, "#define __BYTE_ORDER__ {str}") {
150 endian = Some(match v {
151 "__ORDER_LITTLE_ENDIAN__" => "little",
152 "__ORDER_BIG_ENDIAN__" => "big",
153 v => bail!("Unknown __BYTE_ORDER__ {:?}", v),
154 });
155 break;
156 }
157 }
158 let endian = match endian {
159 Some(v) => v,
160 None => bail!("Failed to find __BYTE_ORDER__ from {:?}", self.clang),
161 };
162
163 let mut cflags: Vec<String> = ["-g", "-O2", "-Wall", "-Wno-compare-distinct-pointer-types"]
165 .into_iter()
166 .map(|x| x.into())
167 .collect();
168 cflags.push(format!("-D__TARGET_ARCH_{}", &kernel_target));
169 cflags.push("-mcpu=v3".into());
170 cflags.push(format!("-m{}-endian", endian));
171 cflags.append(
172 &mut sys_incls
173 .into_iter()
174 .flat_map(|x| ["-idirafter".into(), x.into()])
175 .collect(),
176 );
177 Ok(cflags)
178 }
179}