authorbors <bors@rust-lang.org> 2025-01-23 01:03:32 UTC
committerbors <bors@rust-lang.org> 2025-01-23 01:03:32 UTC
log3cd8fcbf87bd28a1f31be000ca906fb66f4d451d
tree80686070e5783cfda5a586eee1fe2ebb3104e220
parenta30f9151fe4a841c4643af4e47ad18a23e1fb044
parent5482bbac0f18d45f0e02bbe8153e47b160287fff

Auto merge of #135164 - Kobzol:run-make-test-glibc-symbols, r=jieyouxu

Add test for checking used glibc symbols This test checks that we do not use too new glibc symbols in the compiler on x64 GNU Linux, in order not to break our [glibc promises](https://blog.rust-lang.org/2022/08/01/Increasing-glibc-kernel-requirements.html). One thing that isn't solved in the PR yet is to make sure that this test will only run on `dist` CI, more specifically on the `dist-x86_64-linux` runner, in the opt-dist post-optimization tests (it can fail elsewhere, that doesn't matter). Any suggestions on how to do that are welcome. Fixes: https://github.com/rust-lang/rust/issues/134037 r? `@jieyouxu`

5 files changed, 129 insertions(+), 1 deletions(-)

src/doc/rustc-dev-guide/src/tests/directives.md+2
...@@ -152,6 +152,8 @@ Some examples of `X` in `ignore-X` or `only-X`:...@@ -152,6 +152,8 @@ Some examples of `X` in `ignore-X` or `only-X`:
152 `compare-mode-split-dwarf`, `compare-mode-split-dwarf-single`152 `compare-mode-split-dwarf`, `compare-mode-split-dwarf-single`
153- The two different test modes used by coverage tests:153- The two different test modes used by coverage tests:
154 `ignore-coverage-map`, `ignore-coverage-run`154 `ignore-coverage-map`, `ignore-coverage-run`
155- When testing a dist toolchain: `dist`
156 - This needs to be enabled with `COMPILETEST_ENABLE_DIST_TESTS=1`
155157
156The following directives will check rustc build settings and target158The following directives will check rustc build settings and target
157settings:159settings:
src/tools/compiletest/src/directive-list.rs+1
...@@ -175,6 +175,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[...@@ -175,6 +175,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
175 "only-beta",175 "only-beta",
176 "only-bpf",176 "only-bpf",
177 "only-cdb",177 "only-cdb",
178 "only-dist",
178 "only-gnu",179 "only-gnu",
179 "only-i686-pc-windows-gnu",180 "only-i686-pc-windows-gnu",
180 "only-i686-pc-windows-msvc",181 "only-i686-pc-windows-msvc",
src/tools/compiletest/src/header/cfg.rs+6
...@@ -235,6 +235,12 @@ fn parse_cfg_name_directive<'a>(...@@ -235,6 +235,12 @@ fn parse_cfg_name_directive<'a>(
235 message: "when the test mode is {name}",235 message: "when the test mode is {name}",
236 }236 }
237237
238 condition! {
239 name: "dist",
240 condition: std::env::var("COMPILETEST_ENABLE_DIST_TESTS") == Ok("1".to_string()),
241 message: "when performing tests on dist toolchain"
242 }
243
238 if prefix == "ignore" && outcome == MatchOutcome::Invalid {244 if prefix == "ignore" && outcome == MatchOutcome::Invalid {
239 // Don't error out for ignore-tidy-* diretives, as those are not handled by compiletest.245 // Don't error out for ignore-tidy-* diretives, as those are not handled by compiletest.
240 if name.starts_with("tidy-") {246 if name.starts_with("tidy-") {
src/tools/opt-dist/src/tests.rs+8-1
...@@ -69,6 +69,7 @@ change-id = 115898...@@ -69,6 +69,7 @@ change-id = 115898
6969
70[rust]70[rust]
71channel = "{channel}"71channel = "{channel}"
72verbose-tests = true
7273
73[build]74[build]
74rustc = "{rustc}"75rustc = "{rustc}"
...@@ -102,13 +103,19 @@ llvm-config = "{llvm_config}"...@@ -102,13 +103,19 @@ llvm-config = "{llvm_config}"
102 "tests/incremental",103 "tests/incremental",
103 "tests/mir-opt",104 "tests/mir-opt",
104 "tests/pretty",105 "tests/pretty",
106 "tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu",
105 "tests/ui",107 "tests/ui",
106 "tests/crashes",108 "tests/crashes",
107 ];109 ];
108 for test_path in env.skipped_tests() {110 for test_path in env.skipped_tests() {
109 args.extend(["--skip", test_path]);111 args.extend(["--skip", test_path]);
110 }112 }
111 cmd(&args).env("COMPILETEST_FORCE_STAGE0", "1").run().context("Cannot execute tests")113 cmd(&args)
114 .env("COMPILETEST_FORCE_STAGE0", "1")
115 // Also run dist-only tests
116 .env("COMPILETEST_ENABLE_DIST_TESTS", "1")
117 .run()
118 .context("Cannot execute tests")
112}119}
113120
114/// Tries to find the version of the dist artifacts (either nightly, beta, or 1.XY.Z).121/// Tries to find the version of the dist artifacts (either nightly, beta, or 1.XY.Z).
tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu/rmake.rs created+112
...@@ -0,0 +1,112 @@
1// Check that the compiler toolchain (rustc) that we distribute is not using newer glibc
2// symbols than a specified minimum.
3// This test should only be executed on an extracted dist archive or in a dist-* CI job.
4
5//@ only-dist
6//@ only-x86_64-unknown-linux-gnu
7//@ ignore-cross-compile
8
9use std::path::{Path, PathBuf};
10
11use run_make_support::{cmd, llvm_objdump, regex, rustc_path};
12
13fn main() {
14 // This is the maximum glibc version that we are *permitted* to use for the
15 // x86_64-unknown-linux-gnu target.
16 // All glibc symbols used in the compiler must be lower or equal than this version.
17 // So that if a given machine only has glibc 2.17, it is able to run the compiler.
18 let max_supported = (2, 17, 99);
19
20 let rustc = PathBuf::from(rustc_path());
21 // Check symbols directly in rustc
22 check_symbols(&rustc, max_supported);
23
24 // Find dynamic libraries referenced by rustc that come from our lib directory
25 let lib_path = rustc.parent().unwrap().parent().unwrap().join("lib");
26 let dynamic_libs = find_dynamic_libs(&rustc)
27 .into_iter()
28 .filter_map(|path| path.canonicalize().ok())
29 .filter(|lib| lib.starts_with(&lib_path))
30 .collect::<Vec<_>>();
31 for lib in dynamic_libs {
32 check_symbols(&lib, max_supported);
33 }
34}
35
36#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
37struct GlibcSymbol {
38 name: String,
39 version: (u32, u32, u32),
40}
41
42fn find_dynamic_libs(path: &Path) -> Vec<PathBuf> {
43 cmd("ldd")
44 .arg(path)
45 .run()
46 .stdout_utf8()
47 .lines()
48 .filter_map(|line| {
49 let line = line.trim();
50 let Some((_, line)) = line.split_once(" => ") else {
51 return None;
52 };
53 line.split_ascii_whitespace().next().map(|path| PathBuf::from(path))
54 })
55 .collect()
56}
57
58fn check_symbols(file: &Path, max_supported: (u32, u32, u32)) {
59 println!("Checking {}", file.display());
60 let mut invalid: Vec<GlibcSymbol> = get_glibc_symbols(file)
61 .into_iter()
62 .filter(|symbol| symbol.version > max_supported)
63 .collect();
64 if !invalid.is_empty() {
65 invalid.sort();
66 panic!(
67 "Found invalid glibc symbols in {}:\n{}",
68 file.display(),
69 invalid
70 .into_iter()
71 .map(|symbol| format!(
72 "{} ({:?} higher than max allowed {:?})",
73 symbol.name, symbol.version, max_supported
74 ))
75 .collect::<Vec<_>>()
76 .join("\n")
77 )
78 }
79}
80
81fn get_glibc_symbols(file: &Path) -> Vec<GlibcSymbol> {
82 let regex = regex::Regex::new(r#"GLIBC_(\d)+\.(\d+)(:?\.(\d+))?"#).unwrap();
83
84 // FIXME(kobzol): llvm-objdump currently chokes on the BOLTed librustc_driver.so file.
85 // Use objdump instead, since it seems to work, and we only run this test in a specific
86 // CI environment anyway.
87 cmd("objdump")
88 .arg("--dynamic-syms")
89 .arg(file)
90 .run()
91 .stdout_utf8()
92 .lines()
93 .filter_map(|line| {
94 // Example line
95 // 0000000000000000 DF *UND* 0000000000000000 (GLIBC_2.2.5) sbrk
96 let mut parts = line.split(" ").collect::<Vec<_>>().into_iter().rev();
97 let Some(name) = parts.next() else {
98 return None;
99 };
100 let Some(lib) = parts.next() else {
101 return None;
102 };
103 let Some(version) = regex.captures(lib) else {
104 return None;
105 };
106 let major = version.get(1).and_then(|m| m.as_str().parse().ok()).unwrap_or(0);
107 let minor = version.get(2).and_then(|m| m.as_str().parse().ok()).unwrap_or(0);
108 let patch = version.get(3).and_then(|m| m.as_str().parse().ok()).unwrap_or(0);
109 Some(GlibcSymbol { version: (major, minor, patch), name: name.to_string() })
110 })
111 .collect()
112}