diff options
| author | Jakub Beránek <berykubik@gmail.com> | 2025-01-06 17:23:28 +0100 |
|---|---|---|
| committer | Jakub Beránek <berykubik@gmail.com> | 2025-01-21 10:20:24 +0100 |
| commit | ef9349db86dcbbe705aaf40a2f5dcb1dd9c9063e (patch) | |
| tree | 657d7333da396b35cb4d05c49ca2442b3b6062da /tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu | |
| parent | b605c65b6eb5fa71783f8e26df69975f9f1680ee (diff) | |
| download | rust-ef9349db86dcbbe705aaf40a2f5dcb1dd9c9063e.tar.gz rust-ef9349db86dcbbe705aaf40a2f5dcb1dd9c9063e.zip | |
Add test for checking used glibc symbols
Diffstat (limited to 'tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu')
| -rw-r--r-- | tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu/rmake.rs | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu/rmake.rs b/tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu/rmake.rs new file mode 100644 index 00000000000..ec693ca793c --- /dev/null +++ b/tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu/rmake.rs @@ -0,0 +1,108 @@ +// Check that the compiler toolchain (rustc) that we distribute is not using newer glibc +// symbols than a specified minimum. +// This test should only be executed on an extracted dist archive or in a dist-* CI job. + +//@ only-dist +//@ only-x86_64-unknown-linux-gnu +//@ ignore-cross-compile + +use std::path::{Path, PathBuf}; + +use run_make_support::{cmd, llvm_objdump, regex, rustc_path}; + +fn main() { + // This is the maximum glibc version *supported* by the x86_64-unknown-linux-gnu target. + // All glibc symbols used in the compiler must be lower or equal than this version. + let max_supported = (2, 17, 99); + + let rustc = PathBuf::from(rustc_path()); + // Check symbols directly in rustc + check_symbols(&rustc, max_supported); + + // Find dynamic libraries referenced by rustc that come from our lib directory + let lib_path = rustc.parent().unwrap().parent().unwrap().join("lib"); + let dynamic_libs = find_dynamic_libs(&rustc) + .into_iter() + .filter_map(|path| path.canonicalize().ok()) + .filter(|lib| lib.starts_with(&lib_path)) + .collect::<Vec<_>>(); + for lib in dynamic_libs { + check_symbols(&lib, max_supported); + } +} + +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +struct GlibcSymbol { + name: String, + version: (u32, u32, u32), +} + +fn find_dynamic_libs(path: &Path) -> Vec<PathBuf> { + cmd("ldd") + .arg(path) + .run() + .stdout_utf8() + .lines() + .filter_map(|line| { + let line = line.trim(); + let Some((_, line)) = line.split_once(" => ") else { + return None; + }; + line.split_ascii_whitespace().next().map(|path| PathBuf::from(path)) + }) + .collect() +} + +fn check_symbols(file: &Path, max_supported: (u32, u32, u32)) { + println!("Checking {}", file.display()); + let mut invalid: Vec<GlibcSymbol> = get_glibc_symbols(file) + .into_iter() + .filter(|symbol| symbol.version > max_supported) + .collect(); + if !invalid.is_empty() { + invalid.sort(); + panic!( + "Found invalid glibc symbols in {}:\n{}", + file.display(), + invalid + .into_iter() + .map(|symbol| format!( + "{} ({:?} higher than max allowed {:?})", + symbol.name, symbol.version, max_supported + )) + .collect::<Vec<_>>() + .join("\n") + ) + } +} + +fn get_glibc_symbols(file: &Path) -> Vec<GlibcSymbol> { + let regex = regex::Regex::new(r#"GLIBC_(\d)+\.(\d+)(:?\.(\d+))?"#).unwrap(); + + // Uses llvm-objdump, because implementing this using the `object` crate is quite complicated. + llvm_objdump() + .arg("-T") + .arg(file) + .run() + .stdout_utf8() + .lines() + .filter_map(|line| { + // Example line + // 0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.2.5) sbrk + let mut parts = line.split(" ").collect::<Vec<_>>().into_iter().rev(); + let Some(name) = parts.next() else { + return None; + }; + let Some(lib) = parts.next() else { + return None; + }; + let Some(version) = regex.captures(lib) else { + return None; + }; + let major = version.get(1).and_then(|m| m.as_str().parse().ok()).unwrap_or(0); + let minor = version.get(2).and_then(|m| m.as_str().parse().ok()).unwrap_or(0); + let patch = version.get(3).and_then(|m| m.as_str().parse().ok()).unwrap_or(0); + Some(GlibcSymbol { version: (major, minor, patch), name: name.to_string() }) + }) + .collect() +} |
