Skip to content

Commit 0d37dca

Browse files
committed
Auto merge of #76448 - haraldh:default_alloc_error_handler_reduced, r=Amanieu
Implement Make `handle_alloc_error` default to panic (for no_std + liballoc) Related: #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.
2 parents 32cbc65 + cadd12b commit 0d37dca

File tree

12 files changed

+292
-7
lines changed

12 files changed

+292
-7
lines changed

compiler/rustc_codegen_llvm/src/allocator.rs

+44-1
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ use libc::c_uint;
33
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
44
use rustc_middle::bug;
55
use rustc_middle::ty::TyCtxt;
6+
use rustc_span::symbol::sym;
67

78
use crate::llvm::{self, False, True};
89
use crate::ModuleLlvm;
910

10-
pub(crate) unsafe fn codegen(tcx: TyCtxt<'_>, mods: &mut ModuleLlvm, kind: AllocatorKind) {
11+
pub(crate) unsafe fn codegen(
12+
tcx: TyCtxt<'_>,
13+
mods: &mut ModuleLlvm,
14+
kind: AllocatorKind,
15+
has_alloc_error_handler: bool,
16+
) {
1117
let llcx = &*mods.llcx;
1218
let llmod = mods.llmod();
1319
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
8288
}
8389
llvm::LLVMDisposeBuilder(llbuilder);
8490
}
91+
92+
// rust alloc error handler
93+
let args = [usize, usize]; // size, align
94+
95+
let ty = llvm::LLVMFunctionType(void, args.as_ptr(), args.len() as c_uint, False);
96+
let name = format!("__rust_alloc_error_handler");
97+
let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, name.as_ptr().cast(), name.len(), ty);
98+
// -> ! DIFlagNoReturn
99+
llvm::Attribute::NoReturn.apply_llfn(llvm::AttributePlace::Function, llfn);
100+
101+
if tcx.sess.target.target.options.default_hidden_visibility {
102+
llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
103+
}
104+
if tcx.sess.must_emit_unwind_tables() {
105+
attributes::emit_uwtable(llfn, true);
106+
}
107+
108+
let kind = if has_alloc_error_handler { AllocatorKind::Global } else { AllocatorKind::Default };
109+
let callee = kind.fn_name(sym::oom);
110+
let callee = llvm::LLVMRustGetOrInsertFunction(llmod, callee.as_ptr().cast(), callee.len(), ty);
111+
// -> ! DIFlagNoReturn
112+
llvm::Attribute::NoReturn.apply_llfn(llvm::AttributePlace::Function, callee);
113+
llvm::LLVMRustSetVisibility(callee, llvm::Visibility::Hidden);
114+
115+
let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, llfn, "entry\0".as_ptr().cast());
116+
117+
let llbuilder = llvm::LLVMCreateBuilderInContext(llcx);
118+
llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb);
119+
let args = args
120+
.iter()
121+
.enumerate()
122+
.map(|(i, _)| llvm::LLVMGetParam(llfn, i as c_uint))
123+
.collect::<Vec<_>>();
124+
let ret = llvm::LLVMRustBuildCall(llbuilder, callee, args.as_ptr(), args.len() as c_uint, None);
125+
llvm::LLVMSetTailCall(ret, True);
126+
llvm::LLVMBuildRetVoid(llbuilder);
127+
llvm::LLVMDisposeBuilder(llbuilder);
85128
}

