Skip to content

Commit 1f74654

Browse files
committed
Add test for checking used glibc symbols
1 parent 13738b0 commit 1f74654

File tree

1 file changed

+105
-0
lines changed
  • tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu

1 file changed

+105
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Check that the compiler (rustc) is not using newer glibc symbols than a specified minimum.
2+
3+
//@ only-x86_64-unknown-linux-gnu
4+
//@ ignore-cross-compile
5+
6+
use std::path::{Path, PathBuf};
7+
8+
use run_make_support::{cmd, llvm_objdump, regex, rustc_path};
9+
10+
fn main() {
11+
// This is the maximum glibc version *supported* by the x86_64-unknown-linux-gnu target.
12+
// All glibc symbols used in the compiler must be lower or equal than this version.
13+
let max_supported = (2, 17, 99);
14+
15+
let rustc = PathBuf::from(rustc_path());
16+
// Check symbols directly in rustc
17+
check_symbols(&rustc, max_supported);
18+
19+
// Find dynamic libraries referenced by rustc that come from our lib directory
20+
let lib_path = rustc.parent().unwrap().parent().unwrap().join("lib");
21+
let dynamic_libs = find_dynamic_libs(&rustc)
22+
.into_iter()
23+
.filter_map(|path| path.canonicalize().ok())
24+
.filter(|lib| lib.starts_with(&lib_path))
25+
.collect::<Vec<_>>();
26+
for lib in dynamic_libs {
27+
check_symbols(&lib, max_supported);
28+
}
29+
}
30+
31+
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
32+
struct GlibcSymbol {
33+
name: String,
34+
version: (u32, u32, u32),
35+
}
36+
37+
fn find_dynamic_libs(path: &Path) -> Vec<PathBuf> {
38+
cmd("ldd")
39+
.arg(path)
40+
.run()
41+
.stdout_utf8()
42+
.lines()
43+
.filter_map(|line| {
44+
let line = line.trim();
45+
let Some((_, line)) = line.split_once(" => ") else {
46+
return None;
47+
};
48+
line.split_ascii_whitespace().next().map(|path| PathBuf::from(path))
49+
})
50+
.collect()
51+
}
52+
53+
fn check_symbols(file: &Path, max_supported: (u32, u32, u32)) {
54+
println!("Checking {}", file.display());
55+
let mut invalid: Vec<GlibcSymbol> = get_glibc_symbols(file)
56+
.into_iter()
57+
.filter(|symbol| symbol.version > max_supported)
58+
.collect();
59+
if !invalid.is_empty() {
60+
invalid.sort();
61+
panic!(
62+
"Found invalid glibc symbols in {}:\n{}",
63+
file.display(),
64+
invalid
65+
.into_iter()
66+
.map(|symbol| format!(
67+
"{} ({:?} higher than max allowed {:?})",
68+
symbol.name, symbol.version, max_supported
69+
))
70+
.collect::<Vec<_>>()
71+
.join("\n")
72+
)
73+
}
74+
}
75+
76+
fn get_glibc_symbols(file: &Path) -> Vec<GlibcSymbol> {
77+
let regex = regex::Regex::new(r#"GLIBC_(\d)+\.(\d+)(:?\.(\d+))?"#).unwrap();
78+
79+
// Uses llvm-objdump, because implementing this using the `object` crate is quite complicated.
80+
llvm_objdump()
81+
.arg("-T")
82+
.arg(file)
83+
.run()
84+
.stdout_utf8()
85+
.lines()
86+
.filter_map(|line| {
87+
// Example line
88+
// 0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.2.5) sbrk
89+
let mut parts = line.split(" ").collect::<Vec<_>>().into_iter().rev();
90+
let Some(name) = parts.next() else {
91+
return None;
92+
};
93+
let Some(lib) = parts.next() else {
94+
return None;
95+
};
96+
let Some(version) = regex.captures(lib) else {
97+
return None;
98+
};
99+
let major = version.get(1).and_then(|m| m.as_str().parse().ok()).unwrap_or(0);
100+
let minor = version.get(2).and_then(|m| m.as_str().parse().ok()).unwrap_or(0);
101+
let patch = version.get(3).and_then(|m| m.as_str().parse().ok()).unwrap_or(0);
102+
Some(GlibcSymbol { version: (major, minor, patch), name: name.to_string() })
103+
})
104+
.collect()
105+
}

0 commit comments

Comments
 (0)