From cadd12b5f020b9b2c7bb0c5e7078b81fbdcfb6d8 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Mon, 7 Sep 2020 10:45:20 +0200 Subject: [PATCH] Implement Make `handle_alloc_error` default to panic (for no_std + liballoc) Related: https://github.com/rust-lang/rust/issues/66741 Guarded with `#![feature(default_alloc_error_handler)]` a default `alloc_error_handler` is called, if a custom allocator is used and no other custom `#[alloc_error_handler]` is defined. The panic message does not contain the size anymore, because it would pull in the fmt machinery, which would blow up the code size significantly. --- compiler/rustc_codegen_llvm/src/allocator.rs | 45 ++++++++- compiler/rustc_codegen_llvm/src/lib.rs | 3 +- compiler/rustc_codegen_ssa/src/base.rs | 5 +- .../rustc_codegen_ssa/src/traits/backend.rs | 1 + compiler/rustc_feature/src/active.rs | 3 + compiler/rustc_passes/src/weak_lang_items.rs | 5 +- compiler/rustc_span/src/symbol.rs | 1 + library/alloc/src/alloc.rs | 47 +++++++++ src/test/ui/allocator/auxiliary/helper.rs | 4 +- .../no_std-alloc-error-handler-custom.rs | 97 +++++++++++++++++++ .../no_std-alloc-error-handler-default.rs | 84 ++++++++++++++++ .../missing-alloc_error_handler.stderr | 4 +- 12 files changed, 292 insertions(+), 7 deletions(-) create mode 100644 src/test/ui/allocator/no_std-alloc-error-handler-custom.rs create mode 100644 src/test/ui/allocator/no_std-alloc-error-handler-default.rs diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs index bc1d9e1818c..e028b2c2dc7 100644 --- a/compiler/rustc_codegen_llvm/src/allocator.rs +++ b/compiler/rustc_codegen_llvm/src/allocator.rs @@ -3,11 +3,17 @@ use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS}; use rustc_middle::bug; use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::sym; use crate::llvm::{self, False, True}; use crate::ModuleLlvm; -pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: AllocatorKind) { +pub(crate) unsafe fn codegen( + tcx: TyCtxt<'_>, + mods: &mut ModuleLlvm, + kind: AllocatorKind, + has_alloc_error_handler: bool, +) { let llcx = &*mods.llcx; let llmod = mods.llmod(); let usize = match &tcx.sess.target.target.target_pointer_width[..] { @@ -82,4 +88,41 @@ pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: Alloc } llvm::LLVMDisposeBuilder(llbuilder); } + + // rust alloc error handler + let args = [usize, usize]; // size, align + + let ty = llvm::LLVMFunctionType(void, args.as_ptr(), args.len() as c_uint, False); + let name = format!("__rust_alloc_error_handler"); + let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty); + // -> ! DIFlagNoReturn + llvm::Attribute::NoReturn.apply_llfn(llvm::AttributePlace::Function, llfn); + + if tcx.sess.target.target.options.default_hidden_visibility { + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + if tcx.sess.must_emit_unwind_tables() { + attributes::emit_uwtable(llfn, true); + } + + let kind = if has_alloc_error_handler { AllocatorKind::Global } else { AllocatorKind::Default }; + let callee = kind.fn_name(sym::oom); + let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty); + // -> ! DIFlagNoReturn + llvm::Attribute::NoReturn.apply_llfn(llvm::AttributePlace::Function, callee); + llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden); + + let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast()); + + let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); + llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); + let args = args + .iter() + .enumerate() + .map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint)) + .collect::>(); + let ret = llvm::LLVMRustBuildCall(llbuilder, callee, args.as_ptr(), args.len() as c_uint, None); + llvm::LLVMSetTailCall(ret, True); + llvm::LLVMBuildRetVoid(llbuilder); + llvm::LLVMDisposeBuilder(llbuilder); } diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index f14493e6043..1237b39b300 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -95,8 +95,9 @@ fn codegen_allocator<'tcx>( tcx: TyCtxt<'tcx>, mods: &mut ModuleLlvm, kind: AllocatorKind, + has_alloc_error_handler: bool, ) { - unsafe { allocator::codegen(tcx, mods, kind) } + unsafe { allocator::codegen(tcx, mods, kind, has_alloc_error_handler) } } fn compile_codegen_unit( &self, diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index d82fc2c9f63..8e6f8e193c0 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -538,8 +538,9 @@ pub fn codegen_crate( let llmod_id = cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string(); let mut modules = backend.new_metadata(tcx, &llmod_id); - tcx.sess - .time("write_allocator_module", || backend.codegen_allocator(tcx, &mut modules, kind)); + tcx.sess.time("write_allocator_module", || { + backend.codegen_allocator(tcx, &mut modules, kind, tcx.lang_items().oom().is_some()) + }); Some(ModuleCodegen { name: llmod_id, module_llvm: modules, kind: ModuleKind::Allocator }) } else { diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 90520f77e3c..48c07b00894 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -109,6 +109,7 @@ fn codegen_allocator<'tcx>( tcx: TyCtxt<'tcx>, mods: &mut Self::Module, kind: AllocatorKind, + has_alloc_error_handler: bool, ); /// This generates the codegen unit and returns it along with /// a `u64` giving an estimate of the unit's processing cost. diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 060efd270dd..5cd0a56d524 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -593,6 +593,9 @@ pub fn set(&self, features: &mut Features, span: Span) { /// Allows to use the `#[cmse_nonsecure_entry]` attribute. (active, cmse_nonsecure_entry, "1.48.0", Some(75835), None), + /// Allows rustc to inject a default alloc_error_handler + (active, default_alloc_error_handler, "1.48.0", Some(66741), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index 6bc2110bfb3..effb25b0224 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -64,7 +64,10 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) { if item == LangItem::PanicImpl { tcx.sess.err("`#[panic_handler]` function required, but not found"); } else if item == LangItem::Oom { - tcx.sess.err("`#[alloc_error_handler]` function required, but not found"); + if !tcx.features().default_alloc_error_handler { + tcx.sess.err("`#[alloc_error_handler]` function required, but not found."); + tcx.sess.note_without_error("Use `#![feature(default_alloc_error_handler)]` for a default error handler."); + } } else { tcx.sess.err(&format!("language item required, but not found: `{}`", name)); } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index e3ad31469b2..6309b00f5f5 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -415,6 +415,7 @@ decl_macro, declare_lint_pass, decode, + default_alloc_error_handler, default_lib_allocator, default_type_parameter_fallback, default_type_params, diff --git a/library/alloc/src/alloc.rs b/library/alloc/src/alloc.rs index 75158eefcac..ce70de6ebdd 100644 --- a/library/alloc/src/alloc.rs +++ b/library/alloc/src/alloc.rs @@ -26,6 +26,8 @@ fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8; #[rustc_allocator_nounwind] fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; + #[rustc_allocator_nounwind] + fn __rust_alloc_error_handler(size: usize, align: usize) -> !; } /// The global memory allocator. @@ -334,6 +336,24 @@ pub(crate) unsafe fn box_free(ptr: Unique) { /// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html /// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html #[stable(feature = "global_alloc", since = "1.28.0")] +#[cfg(not(any(test, bootstrap)))] +#[rustc_allocator_nounwind] +pub fn handle_alloc_error(layout: Layout) -> ! { + unsafe { + __rust_alloc_error_handler(layout.size(), layout.align()); + } +} + +// For alloc test `std::alloc::handle_alloc_error` can be used directly. +#[cfg(test)] +pub use std::alloc::handle_alloc_error; + +// In stage0 (bootstrap) `__rust_alloc_error_handler`, +// might not be generated yet, because an old compiler is used, +// so use the old direct call. +#[cfg(all(bootstrap, not(test)))] +#[stable(feature = "global_alloc", since = "1.28.0")] +#[doc(hidden)] #[rustc_allocator_nounwind] pub fn handle_alloc_error(layout: Layout) -> ! { extern "Rust" { @@ -342,3 +362,30 @@ pub fn handle_alloc_error(layout: Layout) -> ! { } unsafe { oom_impl(layout) } } + +#[cfg(not(any(test, bootstrap)))] +#[doc(hidden)] +#[allow(unused_attributes)] +#[unstable(feature = "alloc_internals", issue = "none")] +pub mod __default_lib_allocator { + use crate::alloc::Layout; + + // called via generated `__rust_alloc_error_handler` + + // if there is no `#[alloc_error_handler]` + #[rustc_std_internal_symbol] + pub unsafe extern "C" fn __rdl_oom(size: usize, _align: usize) -> ! { + panic!("memory allocation of {} bytes failed", size) + } + + // if there is a `#[alloc_error_handler]` + #[rustc_std_internal_symbol] + pub unsafe extern "C" fn __rg_oom(size: usize, align: usize) -> ! { + let layout = unsafe { Layout::from_size_align_unchecked(size, align) }; + extern "Rust" { + #[lang = "oom"] + fn oom_impl(layout: Layout) -> !; + } + unsafe { oom_impl(layout) } + } +} diff --git a/src/test/ui/allocator/auxiliary/helper.rs b/src/test/ui/allocator/auxiliary/helper.rs index 7f6770c226a..008fb3501d9 100644 --- a/src/test/ui/allocator/auxiliary/helper.rs +++ b/src/test/ui/allocator/auxiliary/helper.rs @@ -1,8 +1,10 @@ // no-prefer-dynamic #![crate_type = "rlib"] +#![no_std] -use std::fmt; +extern crate alloc; +use alloc::fmt; pub fn work_with(p: &fmt::Debug) { drop(p); diff --git a/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs b/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs new file mode 100644 index 00000000000..f09fafbc98a --- /dev/null +++ b/src/test/ui/allocator/no_std-alloc-error-handler-custom.rs @@ -0,0 +1,97 @@ +// run-pass +// ignore-android no libc +// ignore-cloudabi no libc +// ignore-emscripten no libc +// ignore-sgx no libc +// ignore-wasm32 no libc +// only-linux +// compile-flags:-C panic=abort +// aux-build:helper.rs + +#![feature(start, rustc_private, new_uninit, panic_info_message)] +#![feature(alloc_error_handler)] +#![no_std] + +extern crate alloc; +extern crate libc; + +// ARM targets need these symbols +#[no_mangle] +pub fn __aeabi_unwind_cpp_pr0() {} + +#[no_mangle] +pub fn __aeabi_unwind_cpp_pr1() {} + +use core::ptr::null_mut; +use core::alloc::{GlobalAlloc, Layout}; +use alloc::boxed::Box; + +extern crate helper; + +struct MyAllocator; + +#[alloc_error_handler] +fn my_oom(layout: Layout) -> ! +{ + use alloc::fmt::write; + unsafe { + let size = layout.size(); + let mut s = alloc::string::String::new(); + write(&mut s, format_args!("My OOM: failed to allocate {} bytes!\n", size)).unwrap(); + let s = s.as_str(); + libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len()); + libc::exit(0) + } +} + +unsafe impl GlobalAlloc for MyAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if layout.size() < 4096 { + libc::malloc(layout.size()) as _ + } else { + null_mut() + } + } + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +} + +#[global_allocator] +static A: MyAllocator = MyAllocator; + +#[panic_handler] +fn panic(panic_info: &core::panic::PanicInfo) -> ! { + unsafe { + if let Some(s) = panic_info.payload().downcast_ref::<&str>() { + const PSTR: &str = "panic occurred: "; + const CR: &str = "\n"; + libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len()); + libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len()); + libc::write(libc::STDERR_FILENO, CR as *const _ as _, CR.len()); + } + if let Some(args) = panic_info.message() { + let mut s = alloc::string::String::new(); + alloc::fmt::write(&mut s, *args).unwrap(); + let s = s.as_str(); + const PSTR: &str = "panic occurred: "; + const CR: &str = "\n"; + libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len()); + libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len()); + libc::write(libc::STDERR_FILENO, CR as *const _ as _, CR.len()); + } else { + const PSTR: &str = "panic occurred\n"; + libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len()); + } + libc::exit(1) + } +} + +#[derive(Debug)] +struct Page([[u64; 32]; 16]); + +#[start] +pub fn main(_argc: isize, _argv: *const *const u8) -> isize { + let zero = Box::::new_zeroed(); + let zero = unsafe { zero.assume_init() }; + helper::work_with(&zero); + 1 +} diff --git a/src/test/ui/allocator/no_std-alloc-error-handler-default.rs b/src/test/ui/allocator/no_std-alloc-error-handler-default.rs new file mode 100644 index 00000000000..4d68160379d --- /dev/null +++ b/src/test/ui/allocator/no_std-alloc-error-handler-default.rs @@ -0,0 +1,84 @@ +// run-pass +// ignore-android no libc +// ignore-cloudabi no libc +// ignore-emscripten no libc +// ignore-sgx no libc +// ignore-wasm32 no libc +// only-linux +// compile-flags:-C panic=abort +// aux-build:helper.rs +// gate-test-default_alloc_error_handler + +#![feature(start, rustc_private, new_uninit, panic_info_message)] +#![feature(default_alloc_error_handler)] +#![no_std] + +extern crate alloc; +extern crate libc; + +// ARM targets need these symbols +#[no_mangle] +pub fn __aeabi_unwind_cpp_pr0() {} + +#[no_mangle] +pub fn __aeabi_unwind_cpp_pr1() {} + +use alloc::boxed::Box; +use core::alloc::{GlobalAlloc, Layout}; +use core::ptr::null_mut; + +extern crate helper; + +struct MyAllocator; + +unsafe impl GlobalAlloc for MyAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if layout.size() < 4096 { + libc::malloc(layout.size()) as _ + } else { + null_mut() + } + } + unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +} + +#[global_allocator] +static A: MyAllocator = MyAllocator; + +#[panic_handler] +fn panic(panic_info: &core::panic::PanicInfo) -> ! { + unsafe { + if let Some(s) = panic_info.payload().downcast_ref::<&str>() { + const PSTR: &str = "panic occurred: "; + const CR: &str = "\n"; + libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len()); + libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len()); + libc::write(libc::STDERR_FILENO, CR as *const _ as _, CR.len()); + } + if let Some(args) = panic_info.message() { + let mut s = alloc::string::String::new(); + alloc::fmt::write(&mut s, *args).unwrap(); + let s = s.as_str(); + const PSTR: &str = "panic occurred: "; + const CR: &str = "\n"; + libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len()); + libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len()); + libc::write(libc::STDERR_FILENO, CR as *const _ as _, CR.len()); + } else { + const PSTR: &str = "panic occurred\n"; + libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len()); + } + libc::exit(0) + } +} + +#[derive(Debug)] +struct Page([[u64; 32]; 16]); + +#[start] +pub fn main(_argc: isize, _argv: *const *const u8) -> isize { + let zero = Box::::new_zeroed(); + let zero = unsafe { zero.assume_init() }; + helper::work_with(&zero); + 1 +} diff --git a/src/test/ui/missing/missing-alloc_error_handler.stderr b/src/test/ui/missing/missing-alloc_error_handler.stderr index 5489b2cbbfa..511d0788b40 100644 --- a/src/test/ui/missing/missing-alloc_error_handler.stderr +++ b/src/test/ui/missing/missing-alloc_error_handler.stderr @@ -1,4 +1,6 @@ -error: `#[alloc_error_handler]` function required, but not found +error: `#[alloc_error_handler]` function required, but not found. + +note: Use `#![feature(default_alloc_error_handler)]` for a default error handler. error: aborting due to previous error