compiler/rustc_codegen_llvm/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,9 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
9595
tcx: TyCtxt<'tcx>,
9696
mods: &mut ModuleLlvm,
9797
kind: AllocatorKind,
98+
has_alloc_error_handler: bool,
9899
) {
99-
unsafe { allocator::codegen(tcx, mods, kind) }
100+
unsafe { allocator::codegen(tcx, mods, kind, has_alloc_error_handler) }
100101
}
101102
fn compile_codegen_unit(
102103
&self,

compiler/rustc_codegen_ssa/src/base.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -538,8 +538,9 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
538538
let llmod_id =
539539
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string();
540540
let mut modules = backend.new_metadata(tcx, &llmod_id);
541-
tcx.sess
542-
.time("write_allocator_module", || backend.codegen_allocator(tcx, &mut modules, kind));
541+
tcx.sess.time("write_allocator_module", || {
542+
backend.codegen_allocator(tcx, &mut modules, kind, tcx.lang_items().oom().is_some())
543+
});
543544

544545
Some(ModuleCodegen { name: llmod_id, module_llvm: modules, kind: ModuleKind::Allocator })
545546
} else {

compiler/rustc_codegen_ssa/src/traits/backend.rs

+1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se
109109
tcx: TyCtxt<'tcx>,
110110
mods: &mut Self::Module,
111111
kind: AllocatorKind,
112+
has_alloc_error_handler: bool,
112113
);
113114
/// This generates the codegen unit and returns it along with
114115
/// a `u64` giving an estimate of the unit's processing cost.

compiler/rustc_feature/src/active.rs

