Skip to content

Commit a3df310

Browse files
committed
Auto merge of rust-lang#135164 - Kobzol:run-make-test-glibc-symbols, r=<try>
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: rust-lang#134037 r? `@jieyouxu`
2 parents b2728d5 + 76937aa commit a3df310

File tree

7 files changed

+129
-3
lines changed

7 files changed

+129
-3
lines changed

src/ci/github-actions/jobs.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ envs:
8383
# - not running `opt-dist`'s post-optimization smoke tests on the resulting toolchain
8484
#
8585
# If you *want* these to happen however, temporarily uncomment it before triggering a try build.
86-
DIST_TRY_BUILD: 1
86+
# DIST_TRY_BUILD: 1
8787

8888
auto:
8989
<<: *production

src/doc/rustc-dev-guide/src/tests/directives.md

+2
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ Some examples of `X` in `ignore-X` or `only-X`:
152152
`compare-mode-split-dwarf`, `compare-mode-split-dwarf-single`
153153
- The two different test modes used by coverage tests:
154154
`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

156158
The following directives will check rustc build settings and target
157159
settings:

src/tools/compiletest/src/directive-list.rs

+1
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
175175
"only-beta",
176176
"only-bpf",
177177
"only-cdb",
178+
"only-dist",
178179
"only-gnu",
179180
"only-i686-pc-windows-gnu",
180181
"only-i686-pc-windows-msvc",

src/tools/compiletest/src/header/cfg.rs

+6
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,12 @@ fn parse_cfg_name_directive<'a>(
235235
message: "when the test mode is {name}",
236236
}
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+
238244
if prefix == "ignore" && outcome == MatchOutcome::Invalid {
239245
// Don't error out for ignore-tidy-* diretives, as those are not handled by compiletest.
240246
if name.starts_with("tidy-") {

src/tools/compiletest/src/read2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub fn read2_abbreviated(
3838
Ok((Output { status, stdout: stdout.into_bytes(), stderr: stderr.into_bytes() }, truncated))
3939
}
4040

41-
const MAX_OUT_LEN: usize = 512 * 1024;
41+
const MAX_OUT_LEN: usize = 512 * 1024 * 1024;
4242

4343
// Whenever a path is filtered when counting the length of the output, we need to add some
4444
// placeholder length to ensure a compiler emitting only filtered paths doesn't cause a OOM.

src/tools/opt-dist/src/tests.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ change-id = 115898
6969
7070
[rust]
7171
channel = "{channel}"
72+
verbose-tests = true
7273
7374
[build]
7475
rustc = "{rustc}"
@@ -102,13 +103,19 @@ llvm-config = "{llvm_config}"
102103
"tests/incremental",
103104
"tests/mir-opt",
104105
"tests/pretty",
106+
"tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu",
105107
"tests/ui",
106108
"tests/crashes",
107109
];
108110
for test_path in env.skipped_tests() {
109111
args.extend(["--skip", test_path]);
110112
}
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")
112119
}
113120

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

0 commit comments

Comments
 (0)