From de2a8b9f3df3f62cddbac9e8b000a1e7b244a8df Mon Sep 17 00:00:00 2001 From: Matt Hammerly Date: Sat, 25 Feb 2023 21:09:10 -0800 Subject: [PATCH 1/5] -Z unified_sysroot_injection implemented for allocator crates --- Cargo.lock | 20 +++++ .../src/extern_allocator_crate.rs | 45 ++++++++++ compiler/rustc_builtin_macros/src/lib.rs | 1 + compiler/rustc_interface/src/passes.rs | 24 ++++++ compiler/rustc_metadata/src/creader.rs | 83 +++++++++++++------ compiler/rustc_session/src/options.rs | 2 + compiler/rustc_span/src/hygiene.rs | 2 + library/alloc_error_handler/Cargo.toml | 21 +++++ library/alloc_error_handler/build.rs | 52 ++++++++++++ library/alloc_error_handler/src/lib.rs | 31 +++++++ library/global_allocator/Cargo.toml | 15 ++++ library/global_allocator/build.rs | 52 ++++++++++++ library/global_allocator/src/lib.rs | 16 ++++ library/test/Cargo.toml | 4 +- 14 files changed, 340 insertions(+), 28 deletions(-) create mode 100644 compiler/rustc_builtin_macros/src/extern_allocator_crate.rs create mode 100644 library/alloc_error_handler/Cargo.toml create mode 100644 library/alloc_error_handler/build.rs create mode 100644 library/alloc_error_handler/src/lib.rs create mode 100644 library/global_allocator/Cargo.toml create mode 100644 library/global_allocator/build.rs create mode 100644 library/global_allocator/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 705210e44b24c..2887ba4f98ae6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,6 +65,15 @@ dependencies = [ "rand_xorshift", ] +[[package]] +name = "alloc_error_handler" +version = "0.0.0" +dependencies = [ + "alloc", + "compiler_builtins", + "core", +] + [[package]] name = "ammonia" version = "3.2.0" @@ -1830,6 +1839,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +[[package]] +name = "global_allocator" +version = "0.0.0" +dependencies = [ + "panic_abort", + "panic_unwind", + "std", +] + [[package]] name = "globset" version = "0.4.9" @@ -5533,9 +5551,11 @@ dependencies = [ name = "test" version = "0.0.0" dependencies = [ + "alloc_error_handler", "cfg-if", "core", "getopts", + "global_allocator", "libc", "panic_abort", "panic_unwind", diff --git a/compiler/rustc_builtin_macros/src/extern_allocator_crate.rs b/compiler/rustc_builtin_macros/src/extern_allocator_crate.rs new file mode 100644 index 0000000000000..6fee40281c81c --- /dev/null +++ b/compiler/rustc_builtin_macros/src/extern_allocator_crate.rs @@ -0,0 +1,45 @@ +// Code that injects an extern statement pointing to the `global_allocator` or +// `alloc_error_handler` sysroot crates. Should +// only be used when the `incomplete_dylib` compiler feature is enabled, an allocator is needed, +// and a global allocator or alloc error handler is not already found in the crate graph. + +// mattmatt could/should this be fully generic and used for other pre-existing extern imports as +// well or what + +use rustc_ast as ast; +use rustc_expand::base::{ExtCtxt, ResolverExpand}; +use rustc_expand::expand::{AstFragment, ExpansionConfig}; +use rustc_session::Session; +use rustc_span::edition::Edition::Edition2018; +use rustc_span::hygiene::AstPass; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::DUMMY_SP; +use smallvec::smallvec; +use thin_vec::thin_vec; + +pub fn inject( + sess: &Session, + resolver: &mut dyn ResolverExpand, + krate: &mut ast::Crate, + sym: Symbol, +) { + let ecfg = ExpansionConfig::default("allocator_crate_injection".to_string()); + let mut cx = ExtCtxt::new(sess, ecfg, resolver, None); + + let expn_id = cx.resolver.expansion_for_ast_pass(DUMMY_SP, AstPass::AllocatorCrates, &[], None); + + let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id()); + let call_site = DUMMY_SP.with_call_site_ctxt(expn_id.to_expn_id()); + let ident = if sess.parse_sess.edition >= Edition2018 { + Ident::new(sym, span) + } else { + Ident::new(sym, call_site) + }; + + let extern_stmt = cx.item(span, ident, thin_vec![], ast::ItemKind::ExternCrate(None)); + let fragment = AstFragment::Items(smallvec![extern_stmt]); + let expanded_fragment = + cx.monotonic_expander().fully_expand_fragment(fragment).make_items().pop().unwrap(); + + krate.items.insert(0, expanded_fragment); // mattmatt 0 may be wrong +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 75cfac7238485..2f8407b1fcc7d 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -50,6 +50,7 @@ mod util; pub mod asm; pub mod cmdline_attrs; +pub mod extern_allocator_crate; pub mod proc_macro_harness; pub mod standard_library_imports; pub mod test_harness; diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 2a373ebc1324d..8b462ae5ede23 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -403,6 +403,30 @@ pub fn configure_and_expand( }); } + if sess.opts.unstable_opts.unified_sysroot_injection { + sess.time("maybe_inject_allocator_crates", || { + let allocator_crates = resolver.crate_loader().find_allocator_crates(&krate); + if let Some((global_allocator, alloc_error_handler)) = allocator_crates { + if !global_allocator.is_some() { + rustc_builtin_macros::extern_allocator_crate::inject( + sess, + resolver, + &mut krate, + sym::global_allocator, + ) + } + if !alloc_error_handler.is_some() { + rustc_builtin_macros::extern_allocator_crate::inject( + sess, + resolver, + &mut krate, + sym::alloc_error_handler, + ) + } + } + }); + } + // Done with macro expansion! if sess.opts.unstable_opts.input_stats { diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 21652063b4716..a5c154241ed9c 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -800,7 +800,13 @@ impl<'a> CrateLoader<'a> { } } - fn inject_allocator_crate(&mut self, krate: &ast::Crate) { + pub fn find_allocator_crates( + &mut self, + krate: &ast::Crate, + ) -> Option<(Option, Option)> { + // First we check if the local crate defines a global allocator or allocator error handler. + // mattmatt I extracted this logic to a separate function but don't want to get rid of this + // side effect. Not sure if new behavior needs it, but old behavior had it self.cstore.has_global_allocator = match &*global_allocator_spans(&self.sess, krate) { [span1, span2, ..] => { self.sess.emit_err(errors::NoMultipleGlobalAlloc { span2: *span2, span1: *span1 }); @@ -823,7 +829,7 @@ impl<'a> CrateLoader<'a> { if !self.sess.contains_name(&krate.attrs, sym::needs_allocator) && !self.cstore.iter_crate_data().any(|(_, data)| data.needs_allocator()) { - return; + return None; } // At this point we've determined that we need an allocator. Let's see @@ -831,13 +837,18 @@ impl<'a> CrateLoader<'a> { // we're emitting. let all_rlib = self.sess.crate_types().iter().all(|ct| matches!(*ct, CrateType::Rlib)); if all_rlib { - return; + return None; + } + + // When this flag is enabled, we always use a global allocator. Either one is + // provided by the user, or we inject a dependency on the `global_allocator` sysroot crate. + // If the `#[global_allocator]` attribute is made to generate `__rustc_*` symbols directly, + // this can be removed. + if self.sess.opts.unstable_opts.unified_sysroot_injection { + self.cstore.allocator_kind = Some(AllocatorKind::Global); + self.cstore.alloc_error_handler_kind = Some(AllocatorKind::Global); } - // Ok, we need an allocator. Not only that but we're actually going to - // create an artifact that needs one linked in. Let's go find the one - // that we're going to link in. - // // First up we check for global allocators. Look at the crate graph here // and see what's a global allocator, including if we ourselves are a // global allocator. @@ -872,27 +883,39 @@ impl<'a> CrateLoader<'a> { } } - if global_allocator.is_some() { - self.cstore.allocator_kind = Some(AllocatorKind::Global); - } else { - // Ok we haven't found a global allocator but we still need an - // allocator. At this point our allocator request is typically fulfilled - // by the standard library, denoted by the `#![default_lib_allocator]` - // attribute. - if !self.sess.contains_name(&krate.attrs, sym::default_lib_allocator) - && !self.cstore.iter_crate_data().any(|(_, data)| data.has_default_lib_allocator()) - { - self.sess.emit_err(errors::GlobalAllocRequired); + Some((global_allocator, alloc_error_handler)) + } + + /// Determines whether an allocator shim is required and, if so, what type. The shim will be + /// generated later during codegen and included in linked output. This does not run if the + /// `-Z unified_sysroot_injection` flag is enabled. + fn inject_allocator_crate(&mut self, krate: &ast::Crate) { + if let Some((global_allocator, alloc_error_handler)) = self.find_allocator_crates(krate) { + if global_allocator.is_some() { + self.cstore.allocator_kind = Some(AllocatorKind::Global); + } else { + // Ok we haven't found a global allocator but we still need an + // allocator. At this point our allocator request is typically fulfilled + // by the standard library, denoted by the `#![default_lib_allocator]` + // attribute. + if !self.sess.contains_name(&krate.attrs, sym::default_lib_allocator) + && !self + .cstore + .iter_crate_data() + .any(|(_, data)| data.has_default_lib_allocator()) + { + self.sess.emit_err(errors::GlobalAllocRequired); + } + self.cstore.allocator_kind = Some(AllocatorKind::Default); } - self.cstore.allocator_kind = Some(AllocatorKind::Default); - } - if alloc_error_handler.is_some() { - self.cstore.alloc_error_handler_kind = Some(AllocatorKind::Global); - } else { - // The alloc crate provides a default allocation error handler if - // one isn't specified. - self.cstore.alloc_error_handler_kind = Some(AllocatorKind::Default); + if alloc_error_handler.is_some() { + self.cstore.alloc_error_handler_kind = Some(AllocatorKind::Global); + } else { + // The alloc crate provides a default allocation error handler if + // one isn't specified. + self.cstore.alloc_error_handler_kind = Some(AllocatorKind::Default); + } } } @@ -974,7 +997,13 @@ impl<'a> CrateLoader<'a> { pub fn postprocess(&mut self, krate: &ast::Crate) { self.inject_profiler_runtime(krate); - self.inject_allocator_crate(krate); + + // When this flag is enabled, these injections happen during AST expansion alongside + // injections of std/core, test, and proc_macro. + if !self.sess.opts.unstable_opts.unified_sysroot_injection { + self.inject_allocator_crate(krate); + } + self.inject_panic_runtime(krate); self.report_unused_deps(krate); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 0db4d85ff4b67..4e2c202eca1de 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1650,6 +1650,8 @@ options! { "select processor to schedule for (`rustc --print target-cpus` for details)"), ui_testing: bool = (false, parse_bool, [UNTRACKED], "emit compiler diagnostics in a form suitable for UI testing (default: no)"), + unified_sysroot_injection: bool = (false, parse_bool, [TRACKED], + "inject allocator and panic dependencies into the AST rather than ad-hoc"), uninit_const_chunk_threshold: usize = (16, parse_number, [TRACKED], "allow generating const initializers with mixed init/uninit chunks, \ and set the maximum number of chunks for which this is allowed (default: 16)"), diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index dee823eefde68..9333a391258e0 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1118,6 +1118,7 @@ impl MacroKind { #[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] pub enum AstPass { StdImports, + AllocatorCrates, TestHarness, ProcMacroHarness, } @@ -1126,6 +1127,7 @@ impl AstPass { pub fn descr(self) -> &'static str { match self { AstPass::StdImports => "standard library imports", + AstPass::AllocatorCrates => "injected allocator crates", AstPass::TestHarness => "test harness", AstPass::ProcMacroHarness => "proc macro harness", } diff --git a/library/alloc_error_handler/Cargo.toml b/library/alloc_error_handler/Cargo.toml new file mode 100644 index 0000000000000..85de432627bfd --- /dev/null +++ b/library/alloc_error_handler/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "alloc_error_handler" +version = "0.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" +description = "The Rust Standard Library - Default alloc error handler" +edition = "2021" + +[lib] +crate-type = ["rlib"] # mattmatt add dylib after dylib changes in? + +[dependencies] +alloc = { path = "../alloc" } +compiler_builtins = { version = "0.1.40", features = ['rustc-dep-of-std'] } +core = { path = "../core" } + +[features] +compiler-builtins-mem = ['compiler_builtins/mem'] +compiler-builtins-c = ["compiler_builtins/c"] +compiler-builtins-no-asm = ["compiler_builtins/no-asm"] +compiler-builtins-mangled-names = ["compiler_builtins/mangled-names"] diff --git a/library/alloc_error_handler/build.rs b/library/alloc_error_handler/build.rs new file mode 100644 index 0000000000000..8b1a06ee750fb --- /dev/null +++ b/library/alloc_error_handler/build.rs @@ -0,0 +1,52 @@ +use std::env; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + let target = env::var("TARGET").expect("TARGET was not set"); + if target.contains("freebsd") { + if env::var("RUST_STD_FREEBSD_12_ABI").is_ok() { + println!("cargo:rustc-cfg=freebsd12"); + } + } else if target.contains("linux") + || target.contains("netbsd") + || target.contains("dragonfly") + || target.contains("openbsd") + || target.contains("solaris") + || target.contains("illumos") + || target.contains("apple-darwin") + || target.contains("apple-ios") + || target.contains("apple-watchos") + || target.contains("uwp") + || target.contains("windows") + || target.contains("fuchsia") + || (target.contains("sgx") && target.contains("fortanix")) + || target.contains("hermit") + || target.contains("l4re") + || target.contains("redox") + || target.contains("haiku") + || target.contains("vxworks") + || target.contains("wasm32") + || target.contains("wasm64") + || target.contains("asmjs") + || target.contains("espidf") + || target.contains("solid") + || target.contains("nintendo-3ds") + { + // These platforms don't have any special requirements. + } else { + // This is for Cargo's build-std support, to mark std as unstable for + // typically no_std platforms. + // This covers: + // - os=none ("bare metal" targets) + // - mipsel-sony-psp + // - nvptx64-nvidia-cuda + // - arch=avr + // - tvos (aarch64-apple-tvos, x86_64-apple-tvos) + // - uefi (x86_64-unknown-uefi, i686-unknown-uefi) + // - JSON targets + // - Any new targets that have not been explicitly added above. + println!("cargo:rustc-cfg=feature=\"restricted-std\""); + } + println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap()); + println!("cargo:rustc-cfg=backtrace_in_libstd"); +} diff --git a/library/alloc_error_handler/src/lib.rs b/library/alloc_error_handler/src/lib.rs new file mode 100644 index 0000000000000..ce2bc49250a68 --- /dev/null +++ b/library/alloc_error_handler/src/lib.rs @@ -0,0 +1,31 @@ +#![feature(alloc_error_handler)] +#![feature(core_panic)] +#![no_std] + +use alloc::alloc::Layout; + +// mattmatt below is __rdl_oom() which i guess is just for no_std crates? +// library/std/src/alloc.rs has a #[alloc_error_handler] impl which has runtime hooks +// +// probably would need to supply normal crates with the hook behavior, but would need to supply the +// __rdl_oom impl to no_std crates. either keep the bad codegen shim or inject a different extern +// statement depending on whether local crate is no_std + +#[alloc_error_handler] +pub unsafe fn on_oom(layout: Layout) -> ! { + extern "Rust" { + // This symbol is emitted by rustc next to __rust_alloc_error_handler. + // Its value depends on the -Zoom={panic,abort} compiler option. + static __rust_alloc_error_handler_should_panic: u8; + } + + #[allow(unused_unsafe)] + if unsafe { __rust_alloc_error_handler_should_panic != 0 } { + panic!("memory allocation of {} bytes failed", layout.size()) + } else { + core::panicking::panic_nounwind_fmt(format_args!( + "memory allocation of {} bytes failed", + layout.size() + )) + } +} diff --git a/library/global_allocator/Cargo.toml b/library/global_allocator/Cargo.toml new file mode 100644 index 0000000000000..9c63687dc8cff --- /dev/null +++ b/library/global_allocator/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "global_allocator" +version = "0.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" +description = "The Rust Standard Library - Default global allocator" +edition = "2021" + +[lib] +crate-type = ["rlib"] # mattmatt add dylib after dylib changes in + +[dependencies] +std = { path = "../std" } +panic_unwind = { path = "../panic_unwind" } # mattmatt why can't i make this optional +panic_abort = { path = "../panic_abort" } # panics should be unnecessary after dylib change diff --git a/library/global_allocator/build.rs b/library/global_allocator/build.rs new file mode 100644 index 0000000000000..8b1a06ee750fb --- /dev/null +++ b/library/global_allocator/build.rs @@ -0,0 +1,52 @@ +use std::env; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + let target = env::var("TARGET").expect("TARGET was not set"); + if target.contains("freebsd") { + if env::var("RUST_STD_FREEBSD_12_ABI").is_ok() { + println!("cargo:rustc-cfg=freebsd12"); + } + } else if target.contains("linux") + || target.contains("netbsd") + || target.contains("dragonfly") + || target.contains("openbsd") + || target.contains("solaris") + || target.contains("illumos") + || target.contains("apple-darwin") + || target.contains("apple-ios") + || target.contains("apple-watchos") + || target.contains("uwp") + || target.contains("windows") + || target.contains("fuchsia") + || (target.contains("sgx") && target.contains("fortanix")) + || target.contains("hermit") + || target.contains("l4re") + || target.contains("redox") + || target.contains("haiku") + || target.contains("vxworks") + || target.contains("wasm32") + || target.contains("wasm64") + || target.contains("asmjs") + || target.contains("espidf") + || target.contains("solid") + || target.contains("nintendo-3ds") + { + // These platforms don't have any special requirements. + } else { + // This is for Cargo's build-std support, to mark std as unstable for + // typically no_std platforms. + // This covers: + // - os=none ("bare metal" targets) + // - mipsel-sony-psp + // - nvptx64-nvidia-cuda + // - arch=avr + // - tvos (aarch64-apple-tvos, x86_64-apple-tvos) + // - uefi (x86_64-unknown-uefi, i686-unknown-uefi) + // - JSON targets + // - Any new targets that have not been explicitly added above. + println!("cargo:rustc-cfg=feature=\"restricted-std\""); + } + println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap()); + println!("cargo:rustc-cfg=backtrace_in_libstd"); +} diff --git a/library/global_allocator/src/lib.rs b/library/global_allocator/src/lib.rs new file mode 100644 index 0000000000000..82848c0611c95 --- /dev/null +++ b/library/global_allocator/src/lib.rs @@ -0,0 +1,16 @@ +use std::alloc::{GlobalAlloc, Layout, System}; + +struct DefaultAllocator; + +unsafe impl GlobalAlloc for DefaultAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + System.alloc(layout) + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + System.dealloc(ptr, layout) + } +} + +#[global_allocator] +static GLOBAL: DefaultAllocator = DefaultAllocator; diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index 61b6f33bce020..1465ab2486002 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -15,8 +15,10 @@ libc = { version = "0.2", default-features = false } panic_unwind = { path = "../panic_unwind" } panic_abort = { path = "../panic_abort" } -# not actually used but needed to always have proc_macro in the sysroot +# not actually used but needed to always have these in the sysroot proc_macro = { path = "../proc_macro" } +global_allocator = { path = "../global_allocator" } # mattmatt does this conflict with external allocator +alloc_error_handler = { path = "../alloc_error_handler"} # mattmatt does this conflict with external allocator # Forward features to the `std` crate as necessary [features] From c0a3c0a332ff417196af36a2fefd14be3c9ca15b Mon Sep 17 00:00:00 2001 From: Matt Hammerly Date: Sat, 25 Feb 2023 23:09:42 -0800 Subject: [PATCH 2/5] -Z incomplete_dylibs implemented for allocator crates --- compiler/rustc_codegen_ssa/src/base.rs | 10 ++++++++-- compiler/rustc_metadata/src/creader.rs | 9 +++++++-- compiler/rustc_session/src/options.rs | 2 ++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 02b502d948c2c..222244d46f4f0 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -620,14 +620,20 @@ pub fn codegen_crate( // 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 + // 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! + // + // *If `-Z incomplete_dylibs` is enabled, we can't assume that. Regular dylibs won't have the + // allocator shim but Cdylibs will. If your dependency graph contains a Cdylib (uncommon?) then + // you will get a duplicated allocator shim. If `-Z unified_sysroot_injection` becomes default + // and we make `#[global_allocator]` generate __rust_* directly, we can fully delete the + // codegen including this edge case / assumption. mattmatt 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 { + let allocator_module = if any_dynamic_crate && !tcx.sess.opts.unstable_opts.incomplete_dylibs { None } else if let Some(kind) = tcx.allocator_kind(()) { let llmod_id = diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index a5c154241ed9c..3a63be636d623 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -835,8 +835,13 @@ impl<'a> CrateLoader<'a> { // At this point we've determined that we need an allocator. Let's see // if our compilation session actually needs an allocator based on what // we're emitting. - let all_rlib = self.sess.crate_types().iter().all(|ct| matches!(*ct, CrateType::Rlib)); - if all_rlib { + let needs_allocator_predicate = if self.sess.opts.unstable_opts.incomplete_dylibs { + |ct: &CrateType| !matches!(*ct, CrateType::Rlib) && !matches!(*ct, CrateType::Dylib) + } else { + |ct: &CrateType| !matches!(*ct, CrateType::Rlib) + }; + let needs_allocator = self.sess.crate_types().iter().any(needs_allocator_predicate); + if !needs_allocator { return None; } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 4e2c202eca1de..3704da6a4096d 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1364,6 +1364,8 @@ options! { "generate human-readable, predictable names for codegen units (default: no)"), identify_regions: bool = (false, parse_bool, [UNTRACKED], "display unnamed regions as `'`, using a non-ident unique id (default: no)"), + incomplete_dylibs: bool = (false, parse_bool, [TRACKED], + "build dylib crates (not cdylib) without injecting allocator and panic implementations, mirroring rlib"), incremental_ignore_spans: bool = (false, parse_bool, [TRACKED], "ignore spans during ICH computation -- used for testing (default: no)"), incremental_info: bool = (false, parse_bool, [UNTRACKED], From 60905fa7bd9a10e59d203a6dcc5af9dd20ed13e7 Mon Sep 17 00:00:00 2001 From: Matt Hammerly Date: Sun, 26 Feb 2023 00:33:40 -0800 Subject: [PATCH 3/5] add unified-sysroot-injection cargo feature, gated allocator stuff behind it --- library/std/Cargo.toml | 1 + library/std/src/alloc.rs | 2 ++ library/std/src/lib.rs | 3 +-- library/test/Cargo.toml | 5 +++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index adf521d9b94a1..b790f87842c9b 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -74,6 +74,7 @@ panic_immediate_abort = ["core/panic_immediate_abort"] std_detect_file_io = ["std_detect/std_detect_file_io"] std_detect_dlsym_getauxval = ["std_detect/std_detect_dlsym_getauxval"] std_detect_env_override = ["std_detect/std_detect_env_override"] +unified-sysroot-injection = [] [package.metadata.fortanix-sgx] # Maximum possible number of threads when testing diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index c5a5991cc81c4..9e31fd1231c10 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -345,6 +345,7 @@ fn default_alloc_error_hook(layout: Layout) { } #[cfg(not(test))] +#[cfg(not(feature = "unified-sysroot-injection"))] #[doc(hidden)] #[alloc_error_handler] #[unstable(feature = "alloc_internals", issue = "none")] @@ -357,6 +358,7 @@ pub fn rust_oom(layout: Layout) -> ! { } #[cfg(not(test))] +#[cfg(not(feature = "unified-sysroot-injection"))] #[doc(hidden)] #[allow(unused_attributes)] #[unstable(feature = "alloc_internals", issue = "none")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 762f7a7c9a1a0..fa4ddca81c886 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -359,8 +359,7 @@ #![feature(const_ipv4)] #![feature(const_ipv6)] #![feature(thread_local_internals)] -// -#![default_lib_allocator] +#![cfg_attr(not(feature = "unified-sysroot-injection"), default_lib_allocator)] // Explicitly import the prelude. The compiler uses this same unstable attribute // to import the prelude implicitly when building crates that depend on std. diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index 1465ab2486002..708e8353079ed 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -17,8 +17,8 @@ panic_abort = { path = "../panic_abort" } # not actually used but needed to always have these in the sysroot proc_macro = { path = "../proc_macro" } -global_allocator = { path = "../global_allocator" } # mattmatt does this conflict with external allocator -alloc_error_handler = { path = "../alloc_error_handler"} # mattmatt does this conflict with external allocator +global_allocator = { path = "../global_allocator", optional = true } # mattmatt does this conflict with external allocator +alloc_error_handler = { path = "../alloc_error_handler", optional = true } # mattmatt does this conflict with external allocator # Forward features to the `std` crate as necessary [features] @@ -36,3 +36,4 @@ profiler = ["std/profiler"] std_detect_file_io = ["std/std_detect_file_io"] std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] std_detect_env_override = ["std/std_detect_env_override"] +unified-sysroot-injection = ["dep:global_allocator", "dep:alloc_error_handler", "std/unified-sysroot-injection"] From 59f0023a422f822ae5df8ea25c32987bdc46f9aa Mon Sep 17 00:00:00 2001 From: Matt Hammerly Date: Thu, 2 Mar 2023 15:14:36 -0800 Subject: [PATCH 4/5] defaults seem to override user-provideds, but no_std shared linkage works --- Cargo.lock | 14 ++ compiler/rustc/src/main.rs | 3 +- .../src/alloc_error_handler.rs | 3 +- .../src/extern_allocator_crate.rs | 213 +++++++++++++++++- .../src/global_allocator.rs | 5 +- compiler/rustc_codegen_llvm/src/allocator.rs | 5 +- .../locales/en-US/metadata.ftl | 10 + compiler/rustc_feature/src/active.rs | 2 + compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_interface/src/passes.rs | 15 ++ compiler/rustc_metadata/src/creader.rs | 71 ++++++ compiler/rustc_metadata/src/errors.rs | 17 ++ compiler/rustc_metadata/src/rmeta/decoder.rs | 8 + compiler/rustc_metadata/src/rmeta/encoder.rs | 1 + compiler/rustc_metadata/src/rmeta/mod.rs | 1 + compiler/rustc_passes/src/weak_lang_items.rs | 29 ++- compiler/rustc_session/src/options.rs | 4 +- compiler/rustc_span/src/symbol.rs | 1 + library/alloc_error_handler/Cargo.toml | 15 +- library/alloc_error_handler/src/lib.rs | 19 +- library/core/Cargo.toml | 3 + library/core/src/lib.rs | 12 + library/global_allocator/Cargo.toml | 11 +- library/global_allocator/src/lib.rs | 4 + library/panic_abort/src/lib.rs | 2 + library/panic_handler/Cargo.toml | 21 ++ library/panic_handler/build.rs | 52 +++++ library/panic_handler/src/lib.rs | 97 ++++++++ library/panic_unwind/src/lib.rs | 2 + library/std/Cargo.toml | 5 +- library/std/src/alloc.rs | 4 +- library/std/src/lib.rs | 12 +- library/std/src/panicking.rs | 4 +- library/std/src/sys_common/backtrace.rs | 1 + library/test/Cargo.toml | 11 +- 35 files changed, 640 insertions(+), 38 deletions(-) create mode 100644 library/panic_handler/Cargo.toml create mode 100644 library/panic_handler/build.rs create mode 100644 library/panic_handler/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 2887ba4f98ae6..d8370d5663747 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -72,6 +72,9 @@ dependencies = [ "alloc", "compiler_builtins", "core", + "panic_abort", + "panic_unwind", + "std", ] [[package]] @@ -2948,6 +2951,16 @@ dependencies = [ "libc", ] +[[package]] +name = "panic_handler" +version = "0.0.0" +dependencies = [ + "core", + "panic_abort", + "panic_unwind", + "std", +] + [[package]] name = "panic_unwind" version = "0.0.0" @@ -5558,6 +5571,7 @@ dependencies = [ "global_allocator", "libc", "panic_abort", + "panic_handler", "panic_unwind", "proc_macro", "std", diff --git a/compiler/rustc/src/main.rs b/compiler/rustc/src/main.rs index e21c9b6604440..3bd3347e60d62 100644 --- a/compiler/rustc/src/main.rs +++ b/compiler/rustc/src/main.rs @@ -30,7 +30,8 @@ fn main() { // See the comment at the top of this file for an explanation of this. #[cfg(feature = "jemalloc-sys")] { - use std::os::raw::{c_int, c_void}; + use core::ffi::c_void; + use std::os::raw::c_int; #[used] static _F1: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::calloc; diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index dcf500ddbd3c8..0e282dc4d8f28 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -86,7 +86,8 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span body, })); - let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)]; +// let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)]; + let attrs = thin_vec![cx.attr_word(sym::no_mangle, span)]; let item = cx.item(span, Ident::from_str_and_span("__rg_oom", span), attrs, kind); cx.stmt_item(sig_span, item) diff --git a/compiler/rustc_builtin_macros/src/extern_allocator_crate.rs b/compiler/rustc_builtin_macros/src/extern_allocator_crate.rs index 6fee40281c81c..975ca183d2eb3 100644 --- a/compiler/rustc_builtin_macros/src/extern_allocator_crate.rs +++ b/compiler/rustc_builtin_macros/src/extern_allocator_crate.rs @@ -6,34 +6,50 @@ // mattmatt could/should this be fully generic and used for other pre-existing extern imports as // well or what +#![allow(unused)] + use rustc_ast as ast; +use rustc_ast::ast::StrStyle; +use rustc_ast::expand::allocator::{ + AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS, +}; +use rustc_ast::ptr::P; +use rustc_ast::{ + Fn, FnHeader, FnSig, ForeignItem, ForeignItemKind, ForeignMod, Generics, Mutability, + Param, StrLit, Ty, TyKind, Unsafe, +}; use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_session::Session; use rustc_span::edition::Edition::Edition2018; use rustc_span::hygiene::AstPass; -use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::DUMMY_SP; +use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::{Span, DUMMY_SP}; use smallvec::smallvec; +// use std::vec; use thin_vec::thin_vec; pub fn inject( sess: &Session, resolver: &mut dyn ResolverExpand, krate: &mut ast::Crate, - sym: Symbol, + sym_name: Symbol, ) { let ecfg = ExpansionConfig::default("allocator_crate_injection".to_string()); let mut cx = ExtCtxt::new(sess, ecfg, resolver, None); let expn_id = cx.resolver.expansion_for_ast_pass(DUMMY_SP, AstPass::AllocatorCrates, &[], None); + // mattmatt i am guessing i want to inject extern definitions for the allocator functions. we + // codegen the allocator in, say, the bin, but the definitions are found in the + // global_allocator.so (or .rlib) + let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id()); let call_site = DUMMY_SP.with_call_site_ctxt(expn_id.to_expn_id()); let ident = if sess.parse_sess.edition >= Edition2018 { - Ident::new(sym, span) + Ident::new(sym_name, span) } else { - Ident::new(sym, call_site) + Ident::new(sym_name, call_site) }; let extern_stmt = cx.item(span, ident, thin_vec![], ast::ItemKind::ExternCrate(None)); @@ -41,5 +57,192 @@ pub fn inject( let expanded_fragment = cx.monotonic_expander().fully_expand_fragment(fragment).make_items().pop().unwrap(); + println!("mattmatt injecting {}", sym_name); krate.items.insert(0, expanded_fragment); // mattmatt 0 may be wrong + + /* + if sym_name == sym::global_allocator { + println!("mattmatt trying to make extern module lol"); + // inject extern decls for the allocator functions so that when we dynamically link the + // compiler will expect them to be found elsewhere + // extern "Rust" { + // fn __rg_alloc(); + // fn __rg_realloc(); + // fn __rg_dealloc(); + // etc + // } + + let factory = AllocFnFactory { span, kind: AllocatorKind::Global, cx: &cx }; + println!("mattmatt factory created"); + + let items = ALLOCATOR_METHODS.iter().map(|method| factory.allocator_fn(method)).collect(); + println!("mattmatt allocator ForeignItems created"); + + let abi = Symbol::intern("Rust"); + println!("mattmatt extern Rust symbol created"); + let fmod = ast::ItemKind::ForeignMod(ForeignMod { + unsafety: Unsafe::Yes(span), + abi: Some(StrLit { + symbol: abi, + suffix: None, + symbol_unescaped: abi, + style: StrStyle::Cooked, + span: span, + }), + items: items, + }); + println!("mattmatt foreign module created"); + + let fmod_item = cx.item(span, Ident::new(kw::Underscore, span), thin_vec![], fmod); + println!("mattmatt injecting allocator externs"); + + let fmod_fragment = AstFragment::Items(smallvec![fmod_item]); + let expanded_fmod_fragment = + cx.monotonic_expander().fully_expand_fragment(fmod_fragment).make_items().pop().unwrap(); + krate.items.push(expanded_fmod_fragment); + } + */ +} + +struct AllocFnFactory<'a, 'b> { + span: Span, + kind: AllocatorKind, + cx: &'b ExtCtxt<'a>, +} + +impl AllocFnFactory<'_, '_> { + fn allocator_fn(&self, method: &AllocatorMethod) -> P { + println!("mattmatt generating ForeignItem for {}", self.kind.fn_name(method.name)); + let mut abi_args = Vec::new(); + let mut i = 0; + let mut mk = || { + let name = Ident::from_str_and_span(&format!("arg{}", i), self.span); + i += 1; + name + }; + println!("mattmatt about to populate abi_args"); + let _args: Vec<()> = + method.inputs.iter().map(|ty| self.arg_ty(ty, &mut abi_args, &mut mk)).collect(); + + println!("mattmatt populated abi_args"); + let output_ty = self.ret_ty(&method.output); + println!("mattmatt got return type"); + let decl = self.cx.fn_decl(abi_args, ast::FnRetTy::Ty(output_ty)); + println!("mattmatt got fn declaration"); + let header = FnHeader { ..FnHeader::default() }; + let sig = FnSig { decl, header, span: self.span }; + println!("mattmatt got fn signature"); + let body = None; + + let kind = ForeignItemKind::Fn(Box::new(Fn { + defaultness: ast::Defaultness::Final, + sig, + generics: Generics::default(), + body, + })); + println!("mattmatt made ForeignItemKind"); + let foreign_item = P(ast::Item { + ident: Ident::from_str_and_span(&self.kind.fn_name(method.name), self.span), + attrs: thin_vec![], + id: ast::DUMMY_NODE_ID, + kind: kind, + vis: ast::Visibility { + span: self.span.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, // mattmatt maybe make this public + tokens: None, + }, + span: self.span, + tokens: None, + }); + println!("mattmatt made ForeignItem"); + + foreign_item + } + + fn arg_ty( + &self, + ty: &AllocatorTy, + args: &mut Vec, + ident: &mut dyn FnMut() -> Ident, + ) { +// ) -> P { + match *ty { + AllocatorTy::Layout => { + println!("mattmatt layout return type"); + let usize = self.cx.path_ident(self.span, Ident::new(sym::usize, self.span)); + let ty_usize = self.cx.ty_path(usize); + let size = ident(); + let align = ident(); + println!("mattmatt about to push args"); + args.push(self.cx.param(self.span, size, ty_usize.clone())); + args.push(self.cx.param(self.span, align, ty_usize)); + println!("mattmatt layout pushed to args"); + + /* + let layout_new = + self.cx.std_path(&[sym::alloc, sym::Layout, sym::from_size_align_unchecked]); + println!("mattmatt std_path done"); + let layout_new = self.cx.expr_path(self.cx.path(self.span, layout_new)); + println!("mattmatt expr_path"); + let size = self.cx.expr_ident(self.span, size); + println!("mattmatt first expr_ident"); + let align = self.cx.expr_ident(self.span, align); + println!("mattmatt second expr_ident"); + let layout = self.cx.expr_call(self.span, layout_new, vec![size, align]); + println!("matt returning after expr_call"); + layout + */ + } + + AllocatorTy::Ptr => { + println!("mattmatt ptr rreturn type"); + let ident = ident(); + args.push(self.cx.param(self.span, ident, self.ptr_u8())); + println!("mattmatt pushed to args"); + /* + let arg = self.cx.expr_ident(self.span, ident); + println!("mattmatt expr_ident, about to return expr_cast"); + self.cx.expr_cast(self.span, arg, self.ptr_u8()) + */ + } + + AllocatorTy::Usize => { + println!("mattmatt usize reutrn type"); + let ident = ident(); + args.push(self.cx.param(self.span, ident, self.usize())); + println!("mattmatt pushed to args, about to reutnr expr_ident"); + /* + self.cx.expr_ident(self.span, ident) + */ + } + + AllocatorTy::ResultPtr | AllocatorTy::Unit => { + println!("mattmatt about to panic"); + panic!("can't convert AllocatorTy to an argument") + } + } + } + + fn ret_ty(&self, ty: &AllocatorTy) -> P { + match *ty { + AllocatorTy::ResultPtr => self.ptr_u8(), + + AllocatorTy::Unit => self.cx.ty(self.span, TyKind::Tup(Vec::new())), + + AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { + panic!("can't convert `AllocatorTy` to an output") + } + } + } + + fn usize(&self) -> P { + let usize = self.cx.path_ident(self.span, Ident::new(sym::usize, self.span)); + self.cx.ty_path(usize) + } + + fn ptr_u8(&self) -> P { + let u8 = self.cx.path_ident(self.span, Ident::new(sym::u8, self.span)); + let ty_u8 = self.cx.ty_path(u8); + self.cx.ty_ptr(self.span, ty_u8, Mutability::Mut) + } } diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index f8761653bf5b7..4e85b45457037 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -111,7 +111,10 @@ impl AllocFnFactory<'_, '_> { } fn attrs(&self) -> AttrVec { - thin_vec![self.cx.attr_word(sym::rustc_std_internal_symbol, self.span)] +// thin_vec![self.cx.attr_word(sym::rustc_std_internal_symbol, self.span)] + thin_vec![self.cx.attr_word(sym::no_mangle, self.span)] // mattmatt maybe the + // internal symbol thing messes + // with shared linkage } fn arg_ty( diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index 668d929270530..b5fa140790978 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -71,7 +71,8 @@ pub(crate) unsafe fn codegen( let callee = kind.fn_name(method.name); let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); - llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); + llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Default); // mattmatt originally + // hidden let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); @@ -122,7 +123,7 @@ pub(crate) unsafe fn codegen( let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); // -> ! DIFlagNoReturn attributes::apply_to_llfn(callee, llvm::AttributePlace::Function, &[no_return]); - llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); + llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Default); // mattmatt originally hidden let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); diff --git a/compiler/rustc_error_messages/locales/en-US/metadata.ftl b/compiler/rustc_error_messages/locales/en-US/metadata.ftl index 79b8b41725704..b8d9c96d0e105 100644 --- a/compiler/rustc_error_messages/locales/en-US/metadata.ftl +++ b/compiler/rustc_error_messages/locales/en-US/metadata.ftl @@ -148,6 +148,13 @@ metadata_profiler_builtins_needs_core = metadata_not_profiler_runtime = the crate `{$crate_name}` is not a profiler runtime +metadata_no_multiple_panic_handlers = + cannot define multiple panic handlers + .label = cannot define a new panic handler + +metadata_prev_panic_handlers = + previous panic handler defined here + metadata_no_multiple_global_alloc = cannot define multiple global allocators .label = cannot define a new global allocator @@ -162,6 +169,9 @@ metadata_no_multiple_alloc_error_handler = metadata_prev_alloc_error_handler = previous allocation error handler defined here +metadata_conflicting_panic_handler = + the `#[panic_handler]` in {$other_crate_name} conflicts with panic handler in: {$crate_name} + metadata_conflicting_global_alloc = the `#[global_allocator]` in {$other_crate_name} conflicts with global allocator in: {$crate_name} diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 323f5a368fc09..1b16bf5a8ea8a 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -213,6 +213,8 @@ declare_features! ( (active, link_llvm_intrinsics, "1.0.0", Some(29602), None), /// Allows using the `#[linkage = ".."]` attribute. (active, linkage, "1.0.0", Some(29603), None), + /// Allows declaring with `#![needs_panic_handler]` that a panic handler is needed. + (active, needs_panic_handler, "1.69.0", None, None), // mattmatt needs tracking issue /// Allows declaring with `#![needs_panic_runtime]` that a panic runtime is needed. (active, needs_panic_runtime, "1.10.0", Some(32837), None), /// Allows using the `#![panic_runtime]` attribute. diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index af56a0b245987..c7e80061f9e1b 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -566,6 +566,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ needs_panic_runtime, Normal, template!(Word), WarnFollowing, experimental!(needs_panic_runtime) ), + gated!(needs_panic_handler, Normal, template!(Word), WarnFollowing, experimental!(needs_panic_handler)), gated!( compiler_builtins, Normal, template!(Word), WarnFollowing, "the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \ diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 8b462ae5ede23..f1ba89ee1dd99 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -425,6 +425,21 @@ pub fn configure_and_expand( } } }); + + sess.time("maybe_inject_panic_handler", || { + let panic_handler = resolver.crate_loader().find_panic_handler(&krate); + if let Some(panic_handler) = panic_handler { + if !panic_handler.is_some() { + // mattmatt no reason this needs to be allocator-specific + rustc_builtin_macros::extern_allocator_crate::inject( + sess, + resolver, + &mut krate, + sym::panic_handler, + ) + } + } + }); } // Done with macro expansion! diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 3a63be636d623..c4080ef31d1ad 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -700,9 +700,59 @@ impl<'a> CrateLoader<'a> { Ok(unsafe { **sym }) } + pub fn find_panic_handler(&self, krate: &ast::Crate) -> Option> { + let needs_panic_handler_predicate = if self.sess.opts.unstable_opts.incomplete_dylibs { + |ct: &CrateType| !matches!(*ct, CrateType::Rlib) && !matches!(*ct, CrateType::Dylib) + } else { + |ct: &CrateType| !matches!(*ct, CrateType::Rlib) + }; + let mut needs_panic_handler = + self.sess.crate_types().iter().any(needs_panic_handler_predicate); + if !needs_panic_handler { + return None; + } + + needs_panic_handler = self.sess.contains_name(&krate.attrs, sym::needs_panic_handler); + for (_, data) in self.cstore.iter_crate_data() { + if data.needs_panic_handler() {} + needs_panic_handler = needs_panic_handler || data.needs_panic_handler(); + } + + if !needs_panic_handler { + return None; + } + + let has_panic_handler = match &*panic_handler_spans(&self.sess, krate) { + [span1, span2, ..] => { + self.sess + .emit_err(errors::NoMultiplePanicHandlers { span2: *span2, span1: *span1 }); + true + } + spans => !spans.is_empty(), + }; + + let mut panic_handler = has_panic_handler.then(|| Symbol::intern("this crate")); + for (_, data) in self.cstore.iter_crate_data() { + if data.has_panic_handler() { + match panic_handler { + Some(other_crate) => { + self.sess.emit_err(errors::ConflictingPanicHandler { + crate_name: data.name(), + other_crate_name: other_crate, + }); + } + None => panic_handler = Some(data.name()), + } + } + } + + Some(panic_handler) + } + fn inject_panic_runtime(&mut self, krate: &ast::Crate) { // If we're only compiling an rlib, then there's no need to select a // panic runtime, so we just skip this section entirely. + // mattmatt this probably wants the incomplete dylibs logic let any_non_rlib = self.sess.crate_types().iter().any(|ct| *ct != CrateType::Rlib); if !any_non_rlib { info!("panic runtime injection skipped, only generating rlib"); @@ -1007,8 +1057,10 @@ impl<'a> CrateLoader<'a> { // injections of std/core, test, and proc_macro. if !self.sess.opts.unstable_opts.unified_sysroot_injection { self.inject_allocator_crate(krate); + // self.inject_panic_runtime(krate); } + // inject a panic runtime dependency into a panic handler if necessary self.inject_panic_runtime(krate); self.report_unused_deps(krate); @@ -1080,6 +1132,25 @@ impl<'a> CrateLoader<'a> { } } +fn panic_handler_spans(sess: &Session, krate: &ast::Crate) -> Vec { + struct Finder<'a> { + sess: &'a Session, + spans: Vec, + } + impl<'ast, 'a> visit::Visitor<'ast> for Finder<'a> { + fn visit_item(&mut self, item: &'ast ast::Item) { + if self.sess.contains_name(&item.attrs, sym::panic_handler) { + self.spans.push(item.span); + } + visit::walk_item(self, item) + } + } + + let mut f = Finder { sess, spans: Vec::new() }; + visit::walk_crate(&mut f, krate); + f.spans +} + fn global_allocator_spans(sess: &Session, krate: &ast::Crate) -> Vec { struct Finder<'a> { sess: &'a Session, diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index 02c03114eb67f..fe3f8109800cf 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -341,6 +341,16 @@ pub struct NotProfilerRuntime { pub crate_name: Symbol, } +#[derive(Diagnostic)] +#[diag(metadata_no_multiple_panic_handlers)] +pub struct NoMultiplePanicHandlers { + #[primary_span] + #[label] + pub span2: Span, + #[label(metadata_prev_panic_handlers)] + pub span1: Span, +} + #[derive(Diagnostic)] #[diag(metadata_no_multiple_global_alloc)] pub struct NoMultipleGlobalAlloc { @@ -361,6 +371,13 @@ pub struct NoMultipleAllocErrorHandler { pub span1: Span, } +#[derive(Diagnostic)] +#[diag(metadata_conflicting_panic_handler)] +pub struct ConflictingPanicHandler { + pub crate_name: Symbol, + pub other_crate_name: Symbol, +} + #[derive(Diagnostic)] #[diag(metadata_conflicting_global_alloc)] pub struct ConflictingGlobalAlloc { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index bb2dd290c6d5d..f64106dcf635e 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -1697,6 +1697,14 @@ impl CrateMetadata { self.root.required_panic_strategy } + pub(crate) fn needs_panic_handler(&self) -> bool { + self.root.needs_panic_handler + } + + pub(crate) fn has_panic_handler(&self) -> bool { + self.root.has_panic_handler + } + pub(crate) fn needs_panic_runtime(&self) -> bool { self.root.needs_panic_runtime } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 9d8f14058f681..ed0de2492c660 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -679,6 +679,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { debugger_visualizers, compiler_builtins: tcx.sess.contains_name(&attrs, sym::compiler_builtins), needs_allocator: tcx.sess.contains_name(&attrs, sym::needs_allocator), + needs_panic_handler: tcx.sess.contains_name(&attrs, sym::needs_panic_handler), needs_panic_runtime: tcx.sess.contains_name(&attrs, sym::needs_panic_runtime), no_builtins: tcx.sess.contains_name(&attrs, sym::no_builtins), panic_runtime: tcx.sess.contains_name(&attrs, sym::panic_runtime), diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 37af9e64e9a3d..b6fb2aec7007e 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -257,6 +257,7 @@ pub(crate) struct CrateRoot { compiler_builtins: bool, needs_allocator: bool, + needs_panic_handler: bool, needs_panic_runtime: bool, no_builtins: bool, panic_runtime: bool, diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index fc6372cf99ee8..24532be804062 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -42,15 +42,26 @@ pub fn check_crate(tcx: TyCtxt<'_>, items: &mut lang_items::LanguageItems) { fn verify(tcx: TyCtxt<'_>, items: &lang_items::LanguageItems) { // We only need to check for the presence of weak lang items if we're - // emitting something that's not an rlib. - let needs_check = tcx.sess.crate_types().iter().any(|kind| match *kind { - CrateType::Dylib - | CrateType::ProcMacro - | CrateType::Cdylib - | CrateType::Executable - | CrateType::Staticlib => true, - CrateType::Rlib => false, - }); + // emitting something that's not an rlib. If `-Z incomplete_dylibs` is enabled, we also don't + // need to check dylibs. + let needs_check = if !tcx.sess.opts.unstable_opts.incomplete_dylibs { + tcx.sess.crate_types().iter().any(|kind| match *kind { + CrateType::Dylib + | CrateType::ProcMacro + | CrateType::Cdylib + | CrateType::Executable + | CrateType::Staticlib => true, + CrateType::Rlib => false, + }) + } else { + tcx.sess.crate_types().iter().any(|kind| match *kind { + CrateType::ProcMacro + | CrateType::Cdylib + | CrateType::Executable + | CrateType::Staticlib => true, + CrateType::Rlib | CrateType::Dylib => false, + }) + }; if !needs_check { return; } diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 3704da6a4096d..d379b3b71480a 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1364,7 +1364,7 @@ options! { "generate human-readable, predictable names for codegen units (default: no)"), identify_regions: bool = (false, parse_bool, [UNTRACKED], "display unnamed regions as `'`, using a non-ident unique id (default: no)"), - incomplete_dylibs: bool = (false, parse_bool, [TRACKED], + incomplete_dylibs: bool = (true, parse_bool, [TRACKED], "build dylib crates (not cdylib) without injecting allocator and panic implementations, mirroring rlib"), incremental_ignore_spans: bool = (false, parse_bool, [TRACKED], "ignore spans during ICH computation -- used for testing (default: no)"), @@ -1652,7 +1652,7 @@ options! { "select processor to schedule for (`rustc --print target-cpus` for details)"), ui_testing: bool = (false, parse_bool, [UNTRACKED], "emit compiler diagnostics in a form suitable for UI testing (default: no)"), - unified_sysroot_injection: bool = (false, parse_bool, [TRACKED], + unified_sysroot_injection: bool = (true, parse_bool, [TRACKED], "inject allocator and panic dependencies into the AST rather than ad-hoc"), uninit_const_chunk_threshold: usize = (16, parse_number, [TRACKED], "allow generating const initializers with mixed init/uninit chunks, \ diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index f1119214be44d..21bbe03c981ad 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -973,6 +973,7 @@ symbols! { nearbyintf64, needs_allocator, needs_drop, + needs_panic_handler, needs_panic_runtime, neg, negate_unsigned, diff --git a/library/alloc_error_handler/Cargo.toml b/library/alloc_error_handler/Cargo.toml index 85de432627bfd..f7588ed3eb2f8 100644 --- a/library/alloc_error_handler/Cargo.toml +++ b/library/alloc_error_handler/Cargo.toml @@ -7,15 +7,28 @@ description = "The Rust Standard Library - Default alloc error handler" edition = "2021" [lib] -crate-type = ["rlib"] # mattmatt add dylib after dylib changes in? +crate-type = ["rlib", "dylib"] [dependencies] +# we want this to be no_std, but we need to build with std while bootstrapping. +# after bootstrapping, with no_std on, std will not be externed/used even with a dep +std = { path = "../std" } + +# panic stuff is just a silly dance i have to do in this prototype +panic_unwind = { path = "../panic_unwind", optional = true } +panic_abort = { path = "../panic_abort" } + alloc = { path = "../alloc" } compiler_builtins = { version = "0.1.40", features = ['rustc-dep-of-std'] } core = { path = "../core" } [features] +default = ["unified-sysroot-injection"] + compiler-builtins-mem = ['compiler_builtins/mem'] compiler-builtins-c = ["compiler_builtins/c"] compiler-builtins-no-asm = ["compiler_builtins/no-asm"] compiler-builtins-mangled-names = ["compiler_builtins/mangled-names"] + +unified-sysroot-injection = [] +panic-unwind = ["panic_unwind"] diff --git a/library/alloc_error_handler/src/lib.rs b/library/alloc_error_handler/src/lib.rs index ce2bc49250a68..88bda4691314a 100644 --- a/library/alloc_error_handler/src/lib.rs +++ b/library/alloc_error_handler/src/lib.rs @@ -1,8 +1,20 @@ #![feature(alloc_error_handler)] #![feature(core_panic)] -#![no_std] +// we can't even build an empty crate as no_std dylib until after bootstrapping; +// it'll be missing eh_personality and the compiler doesn't know that's okay +// mattmatt maaaaybe we don't actually need no_std at all +// #![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), no_std)] -use alloc::alloc::Layout; +// depending on std, but need to get core::panicking::panic_nounwind_fmt +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +extern crate core; + +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +use std::alloc::Layout; + +// if we need no_std use below +//#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +//use alloc::alloc::Layout; // mattmatt below is __rdl_oom() which i guess is just for no_std crates? // library/std/src/alloc.rs has a #[alloc_error_handler] impl which has runtime hooks @@ -11,7 +23,8 @@ use alloc::alloc::Layout; // __rdl_oom impl to no_std crates. either keep the bad codegen shim or inject a different extern // statement depending on whether local crate is no_std -#[alloc_error_handler] +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +#[cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), alloc_error_handler)] pub unsafe fn on_oom(layout: Layout) -> ! { extern "Rust" { // This symbol is emitted by rustc next to __rust_alloc_error_handler. diff --git a/library/core/Cargo.toml b/library/core/Cargo.toml index 3dc8c84e0bfde..1d4541a5f7235 100644 --- a/library/core/Cargo.toml +++ b/library/core/Cargo.toml @@ -11,6 +11,8 @@ autobenches = false edition = "2021" [lib] +#crate-type = ["rlib"] # uncomment this when using downloaded stage0 +crate-type = ["dylib", "rlib"] # copy stage1 to another dir, use it as stage0, and uncomment this test = false bench = false @@ -33,3 +35,4 @@ panic_immediate_abort = [] # Make `RefCell` store additional debugging information, which is printed out when # a borrow error occurs debug_refcell = [] +unified-sysroot-injection = [] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index dc0702c467a4e..29d851cd018e9 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -180,6 +180,18 @@ #![feature(const_is_char_boundary)] #![feature(const_cstr_methods)] #![feature(is_ascii_octdigit)] +#![cfg_attr( + all(not(bootstrap), feature = "unified-sysroot-injection"), + feature(needs_panic_handler) +)] +#![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), needs_panic_handler)] +// core is built as a dylib, meaning std will no longer generate an allocator shim during +// bootstrap as something in its crate graph has shared linkage. so, during bootstrap, make core +// generate a default allocator shim as a hack, and then build the new hookups in stage1 +// #![cfg_attr(all(bootstrap, feature = "unified-sysroot-injection"), needs_allocator)] +// #![cfg_attr(all(bootstrap, feature = "unified-sysroot-injection"), feature(allocator_internals))] +// #![cfg_attr(all(bootstrap, feature = "unified-sysroot-injection"), default_lib_allocator)] + // // Language features: #![feature(abi_unadjusted)] diff --git a/library/global_allocator/Cargo.toml b/library/global_allocator/Cargo.toml index 9c63687dc8cff..e7d1d503b5692 100644 --- a/library/global_allocator/Cargo.toml +++ b/library/global_allocator/Cargo.toml @@ -7,9 +7,14 @@ description = "The Rust Standard Library - Default global allocator" edition = "2021" [lib] -crate-type = ["rlib"] # mattmatt add dylib after dylib changes in +crate-type = ["rlib", "dylib"] [dependencies] std = { path = "../std" } -panic_unwind = { path = "../panic_unwind" } # mattmatt why can't i make this optional -panic_abort = { path = "../panic_abort" } # panics should be unnecessary after dylib change +panic_unwind = { path = "../panic_unwind", optional = true } +panic_abort = { path = "../panic_abort" } + +[features] +default = ["unified-sysroot-injection"] +unified-sysroot-injection = [] +panic-unwind = ["panic_unwind"] diff --git a/library/global_allocator/src/lib.rs b/library/global_allocator/src/lib.rs index 82848c0611c95..6c0098d6e2682 100644 --- a/library/global_allocator/src/lib.rs +++ b/library/global_allocator/src/lib.rs @@ -1,7 +1,10 @@ +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] use std::alloc::{GlobalAlloc, Layout, System}; +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] struct DefaultAllocator; +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] unsafe impl GlobalAlloc for DefaultAllocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { System.alloc(layout) @@ -12,5 +15,6 @@ unsafe impl GlobalAlloc for DefaultAllocator { } } +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] #[global_allocator] static GLOBAL: DefaultAllocator = DefaultAllocator; diff --git a/library/panic_abort/src/lib.rs b/library/panic_abort/src/lib.rs index a3cebf99c5342..e98b658d3d6ac 100644 --- a/library/panic_abort/src/lib.rs +++ b/library/panic_abort/src/lib.rs @@ -23,12 +23,14 @@ use core::panic::BoxMeUp; #[rustc_std_internal_symbol] #[allow(improper_ctypes_definitions)] +#[no_mangle] pub unsafe extern "C" fn __rust_panic_cleanup(_: *mut u8) -> *mut (dyn Any + Send + 'static) { unreachable!() } // "Leak" the payload and shim to the relevant abort on the platform in question. #[rustc_std_internal_symbol] +#[no_mangle] pub unsafe fn __rust_start_panic(_payload: *mut &mut dyn BoxMeUp) -> u32 { // Android has the ability to attach a message as part of the abort. #[cfg(target_os = "android")] diff --git a/library/panic_handler/Cargo.toml b/library/panic_handler/Cargo.toml new file mode 100644 index 0000000000000..b776d31178133 --- /dev/null +++ b/library/panic_handler/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "panic_handler" +version = "0.0.0" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rust-lang/rust.git" +description = "The Rust Standard Library - Default panic handler" +edition = "2021" + +[lib] +crate-type = ["rlib", "dylib"] + +[dependencies] +std = { path = "../std" } +core = { path = "../core" } +panic_unwind = { path = "../panic_unwind", optional = true } +panic_abort = { path = "../panic_abort" } + +[features] +default = ["unified-sysroot-injection"] +unified-sysroot-injection = [] +panic-unwind = ["panic_unwind"] diff --git a/library/panic_handler/build.rs b/library/panic_handler/build.rs new file mode 100644 index 0000000000000..8b1a06ee750fb --- /dev/null +++ b/library/panic_handler/build.rs @@ -0,0 +1,52 @@ +use std::env; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + let target = env::var("TARGET").expect("TARGET was not set"); + if target.contains("freebsd") { + if env::var("RUST_STD_FREEBSD_12_ABI").is_ok() { + println!("cargo:rustc-cfg=freebsd12"); + } + } else if target.contains("linux") + || target.contains("netbsd") + || target.contains("dragonfly") + || target.contains("openbsd") + || target.contains("solaris") + || target.contains("illumos") + || target.contains("apple-darwin") + || target.contains("apple-ios") + || target.contains("apple-watchos") + || target.contains("uwp") + || target.contains("windows") + || target.contains("fuchsia") + || (target.contains("sgx") && target.contains("fortanix")) + || target.contains("hermit") + || target.contains("l4re") + || target.contains("redox") + || target.contains("haiku") + || target.contains("vxworks") + || target.contains("wasm32") + || target.contains("wasm64") + || target.contains("asmjs") + || target.contains("espidf") + || target.contains("solid") + || target.contains("nintendo-3ds") + { + // These platforms don't have any special requirements. + } else { + // This is for Cargo's build-std support, to mark std as unstable for + // typically no_std platforms. + // This covers: + // - os=none ("bare metal" targets) + // - mipsel-sony-psp + // - nvptx64-nvidia-cuda + // - arch=avr + // - tvos (aarch64-apple-tvos, x86_64-apple-tvos) + // - uefi (x86_64-unknown-uefi, i686-unknown-uefi) + // - JSON targets + // - Any new targets that have not been explicitly added above. + println!("cargo:rustc-cfg=feature=\"restricted-std\""); + } + println!("cargo:rustc-env=STD_ENV_ARCH={}", env::var("CARGO_CFG_TARGET_ARCH").unwrap()); + println!("cargo:rustc-cfg=backtrace_in_libstd"); +} diff --git a/library/panic_handler/src/lib.rs b/library/panic_handler/src/lib.rs new file mode 100644 index 0000000000000..3de7a00eddc25 --- /dev/null +++ b/library/panic_handler/src/lib.rs @@ -0,0 +1,97 @@ +#![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), feature(panic_can_unwind))] +#![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), feature(panic_info_message))] +#![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), feature(std_internals))] + +#![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), feature(needs_panic_runtime))] +#![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), needs_panic_runtime)] + +// Library features (unwind): +#![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), feature(panic_unwind))] + +// Ensure that std can be linked against panic_abort despite compiled with `-C panic=unwind` +// This is present in std but I get an unknown lint error here +// #![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), deny(ffi_unwind_calls))] + +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +use core::panic::{BoxMeUp, PanicInfo}; +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +use std::rust_panic_with_hook; +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +use core::mem; + +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +use std::any::Any; + +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +use std::fmt; + +/// Entry point of panics from the core crate (`panic_impl` lang item). +#[cfg(not(test))] +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +#[panic_handler] +pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { + struct PanicPayload<'a> { + inner: &'a fmt::Arguments<'a>, + string: Option, + } + + impl<'a> PanicPayload<'a> { + fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> { + PanicPayload { inner, string: None } + } + + fn fill(&mut self) -> &mut String { + use std::fmt::Write; + + let inner = self.inner; + // Lazily, the first time this gets called, run the actual string formatting. + self.string.get_or_insert_with(|| { + let mut s = String::new(); + drop(s.write_fmt(*inner)); + s + }) + } + } + + unsafe impl<'a> BoxMeUp for PanicPayload<'a> { + fn take_box(&mut self) -> *mut (dyn Any + Send) { + // We do two allocations here, unfortunately. But (a) they're required with the current + // scheme, and (b) we don't handle panic + OOM properly anyway (see comment in + // begin_panic below). + let contents = mem::take(self.fill()); + Box::into_raw(Box::new(contents)) + } + + fn get(&mut self) -> &(dyn Any + Send) { + self.fill() + } + } + + struct StrPanicPayload(&'static str); + + unsafe impl BoxMeUp for StrPanicPayload { + fn take_box(&mut self) -> *mut (dyn Any + Send) { + Box::into_raw(Box::new(self.0)) + } + + fn get(&mut self) -> &(dyn Any + Send) { + &self.0 + } + } + + let loc = info.location().unwrap(); // The current implementation always returns Some + let msg = info.message().unwrap(); // The current implementation always returns Some + std::__rust_end_short_backtrace(move || { // mattmatt + // std::sys_common::backtrace::__rust_end_short_backtrace + if let Some(msg) = msg.as_str() { + rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc, info.can_unwind()); + } else { + rust_panic_with_hook( + &mut PanicPayload::new(msg), + info.message(), + loc, + info.can_unwind(), + ); + } + }) +} diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index ea3c9a7a663c2..32d0157706983 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -92,6 +92,7 @@ extern "C" { #[rustc_std_internal_symbol] #[allow(improper_ctypes_definitions)] +#[no_mangle] pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static) { Box::into_raw(imp::cleanup(payload)) } @@ -99,6 +100,7 @@ pub unsafe extern "C" fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any // Entry point for raising an exception, just delegates to the platform-specific // implementation. #[rustc_std_internal_symbol] +#[no_mangle] pub unsafe fn __rust_start_panic(payload: *mut &mut dyn BoxMeUp) -> u32 { let payload = Box::from_raw((*payload).take_box()); diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index b790f87842c9b..a695a88078ec1 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -6,6 +6,9 @@ repository = "https://github.com/rust-lang/rust.git" description = "The Rust Standard Library" edition = "2021" +# [build] +# rustflags = ["-C", "prefer-dynamic"] # mattmatt probably bad + [lib] crate-type = ["dylib", "rlib"] @@ -74,7 +77,7 @@ panic_immediate_abort = ["core/panic_immediate_abort"] std_detect_file_io = ["std_detect/std_detect_file_io"] std_detect_dlsym_getauxval = ["std_detect/std_detect_dlsym_getauxval"] std_detect_env_override = ["std_detect/std_detect_env_override"] -unified-sysroot-injection = [] +unified-sysroot-injection = ["core/unified-sysroot-injection"] [package.metadata.fortanix-sgx] # Maximum possible number of threads when testing diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 9e31fd1231c10..f4b3ffa74104d 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -345,7 +345,7 @@ fn default_alloc_error_hook(layout: Layout) { } #[cfg(not(test))] -#[cfg(not(feature = "unified-sysroot-injection"))] +#[cfg(any(bootstrap, not(feature = "unified-sysroot-injection")))] #[doc(hidden)] #[alloc_error_handler] #[unstable(feature = "alloc_internals", issue = "none")] @@ -358,7 +358,7 @@ pub fn rust_oom(layout: Layout) -> ! { } #[cfg(not(test))] -#[cfg(not(feature = "unified-sysroot-injection"))] +#[cfg(any(bootstrap, not(feature = "unified-sysroot-injection")))] #[doc(hidden)] #[allow(unused_attributes)] #[unstable(feature = "alloc_internals", issue = "none")] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index fa4ddca81c886..841bea7a6ef31 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -359,7 +359,7 @@ #![feature(const_ipv4)] #![feature(const_ipv6)] #![feature(thread_local_internals)] -#![cfg_attr(not(feature = "unified-sysroot-injection"), default_lib_allocator)] +#![cfg_attr(any(bootstrap, not(feature = "unified-sysroot-injection")), default_lib_allocator)] // Explicitly import the prelude. The compiler uses this same unstable attribute // to import the prelude implicitly when building crates that depend on std. @@ -589,12 +589,22 @@ pub use std_detect::is_x86_feature_detected; mod sys; mod sys_common; +// mattmatt +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +#[stable(feature = "panic_hooks", since = "1.10.0")] // mattmatt cheating +pub use sys_common::backtrace::__rust_end_short_backtrace; + pub mod alloc; // Private support modules mod panicking; mod personality; +// mattmatt +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +#[stable(feature = "panic_hooks", since = "1.10.0")] // mattmatt cheating +pub use panicking::rust_panic_with_hook; + #[path = "../../backtrace/src/lib.rs"] #[allow(dead_code, unused_attributes, fuzzy_provenance_casts)] mod backtrace_rs; diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index b0db3112e22fd..4c62f4ad47522 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -519,6 +519,7 @@ pub fn panicking() -> bool { /// Entry point of panics from the core crate (`panic_impl` lang item). #[cfg(not(test))] +#[cfg(any(bootstrap, not(feature = "unified-sysroot-injection")))] #[panic_handler] pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { struct PanicPayload<'a> { @@ -646,7 +647,8 @@ pub const fn begin_panic(msg: M) -> ! { /// Executes the primary logic for a panic, including checking for recursive /// panics, panic hooks, and finally dispatching to the panic runtime to either /// abort or unwind. -fn rust_panic_with_hook( +#[stable(feature = "panic_hooks", since = "1.10.0")] // mattmatt cheating +pub fn rust_panic_with_hook( payload: &mut dyn BoxMeUp, message: Option<&fmt::Arguments<'_>>, location: &Location<'_>, diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs index f1d804ef40c0e..344509cb1b116 100644 --- a/library/std/src/sys_common/backtrace.rs +++ b/library/std/src/sys_common/backtrace.rs @@ -130,6 +130,7 @@ where /// this is only inline(never) when backtraces in std are enabled, otherwise /// it's fine to optimize away. #[cfg_attr(feature = "backtrace", inline(never))] +#[stable(feature = "panic_hooks", since = "1.10.0")] // mattmatt cheating pub fn __rust_end_short_backtrace(f: F) -> T where F: FnOnce() -> T, diff --git a/library/test/Cargo.toml b/library/test/Cargo.toml index 708e8353079ed..a247a7c52a523 100644 --- a/library/test/Cargo.toml +++ b/library/test/Cargo.toml @@ -17,12 +17,13 @@ panic_abort = { path = "../panic_abort" } # not actually used but needed to always have these in the sysroot proc_macro = { path = "../proc_macro" } -global_allocator = { path = "../global_allocator", optional = true } # mattmatt does this conflict with external allocator -alloc_error_handler = { path = "../alloc_error_handler", optional = true } # mattmatt does this conflict with external allocator +global_allocator = { path = "../global_allocator", optional = true } +alloc_error_handler = { path = "../alloc_error_handler", optional = true } +panic_handler = { path = "../panic_handler", optional = true } # Forward features to the `std` crate as necessary [features] -default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind"] +default = ["std_detect_file_io", "std_detect_dlsym_getauxval", "panic-unwind", "unified-sysroot-injection"] backtrace = ["std/backtrace"] compiler-builtins-c = ["std/compiler-builtins-c"] compiler-builtins-mem = ["std/compiler-builtins-mem"] @@ -30,10 +31,10 @@ compiler-builtins-no-asm = ["std/compiler-builtins-no-asm"] compiler-builtins-mangled-names = ["std/compiler-builtins-mangled-names"] llvm-libunwind = ["std/llvm-libunwind"] system-llvm-libunwind = ["std/system-llvm-libunwind"] -panic-unwind = ["std/panic_unwind"] +panic-unwind = ["std/panic_unwind", "panic_handler?/panic-unwind", "global_allocator?/panic-unwind", "alloc_error_handler?/panic-unwind"] panic_immediate_abort = ["std/panic_immediate_abort"] profiler = ["std/profiler"] std_detect_file_io = ["std/std_detect_file_io"] std_detect_dlsym_getauxval = ["std/std_detect_dlsym_getauxval"] std_detect_env_override = ["std/std_detect_env_override"] -unified-sysroot-injection = ["dep:global_allocator", "dep:alloc_error_handler", "std/unified-sysroot-injection"] +unified-sysroot-injection = ["dep:global_allocator", "dep:alloc_error_handler", "dep:panic_handler", "std/unified-sysroot-injection"] From 2c66c74552dd269c7c6246fa7e13e56793c368fc Mon Sep 17 00:00:00 2001 From: Matt Hammerly Date: Thu, 2 Mar 2023 16:58:08 -0800 Subject: [PATCH 5/5] fixed discovery of allocator crates, tidied, formatted --- .../src/alloc_error_handler.rs | 3 +- .../src/extern_allocator_crate.rs | 202 +----------------- .../src/global_allocator.rs | 5 +- compiler/rustc_codegen_llvm/src/allocator.rs | 4 +- compiler/rustc_interface/src/passes.rs | 1 + library/panic_handler/src/lib.rs | 21 +- 6 files changed, 20 insertions(+), 216 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs index 0e282dc4d8f28..dcf500ddbd3c8 100644 --- a/compiler/rustc_builtin_macros/src/alloc_error_handler.rs +++ b/compiler/rustc_builtin_macros/src/alloc_error_handler.rs @@ -86,8 +86,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span body, })); -// let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)]; - let attrs = thin_vec![cx.attr_word(sym::no_mangle, span)]; + let attrs = thin_vec![cx.attr_word(sym::rustc_std_internal_symbol, span)]; let item = cx.item(span, Ident::from_str_and_span("__rg_oom", span), attrs, kind); cx.stmt_item(sig_span, item) diff --git a/compiler/rustc_builtin_macros/src/extern_allocator_crate.rs b/compiler/rustc_builtin_macros/src/extern_allocator_crate.rs index 975ca183d2eb3..ca173129ab624 100644 --- a/compiler/rustc_builtin_macros/src/extern_allocator_crate.rs +++ b/compiler/rustc_builtin_macros/src/extern_allocator_crate.rs @@ -6,27 +6,15 @@ // mattmatt could/should this be fully generic and used for other pre-existing extern imports as // well or what -#![allow(unused)] - use rustc_ast as ast; -use rustc_ast::ast::StrStyle; -use rustc_ast::expand::allocator::{ - AllocatorKind, AllocatorMethod, AllocatorTy, ALLOCATOR_METHODS, -}; -use rustc_ast::ptr::P; -use rustc_ast::{ - Fn, FnHeader, FnSig, ForeignItem, ForeignItemKind, ForeignMod, Generics, Mutability, - Param, StrLit, Ty, TyKind, Unsafe, -}; use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_session::Session; use rustc_span::edition::Edition::Edition2018; use rustc_span::hygiene::AstPass; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::DUMMY_SP; use smallvec::smallvec; -// use std::vec; use thin_vec::thin_vec; pub fn inject( @@ -59,190 +47,4 @@ pub fn inject( println!("mattmatt injecting {}", sym_name); krate.items.insert(0, expanded_fragment); // mattmatt 0 may be wrong - - /* - if sym_name == sym::global_allocator { - println!("mattmatt trying to make extern module lol"); - // inject extern decls for the allocator functions so that when we dynamically link the - // compiler will expect them to be found elsewhere - // extern "Rust" { - // fn __rg_alloc(); - // fn __rg_realloc(); - // fn __rg_dealloc(); - // etc - // } - - let factory = AllocFnFactory { span, kind: AllocatorKind::Global, cx: &cx }; - println!("mattmatt factory created"); - - let items = ALLOCATOR_METHODS.iter().map(|method| factory.allocator_fn(method)).collect(); - println!("mattmatt allocator ForeignItems created"); - - let abi = Symbol::intern("Rust"); - println!("mattmatt extern Rust symbol created"); - let fmod = ast::ItemKind::ForeignMod(ForeignMod { - unsafety: Unsafe::Yes(span), - abi: Some(StrLit { - symbol: abi, - suffix: None, - symbol_unescaped: abi, - style: StrStyle::Cooked, - span: span, - }), - items: items, - }); - println!("mattmatt foreign module created"); - - let fmod_item = cx.item(span, Ident::new(kw::Underscore, span), thin_vec![], fmod); - println!("mattmatt injecting allocator externs"); - - let fmod_fragment = AstFragment::Items(smallvec![fmod_item]); - let expanded_fmod_fragment = - cx.monotonic_expander().fully_expand_fragment(fmod_fragment).make_items().pop().unwrap(); - krate.items.push(expanded_fmod_fragment); - } - */ -} - -struct AllocFnFactory<'a, 'b> { - span: Span, - kind: AllocatorKind, - cx: &'b ExtCtxt<'a>, -} - -impl AllocFnFactory<'_, '_> { - fn allocator_fn(&self, method: &AllocatorMethod) -> P { - println!("mattmatt generating ForeignItem for {}", self.kind.fn_name(method.name)); - let mut abi_args = Vec::new(); - let mut i = 0; - let mut mk = || { - let name = Ident::from_str_and_span(&format!("arg{}", i), self.span); - i += 1; - name - }; - println!("mattmatt about to populate abi_args"); - let _args: Vec<()> = - method.inputs.iter().map(|ty| self.arg_ty(ty, &mut abi_args, &mut mk)).collect(); - - println!("mattmatt populated abi_args"); - let output_ty = self.ret_ty(&method.output); - println!("mattmatt got return type"); - let decl = self.cx.fn_decl(abi_args, ast::FnRetTy::Ty(output_ty)); - println!("mattmatt got fn declaration"); - let header = FnHeader { ..FnHeader::default() }; - let sig = FnSig { decl, header, span: self.span }; - println!("mattmatt got fn signature"); - let body = None; - - let kind = ForeignItemKind::Fn(Box::new(Fn { - defaultness: ast::Defaultness::Final, - sig, - generics: Generics::default(), - body, - })); - println!("mattmatt made ForeignItemKind"); - let foreign_item = P(ast::Item { - ident: Ident::from_str_and_span(&self.kind.fn_name(method.name), self.span), - attrs: thin_vec![], - id: ast::DUMMY_NODE_ID, - kind: kind, - vis: ast::Visibility { - span: self.span.shrink_to_lo(), - kind: ast::VisibilityKind::Inherited, // mattmatt maybe make this public - tokens: None, - }, - span: self.span, - tokens: None, - }); - println!("mattmatt made ForeignItem"); - - foreign_item - } - - fn arg_ty( - &self, - ty: &AllocatorTy, - args: &mut Vec, - ident: &mut dyn FnMut() -> Ident, - ) { -// ) -> P { - match *ty { - AllocatorTy::Layout => { - println!("mattmatt layout return type"); - let usize = self.cx.path_ident(self.span, Ident::new(sym::usize, self.span)); - let ty_usize = self.cx.ty_path(usize); - let size = ident(); - let align = ident(); - println!("mattmatt about to push args"); - args.push(self.cx.param(self.span, size, ty_usize.clone())); - args.push(self.cx.param(self.span, align, ty_usize)); - println!("mattmatt layout pushed to args"); - - /* - let layout_new = - self.cx.std_path(&[sym::alloc, sym::Layout, sym::from_size_align_unchecked]); - println!("mattmatt std_path done"); - let layout_new = self.cx.expr_path(self.cx.path(self.span, layout_new)); - println!("mattmatt expr_path"); - let size = self.cx.expr_ident(self.span, size); - println!("mattmatt first expr_ident"); - let align = self.cx.expr_ident(self.span, align); - println!("mattmatt second expr_ident"); - let layout = self.cx.expr_call(self.span, layout_new, vec![size, align]); - println!("matt returning after expr_call"); - layout - */ - } - - AllocatorTy::Ptr => { - println!("mattmatt ptr rreturn type"); - let ident = ident(); - args.push(self.cx.param(self.span, ident, self.ptr_u8())); - println!("mattmatt pushed to args"); - /* - let arg = self.cx.expr_ident(self.span, ident); - println!("mattmatt expr_ident, about to return expr_cast"); - self.cx.expr_cast(self.span, arg, self.ptr_u8()) - */ - } - - AllocatorTy::Usize => { - println!("mattmatt usize reutrn type"); - let ident = ident(); - args.push(self.cx.param(self.span, ident, self.usize())); - println!("mattmatt pushed to args, about to reutnr expr_ident"); - /* - self.cx.expr_ident(self.span, ident) - */ - } - - AllocatorTy::ResultPtr | AllocatorTy::Unit => { - println!("mattmatt about to panic"); - panic!("can't convert AllocatorTy to an argument") - } - } - } - - fn ret_ty(&self, ty: &AllocatorTy) -> P { - match *ty { - AllocatorTy::ResultPtr => self.ptr_u8(), - - AllocatorTy::Unit => self.cx.ty(self.span, TyKind::Tup(Vec::new())), - - AllocatorTy::Layout | AllocatorTy::Usize | AllocatorTy::Ptr => { - panic!("can't convert `AllocatorTy` to an output") - } - } - } - - fn usize(&self) -> P { - let usize = self.cx.path_ident(self.span, Ident::new(sym::usize, self.span)); - self.cx.ty_path(usize) - } - - fn ptr_u8(&self) -> P { - let u8 = self.cx.path_ident(self.span, Ident::new(sym::u8, self.span)); - let ty_u8 = self.cx.ty_path(u8); - self.cx.ty_ptr(self.span, ty_u8, Mutability::Mut) - } } diff --git a/compiler/rustc_builtin_macros/src/global_allocator.rs b/compiler/rustc_builtin_macros/src/global_allocator.rs index 4e85b45457037..f8761653bf5b7 100644 --- a/compiler/rustc_builtin_macros/src/global_allocator.rs +++ b/compiler/rustc_builtin_macros/src/global_allocator.rs @@ -111,10 +111,7 @@ impl AllocFnFactory<'_, '_> { } fn attrs(&self) -> AttrVec { -// thin_vec![self.cx.attr_word(sym::rustc_std_internal_symbol, self.span)] - thin_vec![self.cx.attr_word(sym::no_mangle, self.span)] // mattmatt maybe the - // internal symbol thing messes - // with shared linkage + thin_vec![self.cx.attr_word(sym::rustc_std_internal_symbol, self.span)] } fn arg_ty( diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index b5fa140790978..be4c2f9b73014 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -71,8 +71,8 @@ pub(crate) unsafe fn codegen( let callee = kind.fn_name(method.name); let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); - llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Default); // mattmatt originally - // hidden + // mattmatt originally hidden + llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Default); let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index f1ba89ee1dd99..00877911cfc09 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -405,6 +405,7 @@ pub fn configure_and_expand( if sess.opts.unstable_opts.unified_sysroot_injection { sess.time("maybe_inject_allocator_crates", || { + println!("mattmatt maybe injecting allocators"); let allocator_crates = resolver.crate_loader().find_allocator_crates(&krate); if let Some((global_allocator, alloc_error_handler)) = allocator_crates { if !global_allocator.is_some() { diff --git a/library/panic_handler/src/lib.rs b/library/panic_handler/src/lib.rs index 3de7a00eddc25..4639b2b9a6f64 100644 --- a/library/panic_handler/src/lib.rs +++ b/library/panic_handler/src/lib.rs @@ -1,10 +1,14 @@ #![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), feature(panic_can_unwind))] -#![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), feature(panic_info_message))] +#![cfg_attr( + all(not(bootstrap), feature = "unified-sysroot-injection"), + feature(panic_info_message) +)] #![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), feature(std_internals))] - -#![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), feature(needs_panic_runtime))] +#![cfg_attr( + all(not(bootstrap), feature = "unified-sysroot-injection"), + feature(needs_panic_runtime) +)] #![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), needs_panic_runtime)] - // Library features (unwind): #![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), feature(panic_unwind))] @@ -12,12 +16,12 @@ // This is present in std but I get an unknown lint error here // #![cfg_attr(all(not(bootstrap), feature = "unified-sysroot-injection"), deny(ffi_unwind_calls))] +#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] +use core::mem; #[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] use core::panic::{BoxMeUp, PanicInfo}; #[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] use std::rust_panic_with_hook; -#[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] -use core::mem; #[cfg(all(not(bootstrap), feature = "unified-sysroot-injection"))] use std::any::Any; @@ -81,8 +85,9 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { let loc = info.location().unwrap(); // The current implementation always returns Some let msg = info.message().unwrap(); // The current implementation always returns Some - std::__rust_end_short_backtrace(move || { // mattmatt - // std::sys_common::backtrace::__rust_end_short_backtrace + std::__rust_end_short_backtrace(move || { + // mattmatt + // std::sys_common::backtrace::__rust_end_short_backtrace if let Some(msg) = msg.as_str() { rust_panic_with_hook(&mut StrPanicPayload(msg), info.message(), loc, info.can_unwind()); } else {