+3
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,9 @@ declare_features! (
593593
/// Allows to use the `#[cmse_nonsecure_entry]` attribute.
594594
(active, cmse_nonsecure_entry, "1.48.0", Some(75835), None),
595595

596+
/// Allows rustc to inject a default alloc_error_handler
597+
(active, default_alloc_error_handler, "1.48.0", Some(66741), None),
598+
596599
// -------------------------------------------------------------------------
597600
// feature-group-end: actual feature gates
598601
// -------------------------------------------------------------------------

compiler/rustc_passes/src/weak_lang_items.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) {
6464
if item == LangItem::PanicImpl {
6565
tcx.sess.err("`#[panic_handler]` function required, but not found");
6666
} else if item == LangItem::Oom {
67-
tcx.sess.err("`#[alloc_error_handler]` function required, but not found");
67+
if !tcx.features().default_alloc_error_handler {
68+
tcx.sess.err("`#[alloc_error_handler]` function required, but not found.");
69+
tcx.sess.note_without_error("Use `#![feature(default_alloc_error_handler)]` for a default error handler.");
70+
}
6871
} else {
6972
tcx.sess.err(&format!("language item required, but not found: `{}`", name));
7073
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,7 @@ symbols! {
415415
decl_macro,
416416
declare_lint_pass,
417417
decode,
418+
default_alloc_error_handler,
418419
default_lib_allocator,
419420
default_type_parameter_fallback,
420421
default_type_params,

library/alloc/src/alloc.rs

+47
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ extern "Rust" {
2626
fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
2727
#[rustc_allocator_nounwind]
2828
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
29+
#[rustc_allocator_nounwind]
30+
fn __rust_alloc_error_handler(size: usize, align: usize) -> !;
2931
}
3032

3133
/// The global memory allocator.
@@ -334,6 +336,24 @@ pub(crate) unsafe fn box_free<T: ?Sized>(ptr: Unique<T>) {
334336
/// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html
335337
/// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html
336338
#[stable(feature = "global_alloc", since = "1.28.0")]
339+
#[cfg(not(any(test, bootstrap)))]
340+
#[rustc_allocator_nounwind]
341+
pub fn handle_alloc_error(layout: Layout) -> ! {
342+
unsafe {
343+
__rust_alloc_error_handler(layout.size(), layout.align());
344+
}
345+
}
346+
347+
// For alloc test `std::alloc::handle_alloc_error` can be used directly.
348+
#[cfg(test)]
349+
pub use std::alloc::handle_alloc_error;
350+
351+
// In stage0 (bootstrap) `__rust_alloc_error_handler`,
352+
// might not be generated yet, because an old compiler is used,
353+
// so use the old direct call.
354+
#[cfg(all(bootstrap, not(test)))]
355+
#[stable(feature = "global_alloc", since = "1.28.0")]
356+
#[doc(hidden)]
337357
#[rustc_allocator_nounwind]
338358
pub fn handle_alloc_error(layout: Layout) -> ! {
339359
extern "Rust" {
@@ -342,3 +362,30 @@ pub fn handle_alloc_error(layout: Layout) -> ! {
342362
}
343363
unsafe { oom_impl(layout) }
344364
}
365+
366+
#[cfg(not(any(test, bootstrap)))]
367+
#[doc(hidden)]
368+
#[allow(unused_attributes)]
369+
#[unstable(feature = "alloc_internals", issue = "none")]
370+
pub mod __default_lib_allocator {
371+
use crate::alloc::Layout;
372+
373+
// called via generated `__rust_alloc_error_handler`
374+
375+
// if there is no `#[alloc_error_handler]`
376+
#[rustc_std_internal_symbol]
377+
pub unsafe extern "C" fn __rdl_oom(size: usize, _align: usize) -> ! {
378+
panic!("memory allocation of {} bytes failed", size)
379+
}
380+
381+
// if there is a `#[alloc_error_handler]`
382+
#[rustc_std_internal_symbol]
383+
pub unsafe extern "C" fn __rg_oom(size: usize, align: usize) -> ! {
384+
let layout = unsafe { Layout::from_size_align_unchecked(size, align) };
385+
extern "Rust" {
386+
#[lang = "oom"]
387+
fn oom_impl(layout: Layout) -> !;
388+
}
389+
unsafe { oom_impl(layout) }
390+
}
391+
}

src/test/ui/allocator/auxiliary/helper.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// no-prefer-dynamic
22

33
#![crate_type = "rlib"]
4+
#![no_std]
45

5-
use std::fmt;
6+
extern crate alloc;
7+
use alloc::fmt;
68

79
pub fn work_with(p: &fmt::Debug) {
810
drop(p);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// run-pass
2+
// ignore-android no libc
3+
// ignore-cloudabi no libc
4+
// ignore-emscripten no libc
5+
// ignore-sgx no libc
6+
// ignore-wasm32 no libc
7+
// only-linux
8+
// compile-flags:-C panic=abort
9+
// aux-build:helper.rs
10+
11+
#![feature(start, rustc_private, new_uninit, panic_info_message)]
12+
#![feature(alloc_error_handler)]
13+
#![no_std]
14+
15+
extern crate alloc;
16+
extern crate libc;
17+
18+
// ARM targets need these symbols
19+
#[no_mangle]
20+
pub fn __aeabi_unwind_cpp_pr0() {}
21+
22+
#[no_mangle]
23+
pub fn __aeabi_unwind_cpp_pr1() {}
24+
25+
use core::ptr::null_mut;
26+
use core::alloc::{GlobalAlloc, Layout};
27+
use alloc::boxed::Box;
28+
29+
extern crate helper;
30+
31+
struct MyAllocator;
32+
33+
#[alloc_error_handler]
34+
fn my_oom(layout: Layout) -> !
35+
{
36+
use alloc::fmt::write;
37+
unsafe {
38+
let size = layout.size();
39+
let mut s = alloc::string::String::new();
40+
write(&mut s, format_args!("My OOM: failed to allocate {} bytes!\n", size)).unwrap();
41+
let s = s.as_str();
42+
libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len());
43+
libc::exit(0)
44+
}
45+
}
46+
47+
unsafe impl GlobalAlloc for MyAllocator {
48+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
49+
if layout.size() < 4096 {
50+
libc::malloc(layout.size()) as _
51+
} else {
52+
null_mut()
53+
}
54+
}
55+
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
56+
}
57+
58+
#[global_allocator]
59+
static A: MyAllocator = MyAllocator;
60+
61+
#[panic_handler]
62+
fn panic(panic_info: &core::panic::PanicInfo) -> ! {
63+
unsafe {
64+
if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
65+
const PSTR: &str = "panic occurred: ";
66+
const CR: &str = "\n";
67+
libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len());
68+
libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len());
69+
libc::write(libc::STDERR_FILENO, CR as *const _ as _, CR.len());
70+
}
71+
if let Some(args) = panic_info.message() {
72+
let mut s = alloc::string::String::new();
73+
alloc::fmt::write(&mut s, *args).unwrap();
74+
let s = s.as_str();
75+
const PSTR: &str = "panic occurred: ";
76+
const CR: &str = "\n";
77+
libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len());
78+
libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len());
79+
libc::write(libc::STDERR_FILENO, CR as *const _ as _, CR.len());
80+
} else {
81+
const PSTR: &str = "panic occurred\n";
82+
libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len());
83+
}
84+
libc::exit(1)
85+
}
86+
}
87+
88+
#[derive(Debug)]
89+
struct Page([[u64; 32]; 16]);
90+
91+
#[start]
92+
pub fn main(_argc: isize, _argv: *const *const u8) -> isize {
93+
let zero = Box::<Page>::new_zeroed();
94+
let zero = unsafe { zero.assume_init() };
95+
helper::work_with(&zero);
96+
1
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// run-pass
2+
// ignore-android no libc
3+
// ignore-cloudabi no libc
4+
// ignore-emscripten no libc
5+
// ignore-sgx no libc
6+
// ignore-wasm32 no libc
7+
// only-linux
8+
// compile-flags:-C panic=abort
9+
// aux-build:helper.rs
10+
// gate-test-default_alloc_error_handler
11+
12+
#![feature(start, rustc_private, new_uninit, panic_info_message)]
13+
#![feature(default_alloc_error_handler)]
14+
#![no_std]
15+
16+
extern crate alloc;
17+
extern crate libc;
18+
19+
// ARM targets need these symbols
20+
#[no_mangle]
21+
pub fn __aeabi_unwind_cpp_pr0() {}
22+
23+
#[no_mangle]
24+
pub fn __aeabi_unwind_cpp_pr1() {}
25+
26+
use alloc::boxed::Box;
27+
use core::alloc::{GlobalAlloc, Layout};
28+
use core::ptr::null_mut;
29+
30+
extern crate helper;
31+
32+
struct MyAllocator;
33+
34+
unsafe impl GlobalAlloc for MyAllocator {
35+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
36+
if layout.size() < 4096 {
37+
libc::malloc(layout.size()) as _
38+
} else {
39+
null_mut()
40+
}
41+
}
42+
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
43+
}
44+
45+
#[global_allocator]
46+
static A: MyAllocator = MyAllocator;
47+
48+
#[panic_handler]
49+
fn panic(panic_info: &core::panic::PanicInfo) -> ! {
50+
unsafe {
51+
if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
52+
const PSTR: &str = "panic occurred: ";
53+
const CR: &str = "\n";
54+
libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len());
55+
libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len());
56+
libc::write(libc::STDERR_FILENO, CR as *const _ as _, CR.len());
57+
}
58+
if let Some(args) = panic_info.message() {
59+
let mut s = alloc::string::String::new();
60+
alloc::fmt::write(&mut s, *args).unwrap();
61+
let s = s.as_str();
62+
const PSTR: &str = "panic occurred: ";
63+
const CR: &str = "\n";
64+
libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len());
65+
libc::write(libc::STDERR_FILENO, s as *const _ as _, s.len());
66+
libc::write(libc::STDERR_FILENO, CR as *const _ as _, CR.len());
67+
} else {
68+
const PSTR: &str = "panic occurred\n";
69+
libc::write(libc::STDERR_FILENO, PSTR as *const _ as _, PSTR.len());
70+
}
71+
libc::exit(0)
72+
}
73+
}
74+
75+
#[derive(Debug)]
76+
struct Page([[u64; 32]; 16]);
77+
78+
#[start]
79+
pub fn main(_argc: isize, _argv: *const *const u8) -> isize {
80+
let zero = Box::<Page>::new_zeroed();
81+
let zero = unsafe { zero.assume_init() };
82+
helper::work_with(&zero);
83+
1
84+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
error: `#[alloc_error_handler]` function required, but not found
1+
error: `#[alloc_error_handler]` function required, but not found.
2+
3+
note: Use `#![feature(default_alloc_error_handler)]` for a default error handler.
24

35
error: aborting due to previous error
46

0 commit comments

Comments
 (0)