Skip to content

Commit 50297bb

Browse files
committed
Auto merge of rust-lang#125411 - onur-ozkan:sanity-check-libstdc++, r=Mark-Simulacrum
check host's libstdc++ version when using ci llvm If the host's libstdc++ version is too old using ci-llvm may result in an ABI mismatch between the local libstdc++ and libLLVM.so. This PR adds a sanity check to immediately fail at the beginning of the bootstrap before starting the actual build. I am not sure if '8' is the best threshold, but it should be a good start and we can increase it anytime if needed. Fixes rust-lang#123037
2 parents 67caf52 + dd99021 commit 50297bb

File tree

5 files changed

+118
-0
lines changed

5 files changed

+118
-0
lines changed

Diff for: src/bootstrap/src/core/build_steps/tool.rs

+54
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun,
1010
use crate::core::config::TargetSelection;
1111
use crate::utils::channel::GitInfo;
1212
use crate::utils::exec::BootstrapCommand;
13+
use crate::utils::helpers::output;
1314
use crate::utils::helpers::{add_dylib_path, exe, t};
1415
use crate::Compiler;
1516
use crate::Mode;
@@ -804,6 +805,59 @@ impl Step for LlvmBitcodeLinker {
804805
}
805806
}
806807

808+
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
809+
pub struct LibcxxVersionTool {
810+
pub target: TargetSelection,
811+
}
812+
813+
#[allow(dead_code)]
814+
#[derive(Debug, Clone)]
815+
pub enum LibcxxVersion {
816+
Gnu(usize),
817+
Llvm(usize),
818+
}
819+
820+
impl Step for LibcxxVersionTool {
821+
type Output = LibcxxVersion;
822+
const DEFAULT: bool = false;
823+
const ONLY_HOSTS: bool = true;
824+
825+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
826+
run.never()
827+
}
828+
829+
fn run(self, builder: &Builder<'_>) -> LibcxxVersion {
830+
let out_dir = builder.out.join(self.target.to_string()).join("libcxx-version");
831+
let _ = fs::remove_dir_all(&out_dir);
832+
t!(fs::create_dir_all(&out_dir));
833+
834+
let compiler = builder.cxx(self.target).unwrap();
835+
let mut cmd = Command::new(compiler);
836+
837+
let executable = out_dir.join("libcxx-version");
838+
cmd.arg("-o").arg(&executable).arg(builder.src.join("src/tools/libcxx-version/main.cpp"));
839+
840+
builder.run_cmd(&mut cmd);
841+
842+
if !executable.exists() {
843+
panic!("Something went wrong. {} is not present", executable.display());
844+
}
845+
846+
let version_output = output(&mut Command::new(executable));
847+
848+
let version_str = version_output.split_once("version:").unwrap().1;
849+
let version = version_str.trim().parse::<usize>().unwrap();
850+
851+
if version_output.starts_with("libstdc++") {
852+
LibcxxVersion::Gnu(version)
853+
} else if version_output.starts_with("libc++") {
854+
LibcxxVersion::Llvm(version)
855+
} else {
856+
panic!("Coudln't recognize the standard library version.");
857+
}
858+
}
859+
}
860+
807861
macro_rules! tool_extended {
808862
(($sel:ident, $builder:ident),
809863
$($name:ident,

Diff for: src/bootstrap/src/core/sanity.rs

+35
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ use std::fs;
1515
use std::path::PathBuf;
1616
use std::process::Command;
1717

18+
#[cfg(not(feature = "bootstrap-self-test"))]
19+
use crate::builder::Builder;
20+
#[cfg(not(feature = "bootstrap-self-test"))]
21+
use crate::core::build_steps::tool;
1822
#[cfg(not(feature = "bootstrap-self-test"))]
1923
use std::collections::HashSet;
2024

@@ -38,6 +42,11 @@ const STAGE0_MISSING_TARGETS: &[&str] = &[
3842
// just a dummy comment so the list doesn't get onelined
3943
];
4044

45+
/// Minimum version threshold for libstdc++ required when using prebuilt LLVM
46+
/// from CI (with`llvm.download-ci-llvm` option).
47+
#[cfg(not(feature = "bootstrap-self-test"))]
48+
const LIBSTDCXX_MIN_VERSION_THRESHOLD: usize = 8;
49+
4150
impl Finder {
4251
pub fn new() -> Self {
4352
Self { cache: HashMap::new(), path: env::var_os("PATH").unwrap_or_default() }
@@ -102,6 +111,32 @@ pub fn check(build: &mut Build) {
102111
cmd_finder.must_have("git");
103112
}
104113

114+
// Ensure that a compatible version of libstdc++ is available on the system when using `llvm.download-ci-llvm`.
115+
#[cfg(not(feature = "bootstrap-self-test"))]
116+
if !build.config.dry_run() && !build.build.is_msvc() && build.config.llvm_from_ci {
117+
let builder = Builder::new(build);
118+
let libcxx_version = builder.ensure(tool::LibcxxVersionTool { target: build.build });
119+
120+
match libcxx_version {
121+
tool::LibcxxVersion::Gnu(version) => {
122+
if LIBSTDCXX_MIN_VERSION_THRESHOLD > version {
123+
eprintln!(
124+
"\nYour system's libstdc++ version is too old for the `llvm.download-ci-llvm` option."
125+
);
126+
eprintln!("Current version detected: '{}'", version);
127+
eprintln!("Minimum required version: '{}'", LIBSTDCXX_MIN_VERSION_THRESHOLD);
128+
eprintln!(
129+
"Consider upgrading libstdc++ or disabling the `llvm.download-ci-llvm` option."
130+
);
131+
crate::exit!(1);
132+
}
133+
}
134+
tool::LibcxxVersion::Llvm(_) => {
135+
// FIXME: Handle libc++ version check.
136+
}
137+
}
138+
}
139+
105140
// We need cmake, but only if we're actually building LLVM or sanitizers.
106141
let building_llvm = build
107142
.hosts

Diff for: src/tools/libcxx-version/main.cpp

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Detecting the standard library version manually using a bunch of shell commands is very
2+
// complicated and fragile across different platforms. This program provides the major version
3+
// of the standard library on any target platform without requiring any messy work.
4+
//
5+
// It's nothing more than specifying the name of the standard library implementation (either libstdc++ or libc++)
6+
// and its major version.
7+
8+
#include <iostream>
9+
10+
int main() {
11+
#ifdef _GLIBCXX_RELEASE
12+
std::cout << "libstdc++ version: " << _GLIBCXX_RELEASE << std::endl;
13+
#elif defined(_LIBCPP_VERSION)
14+
// _LIBCPP_VERSION follows "XXYYZZ" format (e.g., 170001 for 17.0.1).
15+
// ref: https://github.com/llvm/llvm-project/blob/f64732195c1030ee2627ff4e4142038e01df1d26/libcxx/include/__config#L51-L54
16+
//
17+
// Since we use the major version from _GLIBCXX_RELEASE, we need to extract only the first 2 characters of _LIBCPP_VERSION
18+
// to provide the major version for consistency.
19+
std::cout << "libc++ version: " << std::to_string(_LIBCPP_VERSION).substr(0, 2) << std::endl;
20+
#else
21+
std::cerr << "Coudln't recognize the standard library version." << std::endl;
22+
return 1;
23+
#endif
24+
25+
return 0;
26+
}

Diff for: src/tools/tidy/src/walk.rs

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub fn filter_dirs(path: &Path) -> bool {
1616
"library/stdarch",
1717
"src/tools/cargo",
1818
"src/tools/clippy",
19+
"src/tools/libcxx-version",
1920
"src/tools/miri",
2021
"src/tools/rust-analyzer",
2122
"src/tools/rustc-perf",

Diff for: triagebot.toml

+2
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ trigger_files = [
330330
"src/tools/compiletest",
331331
"src/tools/tidy",
332332
"src/tools/rustdoc-gui-test",
333+
"src/tools/libcxx-version",
333334
]
334335

335336
[autolabel."T-infra"]
@@ -1117,6 +1118,7 @@ project-exploit-mitigations = [
11171118
"/src/tools/tidy" = ["bootstrap"]
11181119
"/src/tools/x" = ["bootstrap"]
11191120
"/src/tools/rustdoc-gui-test" = ["bootstrap", "@onur-ozkan"]
1121+
"/src/tools/libcxx-version" = ["@onur-ozkan"]
11201122

11211123
# Enable tracking of PR review assignment
11221124
# Documentation at: https://forge.rust-lang.org/triagebot/pr-assignment-tracking.html

0 commit comments

Comments
 (0)