diff --git a/config.example.toml b/config.example.toml index 367f95b156fed..a7012e5b8a9be 100644 --- a/config.example.toml +++ b/config.example.toml @@ -56,6 +56,11 @@ changelog-seen = 2 # Indicates whether an LLVM Release build should include debug info #release-debuginfo = false +# When building LLVM as a dynamically linked library, and not explicitly +# requesting debuginfo, strips the debuginfo it could contain (most often coming +# from the C++ standard library). +#strip-debuginfo = false + # Indicates whether the LLVM assertions are enabled or not # NOTE: When assertions are disabled, bugs in the integration between rustc and LLVM can lead to # unsoundness (segfaults, etc.) in the rustc process itself, not just in the generated code. diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index f307844456359..03f913898b4b5 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -173,6 +173,7 @@ pub struct Config { pub llvm_optimize: bool, pub llvm_thin_lto: bool, pub llvm_release_debuginfo: bool, + pub llvm_strip_debuginfo: bool, pub llvm_static_stdcpp: bool, /// `None` if `llvm_from_ci` is true and we haven't yet downloaded llvm. #[cfg(not(test))] @@ -823,6 +824,7 @@ define_config! { optimize: Option = "optimize", thin_lto: Option = "thin-lto", release_debuginfo: Option = "release-debuginfo", + strip_debuginfo: Option = "strip-debuginfo", assertions: Option = "assertions", tests: Option = "tests", plugins: Option = "plugins", @@ -1511,6 +1513,7 @@ impl Config { config.llvm_clang = llvm.clang.unwrap_or(false); config.llvm_enable_warnings = llvm.enable_warnings.unwrap_or(false); config.llvm_build_config = llvm.build_config.clone().unwrap_or(Default::default()); + config.llvm_strip_debuginfo = llvm.strip_debuginfo.unwrap_or(false); let asserts = llvm_assertions.unwrap_or(false); config.llvm_from_ci = match llvm.download_ci_llvm { @@ -1552,6 +1555,7 @@ impl Config { check_ci_llvm!(llvm.clang); check_ci_llvm!(llvm.build_config); check_ci_llvm!(llvm.plugins); + check_ci_llvm!(llvm.strip_debuginfo); } // NOTE: can never be hit when downloading from CI, since we call `check_ci_llvm!(thin_lto)` above. @@ -1561,6 +1565,25 @@ impl Config { // the link step) with each stage. config.llvm_link_shared.set(Some(true)); } + + // Stripping LLVM's debuginfo is only accepted when: + // - using a shared library + // - not explicitly requesting debuginfo + if config.llvm_strip_debuginfo { + if config.llvm_release_debuginfo { + panic!( + "enabling `llvm.strip-debuginfo` is incompatible \ + with setting `llvm.release-debuginfo`" + ); + } + let llvm_link_shared = config.llvm_link_shared.get().unwrap_or(false); + if !llvm_link_shared { + panic!( + "enabling `llvm.strip-debuginfo` is incompatible \ + with statically linking LLVM" + ); + } + } } else { config.llvm_from_ci = config.channel == "dev" && crate::llvm::is_ci_llvm_available(&config, false); diff --git a/src/bootstrap/llvm.rs b/src/bootstrap/llvm.rs index 2573c3ced3639..ce499979afdac 100644 --- a/src/bootstrap/llvm.rs +++ b/src/bootstrap/llvm.rs @@ -491,25 +491,53 @@ impl Step for Llvm { cfg.build(); - // When building LLVM with LLVM_LINK_LLVM_DYLIB for macOS, an unversioned - // libLLVM.dylib will be built. However, llvm-config will still look - // for a versioned path like libLLVM-14.dylib. Manually create a symbolic - // link to make llvm-config happy. - if builder.llvm_link_shared() && target.contains("apple-darwin") { + // Helper to find the name of LLVM's shared library on darwin and linux. + let find_llvm_lib_name = |extension| { let mut cmd = Command::new(&res.llvm_config); let version = output(cmd.arg("--version")); let major = version.split('.').next().unwrap(); - let lib_name = match llvm_version_suffix { - Some(s) => format!("libLLVM-{major}{s}.dylib"), - None => format!("libLLVM-{major}.dylib"), + let lib_name = match &llvm_version_suffix { + Some(version_suffix) => format!("libLLVM-{major}{version_suffix}.{extension}"), + None => format!("libLLVM-{major}.{extension}"), }; + lib_name + }; + // When building LLVM with LLVM_LINK_LLVM_DYLIB for macOS, an unversioned + // libLLVM.dylib will be built. However, llvm-config will still look + // for a versioned path like libLLVM-14.dylib. Manually create a symbolic + // link to make llvm-config happy. + if builder.llvm_link_shared() && target.contains("apple-darwin") { + let lib_name = find_llvm_lib_name("dylib"); let lib_llvm = out_dir.join("build").join("lib").join(lib_name); if !lib_llvm.exists() { t!(builder.symlink_file("libLLVM.dylib", &lib_llvm)); } } + // When building LLVM as a shared library on linux, it can contain debuginfo even when + // `llvm.release-debuginfo` is false: some debuginfo can come from the C++ standard library. + // If we're asked to do so, strip this debuginfo away. + if builder.llvm_link_shared() + && target.contains("linux") + && builder.config.llvm_strip_debuginfo + { + // Find the name of the LLVM shared library that we just built. + let lib_name = find_llvm_lib_name("so"); + + // If the shared library exists in LLVM's `/build/lib/` or `/lib/` folders, strip its + // debuginfo. Note: `output` will propagate any errors here. + let strip_if_possible = |path: PathBuf| { + if path.exists() { + let mut cmd = Command::new("strip"); + cmd.arg("--strip-debug").arg(path); + output(&mut cmd); + } + }; + strip_if_possible(out_dir.join("lib").join(&lib_name)); + strip_if_possible(out_dir.join("build").join("lib").join(&lib_name)); + } + t!(stamp.write()); res diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index 319989df33460..91b8a317d6cb5 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -82,6 +82,7 @@ ENV RUST_CONFIGURE_ARGS \ --set target.x86_64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \ --set llvm.thin-lto=true \ --set llvm.ninja=false \ + --set llvm.strip-debuginfo=true \ --set rust.jemalloc \ --set rust.use-lld=true \ --set rust.lto=thin