diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 84bd275df347c..0554fd821ba2e 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -15,6 +15,8 @@ use core::intrinsics::{min_align_of_val, size_of_val}; use core::ptr::{NonNull, Unique}; use core::usize; +#[cfg(target_has_atomic = "ptr")] use core::{mem, ptr}; +#[cfg(target_has_atomic = "ptr")] use core::sync::atomic::{AtomicPtr, Ordering}; #[stable(feature = "alloc_module", since = "1.28.0")] #[doc(inline)] @@ -191,12 +193,89 @@ pub(crate) unsafe fn box_free(ptr: Unique) { #[stable(feature = "global_alloc", since = "1.28.0")] #[rustc_allocator_nounwind] pub fn handle_alloc_error(layout: Layout) -> ! { - #[allow(improper_ctypes)] - extern "Rust" { - #[lang = "oom"] - fn oom_impl(layout: Layout) -> !; + #[cfg(target_has_atomic = "ptr")] + { + let hook = HOOK.load(Ordering::SeqCst); + let hook: fn(Layout) = if hook.is_null() { + default_alloc_error_hook + } else { + unsafe { mem::transmute(hook) } + }; + hook(layout); + } + #[cfg(not(target_has_atomic = "ptr"))] + { + default_alloc_error_hook(layout) + } + + unsafe { + #[cfg(not(stage0))] + { + #[allow(improper_ctypes)] + extern "Rust" { + // This function is generated by the compiler + // and calls __rust_abort_internal in src/libstd/alloc.rs + // if the `std` crate is linked. + fn __rust_maybe_abort_internal(); + } + __rust_maybe_abort_internal(); + } + // The above does nothing and returns when `std` is not linked. + // In that case we abort "naively" here: + ::core::intrinsics::abort() + } +} + +fn default_alloc_error_hook(layout: Layout) { + #[cfg(stage0)] + let _ = layout; + #[cfg(not(stage0))] + unsafe { + #[allow(improper_ctypes)] + extern "Rust" { + // This function is generated by the compiler + // and calls __rust_default_alloc_error_hook in src/libstd/alloc.rs + // if the `std` crate is linked. + fn __rust_maybe_default_alloc_error_hook(size: usize, align: usize); + } + __rust_maybe_default_alloc_error_hook(layout.size(), layout.align()) + } +} + +#[cfg(target_has_atomic = "ptr")] +static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); + +/// Registers a custom allocation error hook, replacing any that was previously registered. +/// +/// The allocation error hook is invoked when an infallible memory allocation fails, before +/// the runtime aborts. The default hook prints a message to standard error, +/// but this behavior can be customized with the [`set_alloc_error_hook`] and +/// [`take_alloc_error_hook`] functions. +/// +/// The hook is provided with a `Layout` struct which contains information +/// about the allocation that failed. +/// +/// The allocation error hook is a global resource. +#[unstable(feature = "alloc_error_hook", issue = "51245")] +#[cfg(target_has_atomic = "ptr")] +pub fn set_alloc_error_hook(hook: fn(Layout)) { + HOOK.store(hook as *mut (), Ordering::SeqCst); +} + +/// Unregisters the current allocation error hook, returning it. +/// +/// *See also the function [`set_alloc_error_hook`].* +/// +/// If no custom hook is registered, the default hook will be returned. +#[unstable(feature = "alloc_error_hook", issue = "51245")] +#[cfg(target_has_atomic = "ptr")] +pub fn take_alloc_error_hook() -> fn(Layout) { + let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst); + if hook.is_null() { + default_alloc_error_hook + } else { + unsafe { mem::transmute(hook) } } - unsafe { oom_impl(layout) } } #[cfg(test)] diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index fe676919a7d14..3195b507b1c20 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -307,7 +307,6 @@ language_item_table! { ExchangeMallocFnLangItem, "exchange_malloc", exchange_malloc_fn; BoxFreeFnLangItem, "box_free", box_free_fn; DropInPlaceFnLangItem, "drop_in_place", drop_in_place_fn; - OomLangItem, "oom", oom; StartFnLangItem, "start", start_fn; diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs index 3c2ea047218a7..6a1a9e9bc4f8b 100644 --- a/src/librustc/middle/weak_lang_items.rs +++ b/src/librustc/middle/weak_lang_items.rs @@ -151,5 +151,4 @@ weak_lang_items! { panic_impl, PanicImplLangItem, rust_begin_unwind; eh_personality, EhPersonalityLangItem, rust_eh_personality; eh_unwind_resume, EhUnwindResumeLangItem, rust_eh_unwind_resume; - oom, OomLangItem, rust_oom; } diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 076d56fb80842..3ffe7a7a8cf67 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -118,6 +118,7 @@ pub struct Session { /// injected. pub injected_allocator: Once>, pub allocator_kind: Once>, + pub injected_default_alloc_error_hook: Once, pub injected_panic_runtime: Once>, /// Map from imported macro spans (which consist of @@ -175,6 +176,12 @@ pub struct PerfStats { pub normalize_projection_ty: AtomicUsize, } +pub enum InjectedDefaultOomHook { + None, + Noop, + Platform, +} + /// Enum to support dispatch of one-time diagnostics (in Session.diag_once) enum DiagnosticBuilderMethod { Note, @@ -1119,6 +1126,7 @@ pub fn build_session_( next_node_id: OneThread::new(Cell::new(NodeId::new(1))), injected_allocator: Once::new(), allocator_kind: Once::new(), + injected_default_alloc_error_hook: Once::new(), injected_panic_runtime: Once::new(), imported_macro_spans: OneThread::new(RefCell::new(HashMap::new())), incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), diff --git a/src/librustc_allocator/lib.rs b/src/librustc_allocator/lib.rs index b217d3665a245..5f9fea076f31b 100644 --- a/src/librustc_allocator/lib.rs +++ b/src/librustc_allocator/lib.rs @@ -42,6 +42,19 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ }, ]; +pub static OOM_HANDLING_METHODS: &[AllocatorMethod] = &[ + AllocatorMethod { + name: "default_alloc_error_hook", + inputs: &[AllocatorTy::Layout], + output: AllocatorTy::Unit, + }, + AllocatorMethod { + name: "abort_internal", + inputs: &[], + output: AllocatorTy::Unit, + }, +]; + pub struct AllocatorMethod { pub name: &'static str, pub inputs: &'static [AllocatorTy], diff --git a/src/librustc_codegen_llvm/allocator.rs b/src/librustc_codegen_llvm/allocator.rs index eeb02e948f1cc..80e4a58c03622 100644 --- a/src/librustc_codegen_llvm/allocator.rs +++ b/src/librustc_codegen_llvm/allocator.rs @@ -14,8 +14,9 @@ use std::ptr; use attributes; use libc::c_uint; use rustc::middle::allocator::AllocatorKind; +use rustc::session::InjectedDefaultOomHook; use rustc::ty::TyCtxt; -use rustc_allocator::{ALLOCATOR_METHODS, AllocatorTy}; +use rustc_allocator::{ALLOCATOR_METHODS, OOM_HANDLING_METHODS, AllocatorTy}; use ModuleLlvm; use llvm::{self, False, True}; @@ -33,9 +34,10 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind let i8p = llvm::LLVMPointerType(i8, 0); let void = llvm::LLVMVoidTypeInContext(llcx); - for method in ALLOCATOR_METHODS { + let build = |name: String, inputs: &[AllocatorTy], output: &AllocatorTy, + callee: Option| { let mut args = Vec::new(); - for ty in method.inputs.iter() { + for ty in inputs.iter() { match *ty { AllocatorTy::Layout => { args.push(usize); // size @@ -48,7 +50,7 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind AllocatorTy::Unit => panic!("invalid allocator arg"), } } - let output = match method.output { + let output = match *output { AllocatorTy::ResultPtr => Some(i8p), AllocatorTy::Unit => None, @@ -60,7 +62,7 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind args.as_ptr(), args.len() as c_uint, False); - let name = CString::new(format!("__rust_{}", method.name)).unwrap(); + let name = CString::new(name).unwrap(); let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr(), ty); @@ -68,14 +70,9 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind if tcx.sess.target.target.options.default_hidden_visibility { llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); } - if tcx.sess.target.target.options.requires_uwtable { - attributes::emit_uwtable(llfn, true); - } - - let callee = CString::new(kind.fn_name(method.name)).unwrap(); - let callee = llvm::LLVMRustGetOrInsertFunction(llmod, - callee.as_ptr(), - ty); + if tcx.sess.target.target.options.requires_uwtable { + attributes::emit_uwtable(llfn, true); + } let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, @@ -83,6 +80,22 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); + + let callee = if let Some(callee) = callee { + callee + } else { + // Generate a no-op function + llvm::LLVMBuildRetVoid(llbuilder); + llvm::LLVMDisposeBuilder(llbuilder); + return + }; + + // Forward the call to another function + let callee = CString::new(callee).unwrap(); + let callee = llvm::LLVMRustGetOrInsertFunction(llmod, + callee.as_ptr(), + ty); + let args = args.iter().enumerate().map(|(i, _)| { llvm::LLVMGetParam(llfn, i as c_uint) }).collect::>(); @@ -98,6 +111,28 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind } else { llvm::LLVMBuildRetVoid(llbuilder); } + llvm::LLVMDisposeBuilder(llbuilder); + }; + + for method in ALLOCATOR_METHODS { + let name = format!("__rust_{}", method.name); + build(name, method.inputs, &method.output, Some(kind.fn_name(method.name))) + } + + let has_plaftom_functions = match tcx.sess.injected_default_alloc_error_hook.get() { + InjectedDefaultOomHook::None => return, + InjectedDefaultOomHook::Noop => false, + InjectedDefaultOomHook::Platform => true, + }; + + for method in OOM_HANDLING_METHODS { + let callee = if has_plaftom_functions { + Some(format!("__rust_{}", method.name)) + } else { + None + }; + let name = format!("__rust_maybe_{}", method.name); + build(name, method.inputs, &method.output, callee) } } diff --git a/src/librustc_codegen_llvm/back/symbol_export.rs b/src/librustc_codegen_llvm/back/symbol_export.rs index 28e76a80513f0..a1e0b04300f46 100644 --- a/src/librustc_codegen_llvm/back/symbol_export.rs +++ b/src/librustc_codegen_llvm/back/symbol_export.rs @@ -17,12 +17,12 @@ use rustc::hir::CodegenFnAttrFlags; use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX}; use rustc::ich::Fingerprint; use rustc::middle::exported_symbols::{SymbolExportLevel, ExportedSymbol, metadata_symbol_name}; -use rustc::session::config; +use rustc::session::{config, InjectedDefaultOomHook}; use rustc::ty::{TyCtxt, SymbolName}; use rustc::ty::query::Providers; use rustc::ty::subst::Substs; use rustc::util::nodemap::{FxHashMap, DefIdMap}; -use rustc_allocator::ALLOCATOR_METHODS; +use rustc_allocator::{ALLOCATOR_METHODS, OOM_HANDLING_METHODS}; use rustc_data_structures::indexed_vec::IndexVec; use std::collections::hash_map::Entry::*; @@ -222,6 +222,19 @@ fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } + match tcx.sess.injected_default_alloc_error_hook.get() { + InjectedDefaultOomHook::None => {} + InjectedDefaultOomHook::Noop | + InjectedDefaultOomHook::Platform => { + for method in OOM_HANDLING_METHODS { + let symbol_name = format!("__rust_{}", method.name); + let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); + + symbols.push((exported_symbol, SymbolExportLevel::Rust)); + } + } + } + if tcx.sess.opts.debugging_opts.pgo_gen.is_some() { // These are weak symbols that point to the profile version and the // profile name, which need to be treated as exported so LTO doesn't nix diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs index 1a13335a0e49d..95059c91826e3 100644 --- a/src/librustc_metadata/creader.rs +++ b/src/librustc_metadata/creader.rs @@ -20,7 +20,7 @@ use rustc::hir::svh::Svh; use rustc::middle::allocator::AllocatorKind; use rustc::middle::cstore::DepKind; use rustc::mir::interpret::AllocDecodingState; -use rustc::session::{Session, CrateDisambiguator}; +use rustc::session::{Session, CrateDisambiguator, InjectedDefaultOomHook}; use rustc::session::config::{Sanitizer, self}; use rustc_target::spec::{PanicStrategy, TargetTriple}; use rustc::session::search_paths::PathKind; @@ -841,6 +841,7 @@ impl<'a> CrateLoader<'a> { if !needs_allocator { self.sess.injected_allocator.set(None); self.sess.allocator_kind.set(None); + self.sess.injected_default_alloc_error_hook.set(InjectedDefaultOomHook::None); return } @@ -862,9 +863,22 @@ impl<'a> CrateLoader<'a> { if !need_lib_alloc && !need_exe_alloc { self.sess.injected_allocator.set(None); self.sess.allocator_kind.set(None); + self.sess.injected_default_alloc_error_hook.set(InjectedDefaultOomHook::None); return } + let mut has_default_lib_allocator = attr::contains_name(&krate.attrs, + "default_lib_allocator");; + self.cstore.iter_crate_data(|_, data| { + has_default_lib_allocator = has_default_lib_allocator || + data.root.has_default_lib_allocator; + }); + self.sess.injected_default_alloc_error_hook.set(if has_default_lib_allocator { + InjectedDefaultOomHook::Platform + } else { + InjectedDefaultOomHook::Noop + }); + // 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. diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index f28e91e19b73c..3c66bd3032ea9 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -74,10 +74,6 @@ #![stable(feature = "alloc_module", since = "1.28.0")] -use core::sync::atomic::{AtomicPtr, Ordering}; -use core::{mem, ptr}; -use sys_common::util::dumb_print; - #[stable(feature = "alloc_module", since = "1.28.0")] #[doc(inline)] pub use alloc_crate::alloc::*; @@ -86,64 +82,14 @@ pub use alloc_crate::alloc::*; #[doc(inline)] pub use alloc_system::System; -static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); - -/// Registers a custom allocation error hook, replacing any that was previously registered. -/// -/// The allocation error hook is invoked when an infallible memory allocation fails, before -/// the runtime aborts. The default hook prints a message to standard error, -/// but this behavior can be customized with the [`set_alloc_error_hook`] and -/// [`take_alloc_error_hook`] functions. -/// -/// The hook is provided with a `Layout` struct which contains information -/// about the allocation that failed. -/// -/// The allocation error hook is a global resource. -#[unstable(feature = "alloc_error_hook", issue = "51245")] -pub fn set_alloc_error_hook(hook: fn(Layout)) { - HOOK.store(hook as *mut (), Ordering::SeqCst); -} - -/// Unregisters the current allocation error hook, returning it. -/// -/// *See also the function [`set_alloc_error_hook`].* -/// -/// If no custom hook is registered, the default hook will be returned. -#[unstable(feature = "alloc_error_hook", issue = "51245")] -pub fn take_alloc_error_hook() -> fn(Layout) { - let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst); - if hook.is_null() { - default_alloc_error_hook - } else { - unsafe { mem::transmute(hook) } - } -} - -fn default_alloc_error_hook(layout: Layout) { - dumb_print(format_args!("memory allocation of {} bytes failed", layout.size())); -} - -#[cfg(not(test))] -#[doc(hidden)] -#[lang = "oom"] -#[unstable(feature = "alloc_internals", issue = "0")] -pub extern fn rust_oom(layout: Layout) -> ! { - let hook = HOOK.load(Ordering::SeqCst); - let hook: fn(Layout) = if hook.is_null() { - default_alloc_error_hook - } else { - unsafe { mem::transmute(hook) } - }; - hook(layout); - unsafe { ::sys::abort_internal(); } -} - #[cfg(not(test))] #[doc(hidden)] #[allow(unused_attributes)] #[unstable(feature = "alloc_internals", issue = "0")] pub mod __default_lib_allocator { use super::{System, Layout, GlobalAlloc}; + use sys_common::util::dumb_print; + // for symbol names src/librustc/middle/allocator.rs // for signatures src/librustc_allocator/lib.rs @@ -181,4 +127,16 @@ pub mod __default_lib_allocator { let layout = Layout::from_size_align_unchecked(size, align); System.alloc_zeroed(layout) } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub extern fn __rust_default_alloc_error_hook(size: usize, _align: usize) { + dumb_print(format_args!("memory allocation of {} bytes failed", size)); + } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rust_abort_internal() { + ::sys::abort_internal() + } } diff --git a/src/test/run-pass/default-alloc-error-hook.rs b/src/test/run-pass/default-alloc-error-hook.rs new file mode 100644 index 0000000000000..5f6b60a5a8d99 --- /dev/null +++ b/src/test/run-pass/default-alloc-error-hook.rs @@ -0,0 +1,28 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-cloudabi no processes +// ignore-emscripten no processes + +use std::alloc::{Layout, handle_alloc_error}; +use std::env; +use std::process::Command; +use std::str; + +fn main() { + if env::args().len() > 1 { + handle_alloc_error(Layout::new::<[u8; 42]>()) + } + + let me = env::current_exe().unwrap(); + let output = Command::new(&me).arg("next").output().unwrap(); + assert!(!output.status.success(), "{:?} is a success", output.status); + assert_eq!(str::from_utf8(&output.stderr).unwrap(), "memory allocation of 42 bytes failed"); +} diff --git a/src/test/ui/missing-allocator.rs b/src/test/ui/missing-allocator.rs index 24282631b7eea..7cc56ce264731 100644 --- a/src/test/ui/missing-allocator.rs +++ b/src/test/ui/missing-allocator.rs @@ -20,7 +20,4 @@ fn panic(_: &core::panic::PanicInfo) -> ! { loop {} } -#[lang = "oom"] -fn oom() {} - extern crate alloc;