Change:
Building Rust toolchain using latest leads to an error when compiling libtest at stage0: Error is coming from (Enable --no-undefined-version by default.), with the description indicating that our version scripts were not correct. Local workaround is to pass in Rustflags. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
I ran into this too. I wonder why rustc is attempting to export those symbols from , but not ? I'm seeing that e.g. works but does not. I can't determine what's different between and that would cause only the latter to output an invalid version script.
This actually repros outside of . If you attempt to compile any crate as a dylib, and is available as a dylib and linked as such (e.g. with the ) flag, you can reproduce the error by simply passing . This was with stable rustc and my system . Hopefully reproducing it outside of Rust's bootstrap will make it easier to debug.
related discussion: https://rust-
I suspect I found the issue: First, early in compilation rustc determines if a crate needs an allocator (whether or not it comes from the crate itself, or a dep). This is filled in a crate metadata struct field: If , the field says whether the crate graph contains a or not, but that's not directly relevant to the bug. This field will usually be ; it's if and are not used. Later, rustc checks whether to codegen the et al functions: Notably, if any dependency is linked as a , it will not generate them and instead link to the implementations exported from another . Independently, rustc determines which symbols to export when generating a and provides this list to the linker. Part of this is deciding whether to export the allocator symbols: Notably it does not perform the check from the last step. Even if a dependency is linked whose allocator symbols this crate should use, the export list still contains these symbol names. So when building a that links another , rustc tells the linker to export symbols that weren't actually defined. The build failure is just one instance of this bug. who helped in the Zulip discussion. Can you confirm this explanation makes sense?
Most of it. There is a further complication that you can specify multiple crate types for a single compilation. It is then possible for some crate types to generate the allocator shim while others don't generate it. The list of exported symbols however is for all crate types at the same time. One way to fix this would be to have a separate list of exported symbols for each crate type. Another option would be to move the addition of the symbols to the actual code that invokes the linker.
Listing exported symbols per crate type seems like the most correct fix in principle, but the lists will be almost the same except for the allocator symbols. I'll try to find a clean way to do the latter.
claim

Discussion:
use crate::prelude::*; use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; <ins> use rustc_codegen_ssa::base::allocator_kind_for_codegen; </ins> use rustc_session::config::OomStrategy; use rustc_span::symbol::sym;

module: &mut impl Module, unwind_context: &mut UnwindContext, ) -> bool { <del> let any_dynamic_crate = tcx.dependency_formats(()).iter().any(|(_, list)| { use rustc_middle::middle::dependency_format::Linkage; list.iter().any(|&linkage| linkage == Linkage::Dynamic) }); if any_dynamic_crate { false } else if let Some(kind) = tcx.allocator_kind(()) { codegen_inner( module, unwind_context, kind, tcx.alloc_error_handler_kind(()).unwrap(), tcx.sess.opts.unstable_opts.oom, ); true } else { false } </del> <ins> let Some(kind) = allocator_kind_for_codegen(tcx) else { return false }; codegen_inner( module, unwind_context, kind, tcx.alloc_error_handler_kind(()).unwrap(), tcx.sess.opts.unstable_opts.oom, ); true </ins> } fn codegen_inner(

let mut arg = OsString::from("--version-script="); arg.push(path); self.linker_arg(arg); <ins> self.linker_arg("--no-undefined-version"); </ins> } } }

<ins> use crate::base::allocator_kind_for_codegen; </ins> use std::collections::hash_map::Entry::*; use rustc_ast::expand::allocator::ALLOCATOR_METHODS;

)); } <del> if tcx.allocator_kind(()).is_some() { </del> <ins> // Mark allocator shim symbols as exported only if they were generated. if allocator_kind_for_codegen(tcx).is_some() { </ins> for symbol_name in ALLOCATOR_METHODS .iter() .map(|method| format!("__rust_{}", method.name))

use crate::traits::*; use crate::{CachedModuleCodegen, CompiledModule, CrateInfo, MemFlags, ModuleCodegen, ModuleKind}; <ins> use rustc_ast::expand::allocator::AllocatorKind; </ins> use rustc_attr as attr; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};

.collect::<BTreeSet<_>>() } <ins> /// Decide allocator kind to codegen. If `Some(_)` this will be the same as /// `tcx.allocator_kind`, but it may be `None` in more cases (e.g. if using /// allocator definitions from a dylib dependency). pub fn allocator_kind_for_codegen(tcx: TyCtxt<'_>) -> Option<AllocatorKind> { // If the crate doesn't have an `allocator_kind` set then there's definitely // no shim to generate. Otherwise we also check our dependency graph for all // our output crate types. If anything there looks like its a `Dynamic` // linkage, then it's already got an allocator shim and we'll be using that // one instead. If nothing exists then it's our job to generate the // allocator! let any_dynamic_crate = tcx.dependency_formats(()).iter().any(|(_, list)| { use rustc_middle::middle::dependency_format::Linkage; list.iter().any(|&linkage| linkage == Linkage::Dynamic) }); if any_dynamic_crate { None } else { tcx.allocator_kind(()) } } </ins> pub fn codegen_crate<B: ExtraBackendMethods>( backend: B, tcx: TyCtxt<'_>,

); // Codegen an allocator shim, if necessary. <del> // // If the crate doesn't have an `allocator_kind` set then there's definitely // no shim to generate. Otherwise we also check our dependency graph for all // our output crate types. If anything there looks like its a `Dynamic` // linkage, then it's already got an allocator shim and we'll be using that // one instead. If nothing exists then it's our job to generate the // allocator! let any_dynamic_crate = tcx.dependency_formats(()).iter().any(|(_, list)| { use rustc_middle::middle::dependency_format::Linkage; list.iter().any(|&linkage| linkage == Linkage::Dynamic) }); let allocator_module = if any_dynamic_crate { None } else if let Some(kind) = tcx.allocator_kind(()) { </del> <ins> if let Some(kind) = allocator_kind_for_codegen(tcx) { </ins> let llmod_id = cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); let module_llvm = tcx.sess.time("write_allocator_module", || {

) }); <del> Some(ModuleCodegen { name: llmod_id, module_llvm, kind: ModuleKind::Allocator }) } else { None }; if let Some(allocator_module) = allocator_module { ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, allocator_module); </del> <ins> ongoing_codegen.submit_pre_codegened_module_to_llvm( tcx, ModuleCodegen { name: llmod_id, module_llvm, kind: ModuleKind::Allocator }, ); </ins> } // For better throughput during parallel processing by LLVM, we used to sort