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.
This commit is contained in:
parent
f283d3f02c
commit
cadd12b5f0
|
@ -3,11 +3,17 @@ use libc::c_uint;
|
||||||
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
|
use rustc_ast::expand::allocator::{AllocatorKind, AllocatorTy, ALLOCATOR_METHODS};
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
use rustc_span::symbol::sym;
|
||||||
|
|
||||||
use crate::llvm::{self, False, True};
|
use crate::llvm::{self, False, True};
|
||||||
use crate::ModuleLlvm;
|
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 llcx = &*mods.llcx;
|
||||||
let llmod = mods.llmod();
|
let llmod = mods.llmod();
|
||||||
let usize = match &tcx.sess.target.target.target_pointer_width[..] {
|
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);
|
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::<Vec<_>>();
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -95,8 +95,9 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
mods: &mut ModuleLlvm,
|
mods: &mut ModuleLlvm,
|
||||||
kind: AllocatorKind,
|
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(
|
fn compile_codegen_unit(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -538,8 +538,9 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
|
||||||
let llmod_id =
|
let llmod_id =
|
||||||
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string();
|
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string();
|
||||||
let mut modules = backend.new_metadata(tcx, &llmod_id);
|
let mut modules = backend.new_metadata(tcx, &llmod_id);
|
||||||
tcx.sess
|
tcx.sess.time("write_allocator_module", || {
|
||||||
.time("write_allocator_module", || backend.codegen_allocator(tcx, &mut modules, kind));
|
backend.codegen_allocator(tcx, &mut modules, kind, tcx.lang_items().oom().is_some())
|
||||||
|
});
|
||||||
|
|
||||||
Some(ModuleCodegen { name: llmod_id, module_llvm: modules, kind: ModuleKind::Allocator })
|
Some(ModuleCodegen { name: llmod_id, module_llvm: modules, kind: ModuleKind::Allocator })
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -109,6 +109,7 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
mods: &mut Self::Module,
|
mods: &mut Self::Module,
|
||||||
kind: AllocatorKind,
|
kind: AllocatorKind,
|
||||||
|
has_alloc_error_handler: bool,
|
||||||
);
|
);
|
||||||
/// This generates the codegen unit and returns it along with
|
/// This generates the codegen unit and returns it along with
|
||||||
/// a `u64` giving an estimate of the unit's processing cost.
|
/// a `u64` giving an estimate of the unit's processing cost.
|
||||||
|
|
|
@ -593,6 +593,9 @@ declare_features! (
|
||||||
/// Allows to use the `#[cmse_nonsecure_entry]` attribute.
|
/// Allows to use the `#[cmse_nonsecure_entry]` attribute.
|
||||||
(active, cmse_nonsecure_entry, "1.48.0", Some(75835), None),
|
(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
|
// feature-group-end: actual feature gates
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
|
@ -64,7 +64,10 @@ fn verify<'tcx>(tcx: TyCtxt<'tcx>, items: &lang_items::LanguageItems) {
|
||||||
if item == LangItem::PanicImpl {
|
if item == LangItem::PanicImpl {
|
||||||
tcx.sess.err("`#[panic_handler]` function required, but not found");
|
tcx.sess.err("`#[panic_handler]` function required, but not found");
|
||||||
} else if item == LangItem::Oom {
|
} 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 {
|
} else {
|
||||||
tcx.sess.err(&format!("language item required, but not found: `{}`", name));
|
tcx.sess.err(&format!("language item required, but not found: `{}`", name));
|
||||||
}
|
}
|
||||||
|
|
|
@ -415,6 +415,7 @@ symbols! {
|
||||||
decl_macro,
|
decl_macro,
|
||||||
declare_lint_pass,
|
declare_lint_pass,
|
||||||
decode,
|
decode,
|
||||||
|
default_alloc_error_handler,
|
||||||
default_lib_allocator,
|
default_lib_allocator,
|
||||||
default_type_parameter_fallback,
|
default_type_parameter_fallback,
|
||||||
default_type_params,
|
default_type_params,
|
||||||
|
|
|
@ -26,6 +26,8 @@ extern "Rust" {
|
||||||
fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
|
fn __rust_realloc(ptr: *mut u8, old_size: usize, align: usize, new_size: usize) -> *mut u8;
|
||||||
#[rustc_allocator_nounwind]
|
#[rustc_allocator_nounwind]
|
||||||
fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8;
|
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.
|
/// The global memory allocator.
|
||||||
|
@ -334,6 +336,24 @@ pub(crate) unsafe fn box_free<T: ?Sized>(ptr: Unique<T>) {
|
||||||
/// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html
|
/// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html
|
||||||
/// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html
|
/// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html
|
||||||
#[stable(feature = "global_alloc", since = "1.28.0")]
|
#[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]
|
#[rustc_allocator_nounwind]
|
||||||
pub fn handle_alloc_error(layout: Layout) -> ! {
|
pub fn handle_alloc_error(layout: Layout) -> ! {
|
||||||
extern "Rust" {
|
extern "Rust" {
|
||||||
|
@ -342,3 +362,30 @@ pub fn handle_alloc_error(layout: Layout) -> ! {
|
||||||
}
|
}
|
||||||
unsafe { oom_impl(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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
// no-prefer-dynamic
|
// no-prefer-dynamic
|
||||||
|
|
||||||
#![crate_type = "rlib"]
|
#![crate_type = "rlib"]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
use std::fmt;
|
extern crate alloc;
|
||||||
|
use alloc::fmt;
|
||||||
|
|
||||||
pub fn work_with(p: &fmt::Debug) {
|
pub fn work_with(p: &fmt::Debug) {
|
||||||
drop(p);
|
drop(p);
|
||||||
|
|
|
@ -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::<Page>::new_zeroed();
|
||||||
|
let zero = unsafe { zero.assume_init() };
|
||||||
|
helper::work_with(&zero);
|
||||||
|
1
|
||||||
|
}
|
|
@ -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::<Page>::new_zeroed();
|
||||||
|
let zero = unsafe { zero.assume_init() };
|
||||||
|
helper::work_with(&zero);
|
||||||
|
1
|
||||||
|
}
|
|
@ -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
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue