Code change:
fs::write(&self.path, self.hash.as_deref().unwrap_or(b"")) } } <ins> #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct CrtBeginEnd { pub target: TargetSelection, } impl Step for CrtBeginEnd { type Output = PathBuf; fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { run.path("src/llvm-project/compiler-rt/lib/crt") } fn make_run(run: RunConfig<'_>) { run.builder.ensure(CrtBeginEnd { target: run.target }); } /// Build crtbegin.o/crtend.o for musl target. fn run(self, builder: &Builder<'_>) -> Self::Output { let out_dir = builder.native_dir(self.target).join("crt"); if builder.config.dry_run { return out_dir; } let crtbegin_src = builder.src.join("src/llvm-project/compiler-rt/lib/crt/crtbegin.c"); let crtend_src = builder.src.join("src/llvm-project/compiler-rt/lib/crt/crtend.c"); if up_to_date(&crtbegin_src, &out_dir.join("crtbegin.o")) && up_to_date(&crtend_src, &out_dir.join("crtendS.o")) { return out_dir; } builder.info("Building crtbegin.o and crtend.o"); t!(fs::create_dir_all(&out_dir)); let mut cfg = cc::Build::new(); if let Some(ar) = builder.ar(self.target) { cfg.archiver(ar); } cfg.compiler(builder.cc(self.target)); cfg.cargo_metadata(false) .out_dir(&out_dir) .target(&self.target.triple) .host(&builder.config.build.triple) .warnings(false) .debug(false) .opt_level(3) .file(crtbegin_src) .file(crtend_src); // Those flags are defined in src/llvm-project/compiler-rt/lib/crt/CMakeLists.txt // Currently only consumer of those objects is musl, which use .init_array/.fini_array // instead of .ctors/.dtors cfg.flag("-std=c11") .define("CRT_HAS_INITFINI_ARRAY", None) .define("EH_USE_FRAME_REGISTRY", None); cfg.compile("crt"); t!(fs::copy(out_dir.join("crtbegin.o"), out_dir.join("crtbeginS.o"))); t!(fs::copy(out_dir.join("crtend.o"), out_dir.join("crtendS.o"))); out_dir } } </ins>

Related discussion:
text: where there's likely not a native toolchain and rustc is forced to work out the answers for itself (as an aside no longer works as the "linker" for musl targets, since it doesn't support which is enabled in rustc as of the last release) I'm worrying about the latter case and I think from above the fix for that is: to add the terminator in builds as well as ones and include it in the target package alongside the (so rustup etc fetch it) the link command rustc emits for the musl target to include before Some side questions: Does the above sound sensible to people? Do we need to include any of too? (I think not, but I'm no expert :shrug:) Do other targets need similar treatment? musl?
In the latter case it would make sense to me for rustc to invoke an actual linker (///etc) since at the moment it goes via a compiler but has to tell the compiler to not do anything and just to pass the link args to the linker anyway. This is off topic for this discussion though.
If we need to improve the self-contained mode specifically, then I think we can just ship the begin/end objects and link to them like gcc does obsoleting the comment cited in That appears to be the simplest solution. If that doesn't work out (e.g. due to licensing), then the necessary parts can be to and linked as , as you suggested above.
Which / objects were you thinking? 's? LLVM's? If you're right and it's the compiler that ships the begin/end then maybe it's right for to have it's own (though given that the link might contain code compiled by random compilers (from scripts) as well as compiled by , which is "the compiler" for the sake of this discussion)?
's? LLVM's? I don't know, but looks like they should be compatible. At least on Ubuntu both gcc and clang link to gcc's objects. The gcc ones can be just copied by rustbuild if the license allows, the LLVM ones can be used if we need to build them by ourselves (they are a part of compiler-rt).
adds crtbegin/crtend objects to Rust distribution and links to them in self-contained mode as was suggested in , so it should fix this issue.
I still have this problem with clang's compiler-rt: