about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-06-06 12:52:06 +0000
committerbors <bors@rust-lang.org>2024-06-06 12:52:06 +0000
commit50297bb417dc338b475fd7cf8b706fa81df85b25 (patch)
treee342fcf464d3af26f2c44db354ec2efc6b5b83db
parent67caf52fbc4f10cdf427f04e6cbb35c8b9d96bb7 (diff)
parentdd9902118c0cbbcfc35c246673b381072ac5b3a8 (diff)
downloadrust-50297bb417dc338b475fd7cf8b706fa81df85b25.tar.gz
rust-50297bb417dc338b475fd7cf8b706fa81df85b25.zip
Auto merge of #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 #123037
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs54
-rw-r--r--src/bootstrap/src/core/sanity.rs35
-rw-r--r--src/tools/libcxx-version/main.cpp26
-rw-r--r--src/tools/tidy/src/walk.rs1
-rw-r--r--triagebot.toml2
5 files changed, 118 insertions, 0 deletions
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 2db3f8f7936..05b19c0a6e3 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -10,6 +10,7 @@ use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun,
 use crate::core::config::TargetSelection;
 use crate::utils::channel::GitInfo;
 use crate::utils::exec::BootstrapCommand;
+use crate::utils::helpers::output;
 use crate::utils::helpers::{add_dylib_path, exe, t};
 use crate::Compiler;
 use crate::Mode;
@@ -804,6 +805,59 @@ impl Step for LlvmBitcodeLinker {
     }
 }
 
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct LibcxxVersionTool {
+    pub target: TargetSelection,
+}
+
+#[allow(dead_code)]
+#[derive(Debug, Clone)]
+pub enum LibcxxVersion {
+    Gnu(usize),
+    Llvm(usize),
+}
+
+impl Step for LibcxxVersionTool {
+    type Output = LibcxxVersion;
+    const DEFAULT: bool = false;
+    const ONLY_HOSTS: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.never()
+    }
+
+    fn run(self, builder: &Builder<'_>) -> LibcxxVersion {
+        let out_dir = builder.out.join(self.target.to_string()).join("libcxx-version");
+        let _ = fs::remove_dir_all(&out_dir);
+        t!(fs::create_dir_all(&out_dir));
+
+        let compiler = builder.cxx(self.target).unwrap();
+        let mut cmd = Command::new(compiler);
+
+        let executable = out_dir.join("libcxx-version");
+        cmd.arg("-o").arg(&executable).arg(builder.src.join("src/tools/libcxx-version/main.cpp"));
+
+        builder.run_cmd(&mut cmd);
+
+        if !executable.exists() {
+            panic!("Something went wrong. {} is not present", executable.display());
+        }
+
+        let version_output = output(&mut Command::new(executable));
+
+        let version_str = version_output.split_once("version:").unwrap().1;
+        let version = version_str.trim().parse::<usize>().unwrap();
+
+        if version_output.starts_with("libstdc++") {
+            LibcxxVersion::Gnu(version)
+        } else if version_output.starts_with("libc++") {
+            LibcxxVersion::Llvm(version)
+        } else {
+            panic!("Coudln't recognize the standard library version.");
+        }
+    }
+}
+
 macro_rules! tool_extended {
     (($sel:ident, $builder:ident),
        $($name:ident,
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index ead38ebc6d5..e3556cb16b0 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -16,6 +16,10 @@ use std::path::PathBuf;
 use std::process::Command;
 
 #[cfg(not(feature = "bootstrap-self-test"))]
+use crate::builder::Builder;
+#[cfg(not(feature = "bootstrap-self-test"))]
+use crate::core::build_steps::tool;
+#[cfg(not(feature = "bootstrap-self-test"))]
 use std::collections::HashSet;
 
 use crate::builder::Kind;
@@ -38,6 +42,11 @@ const STAGE0_MISSING_TARGETS: &[&str] = &[
     // just a dummy comment so the list doesn't get onelined
 ];
 
+/// Minimum version threshold for libstdc++ required when using prebuilt LLVM
+/// from CI (with`llvm.download-ci-llvm` option).
+#[cfg(not(feature = "bootstrap-self-test"))]
+const LIBSTDCXX_MIN_VERSION_THRESHOLD: usize = 8;
+
 impl Finder {
     pub fn new() -> Self {
         Self { cache: HashMap::new(), path: env::var_os("PATH").unwrap_or_default() }
@@ -102,6 +111,32 @@ pub fn check(build: &mut Build) {
         cmd_finder.must_have("git");
     }
 
+    // Ensure that a compatible version of libstdc++ is available on the system when using `llvm.download-ci-llvm`.
+    #[cfg(not(feature = "bootstrap-self-test"))]
+    if !build.config.dry_run() && !build.build.is_msvc() && build.config.llvm_from_ci {
+        let builder = Builder::new(build);
+        let libcxx_version = builder.ensure(tool::LibcxxVersionTool { target: build.build });
+
+        match libcxx_version {
+            tool::LibcxxVersion::Gnu(version) => {
+                if LIBSTDCXX_MIN_VERSION_THRESHOLD > version {
+                    eprintln!(
+                        "\nYour system's libstdc++ version is too old for the `llvm.download-ci-llvm` option."
+                    );
+                    eprintln!("Current version detected: '{}'", version);
+                    eprintln!("Minimum required version: '{}'", LIBSTDCXX_MIN_VERSION_THRESHOLD);
+                    eprintln!(
+                        "Consider upgrading libstdc++ or disabling the `llvm.download-ci-llvm` option."
+                    );
+                    crate::exit!(1);
+                }
+            }
+            tool::LibcxxVersion::Llvm(_) => {
+                // FIXME: Handle libc++ version check.
+            }
+        }
+    }
+
     // We need cmake, but only if we're actually building LLVM or sanitizers.
     let building_llvm = build
         .hosts
diff --git a/src/tools/libcxx-version/main.cpp b/src/tools/libcxx-version/main.cpp
new file mode 100644
index 00000000000..79df7ef457c
--- /dev/null
+++ b/src/tools/libcxx-version/main.cpp
@@ -0,0 +1,26 @@
+// Detecting the standard library version manually using a bunch of shell commands is very
+// complicated and fragile across different platforms. This program provides the major version
+// of the standard library on any target platform without requiring any messy work.
+//
+// It's nothing more than specifying the name of the standard library implementation (either libstdc++ or libc++)
+// and its major version.
+
+#include <iostream>
+
+int main() {
+    #ifdef _GLIBCXX_RELEASE
+        std::cout << "libstdc++ version: " << _GLIBCXX_RELEASE << std::endl;
+    #elif defined(_LIBCPP_VERSION)
+        // _LIBCPP_VERSION follows "XXYYZZ" format (e.g., 170001 for 17.0.1).
+        // ref: https://github.com/llvm/llvm-project/blob/f64732195c1030ee2627ff4e4142038e01df1d26/libcxx/include/__config#L51-L54
+        //
+        // Since we use the major version from _GLIBCXX_RELEASE, we need to extract only the first 2 characters of _LIBCPP_VERSION
+        // to provide the major version for consistency.
+        std::cout << "libc++ version: " << std::to_string(_LIBCPP_VERSION).substr(0, 2) << std::endl;
+    #else
+        std::cerr << "Coudln't recognize the standard library version." << std::endl;
+        return 1;
+    #endif
+
+    return 0;
+}
diff --git a/src/tools/tidy/src/walk.rs b/src/tools/tidy/src/walk.rs
index f68b7675c76..63a03834166 100644
--- a/src/tools/tidy/src/walk.rs
+++ b/src/tools/tidy/src/walk.rs
@@ -16,6 +16,7 @@ pub fn filter_dirs(path: &Path) -> bool {
         "library/stdarch",
         "src/tools/cargo",
         "src/tools/clippy",
+        "src/tools/libcxx-version",
         "src/tools/miri",
         "src/tools/rust-analyzer",
         "src/tools/rustc-perf",
diff --git a/triagebot.toml b/triagebot.toml
index 23e5c0a27f3..55f63ef2567 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -330,6 +330,7 @@ trigger_files = [
     "src/tools/compiletest",
     "src/tools/tidy",
     "src/tools/rustdoc-gui-test",
+    "src/tools/libcxx-version",
 ]
 
 [autolabel."T-infra"]
@@ -1117,6 +1118,7 @@ project-exploit-mitigations = [
 "/src/tools/tidy" =                                      ["bootstrap"]
 "/src/tools/x" =                                         ["bootstrap"]
 "/src/tools/rustdoc-gui-test" =                          ["bootstrap", "@onur-ozkan"]
+"/src/tools/libcxx-version" =                            ["@onur-ozkan"]
 
 # Enable tracking of PR review assignment
 # Documentation at: https://forge.rust-lang.org/triagebot/pr-assignment-tracking.html