Support #[alloc_error_handler] without the allocator shim

Currently it is possible to avoid linking the allocator shim when
__rust_no_alloc_shim_is_unstable_v2 is defined when linking rlibs
directly as some build systems need. However this requires liballoc to
be compiled with --cfg no_global_oom_handling, which places huge
restrictions on what functions you can call and makes it impossible to
use libstd. Or alternatively you have to define
__rust_alloc_error_handler and (when using libstd)
__rust_alloc_error_handler_should_panic
using #[rustc_std_internal_symbol]. With this commit you can either use
libstd and define __rust_alloc_error_handler_should_panic or not use
libstd and use #[alloc_error_handler] instead. Both options are still
unstable though.

Eventually the alloc_error_handler may either be removed entirely
(though the PR for that has been stale for years now) or we may start
using weak symbols for it instead. For the latter case this commit is a
prerequisite anyway.
This commit is contained in:
bjorn3 2025-06-26 13:44:01 +00:00
parent f47c48048d
commit 116f4ae171
15 changed files with 83 additions and 86 deletions

View file

@ -15,13 +15,7 @@ pub fn default_fn_name(base: Symbol) -> String {
format!("__rdl_{base}")
}
pub fn alloc_error_handler_name(alloc_error_handler_kind: AllocatorKind) -> &'static str {
match alloc_error_handler_kind {
AllocatorKind::Global => "__rg_oom",
AllocatorKind::Default => "__rdl_oom",
}
}
pub const ALLOC_ERROR_HANDLER: Symbol = sym::alloc_error_handler;
pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable_v2";
pub enum AllocatorTy {

View file

@ -1,3 +1,4 @@
use rustc_ast::expand::allocator::{ALLOC_ERROR_HANDLER, global_fn_name};
use rustc_ast::{
self as ast, Fn, FnHeader, FnSig, Generics, ItemKind, Safety, Stmt, StmtKind, TyKind,
};
@ -55,7 +56,7 @@ pub(crate) fn expand(
}
// #[rustc_std_internal_symbol]
// unsafe fn __rg_oom(size: usize, align: usize) -> ! {
// unsafe fn __rust_alloc_error_handler(size: usize, align: usize) -> ! {
// handler(core::alloc::Layout::from_size_align_unchecked(size, align))
// }
fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span) -> Stmt {
@ -84,7 +85,7 @@ fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span
let kind = ItemKind::Fn(Box::new(Fn {
defaultness: ast::Defaultness::Final,
sig,
ident: Ident::from_str_and_span("__rg_oom", span),
ident: Ident::from_str_and_span(&global_fn_name(ALLOC_ERROR_HANDLER), span),
generics: Generics::default(),
contract: None,
body,

View file

@ -3,8 +3,8 @@
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
use rustc_ast::expand::allocator::{
ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
alloc_error_handler_name, default_fn_name, global_fn_name,
ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
default_fn_name, global_fn_name,
};
use rustc_codegen_ssa::base::allocator_kind_for_codegen;
use rustc_session::config::OomStrategy;
@ -72,17 +72,19 @@ fn codegen_inner(
}
}
let sig = Signature {
call_conv: module.target_config().default_call_conv,
params: vec![AbiParam::new(usize_ty), AbiParam::new(usize_ty)],
returns: vec![],
};
crate::common::create_wrapper_function(
module,
sig,
&mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind)),
);
if alloc_error_handler_kind == AllocatorKind::Default {
let sig = Signature {
call_conv: module.target_config().default_call_conv,
params: vec![AbiParam::new(usize_ty), AbiParam::new(usize_ty)],
returns: vec![],
};
crate::common::create_wrapper_function(
module,
sig,
&mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)),
&mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER)),
);
}
{
let sig = Signature {

View file

@ -2,8 +2,8 @@
use gccjit::FnAttribute;
use gccjit::{Context, FunctionType, RValue, ToRValue, Type};
use rustc_ast::expand::allocator::{
ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
alloc_error_handler_name, default_fn_name, global_fn_name,
ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
default_fn_name, global_fn_name,
};
use rustc_middle::bug;
use rustc_middle::ty::TyCtxt;
@ -61,15 +61,17 @@ pub(crate) unsafe fn codegen(
}
}
// FIXME(bjorn3): Add noreturn attribute
create_wrapper_function(
tcx,
context,
&mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))),
&[usize, usize],
None,
);
if alloc_error_handler_kind == AllocatorKind::Default {
// FIXME(bjorn3): Add noreturn attribute
create_wrapper_function(
tcx,
context,
&mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)),
Some(&mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER))),
&[usize, usize],
None,
);
}
create_const_value_function(
tcx,

View file

@ -1,7 +1,7 @@
use libc::c_uint;
use rustc_ast::expand::allocator::{
ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
alloc_error_handler_name, default_fn_name, global_fn_name,
ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorTy, NO_ALLOC_SHIM_IS_UNSTABLE,
default_fn_name, global_fn_name,
};
use rustc_codegen_ssa::traits::BaseTypeCodegenMethods as _;
use rustc_middle::bug;
@ -83,17 +83,19 @@ pub(crate) unsafe fn codegen(
}
}
// rust alloc error handler
create_wrapper_function(
tcx,
&cx,
&mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
Some(&mangle_internal_symbol(tcx, alloc_error_handler_name(alloc_error_handler_kind))),
&[usize, usize], // size, align
None,
true,
&CodegenFnAttrs::new(),
);
if alloc_error_handler_kind == AllocatorKind::Default {
// rust alloc error handler
create_wrapper_function(
tcx,
&cx,
&mangle_internal_symbol(tcx, &global_fn_name(ALLOC_ERROR_HANDLER)),
Some(&mangle_internal_symbol(tcx, &default_fn_name(ALLOC_ERROR_HANDLER))),
&[usize, usize], // size, align
None,
true,
&CodegenFnAttrs::new(),
);
}
// __rust_alloc_error_handler_should_panic_v2
create_const_value_function(

View file

@ -1,7 +1,9 @@
use std::collections::hash_map::Entry::*;
use rustc_abi::{CanonAbi, X86Call};
use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name};
use rustc_ast::expand::allocator::{
ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE, global_fn_name,
};
use rustc_data_structures::unord::UnordMap;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId};
@ -498,7 +500,7 @@ pub(crate) fn allocator_shim_symbols(
.iter()
.map(move |method| mangle_internal_symbol(tcx, global_fn_name(method.name).as_str()))
.chain([
mangle_internal_symbol(tcx, "__rust_alloc_error_handler"),
mangle_internal_symbol(tcx, global_fn_name(ALLOC_ERROR_HANDLER).as_str()),
mangle_internal_symbol(tcx, OomStrategy::SYMBOL),
mangle_internal_symbol(tcx, NO_ALLOC_SHIM_IS_UNSTABLE),
])

View file

@ -6,7 +6,7 @@ use std::str::FromStr;
use std::time::Duration;
use std::{cmp, env, iter};
use rustc_ast::expand::allocator::{AllocatorKind, alloc_error_handler_name, global_fn_name};
use rustc_ast::expand::allocator::{ALLOC_ERROR_HANDLER, AllocatorKind, global_fn_name};
use rustc_ast::{self as ast, *};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::owned_slice::OwnedSlice;
@ -1087,17 +1087,17 @@ impl CStore {
}
spans => !spans.is_empty(),
};
self.has_alloc_error_handler = match &*fn_spans(
krate,
Symbol::intern(alloc_error_handler_name(AllocatorKind::Global)),
) {
[span1, span2, ..] => {
tcx.dcx()
.emit_err(errors::NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 });
true
}
spans => !spans.is_empty(),
};
self.has_alloc_error_handler =
match &*fn_spans(krate, Symbol::intern(&global_fn_name(ALLOC_ERROR_HANDLER))) {
[span1, span2, ..] => {
tcx.dcx().emit_err(errors::NoMultipleAllocErrorHandler {
span2: *span2,
span1: *span1,
});
true
}
spans => !spans.is_empty(),
};
// Check to see if we actually need an allocator. This desire comes
// about through the `#![needs_allocator]` attribute and is typically

View file

@ -361,7 +361,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 {
unsafe extern "Rust" {
// This is the magic symbol to call the global alloc error handler. rustc generates
// it to call `__rg_oom` if there is a `#[alloc_error_handler]`, or to call the
// default implementations below (`__rdl_oom`) otherwise.
// default implementations below (`__rdl_alloc_error_handler`) otherwise.
#[rustc_std_internal_symbol]
fn __rust_alloc_error_handler(size: usize, align: usize) -> !;
}
@ -425,7 +425,7 @@ pub mod __alloc_error_handler {
// called via generated `__rust_alloc_error_handler` if there is no
// `#[alloc_error_handler]`.
#[rustc_std_internal_symbol]
pub unsafe fn __rdl_oom(size: usize, _align: usize) -> ! {
pub unsafe fn __rdl_alloc_error_handler(size: usize, _align: usize) -> ! {
unsafe extern "Rust" {
// This symbol is emitted by rustc next to __rust_alloc_error_handler.
// Its value depends on the -Zoom={panic,abort} compiler option.

View file

@ -358,9 +358,10 @@ fn default_alloc_error_hook(layout: Layout) {
// This is the default path taken on OOM, and the only path taken on stable with std.
// Crucially, it does *not* call any user-defined code, and therefore users do not have to
// worry about allocation failure causing reentrancy issues. That makes it different from
// the default `__rdl_oom` defined in alloc (i.e., the default alloc error handler that is
// called when there is no `#[alloc_error_handler]`), which triggers a regular panic and
// thus can invoke a user-defined panic hook, executing arbitrary user-defined code.
// the default `__rdl_alloc_error_handler` defined in alloc (i.e., the default alloc error
// handler that is called when there is no `#[alloc_error_handler]`), which triggers a
// regular panic and thus can invoke a user-defined panic hook, executing arbitrary
// user-defined code.
rtprintpanic!("memory allocation of {} bytes failed\n", layout.size());
}
}

View file

@ -3,7 +3,7 @@ use std::io::Write;
use std::path::Path;
use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size};
use rustc_ast::expand::allocator::alloc_error_handler_name;
use rustc_ast::expand::allocator::AllocatorKind;
use rustc_hir::attrs::Linkage;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::CrateNum;
@ -51,6 +51,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Some shims forward to other MIR bodies.
match link_name.as_str() {
// FIXME should use global_fn_name, but mangle_internal_symbol requires a static str.
name if name == this.mangle_internal_symbol("__rust_alloc_error_handler") => {
// Forward to the right symbol that implements this function.
let Some(handler_kind) = this.tcx.alloc_error_handler_kind(()) else {
@ -59,12 +60,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"`__rust_alloc_error_handler` cannot be called when no alloc error handler is set"
);
};
let name = Symbol::intern(
this.mangle_internal_symbol(alloc_error_handler_name(handler_kind)),
);
let handler =
this.lookup_exported_symbol(name)?.expect("missing alloc error handler symbol");
return interp_ok(Some(handler));
if handler_kind == AllocatorKind::Default {
let name =
Symbol::intern(this.mangle_internal_symbol("__rdl_alloc_error_handler"));
let handler = this
.lookup_exported_symbol(name)?
.expect("missing alloc error handler symbol");
return interp_ok(Some(handler));
}
}
_ => {}
}

View file

@ -7,7 +7,7 @@ LL | crate::process::abort()
|
= note: BACKTRACE:
= note: inside `std::alloc::rust_oom` at RUSTLIB/std/src/alloc.rs:LL:CC
= note: inside `std::alloc::_::__rg_oom` at RUSTLIB/std/src/alloc.rs:LL:CC
= note: inside `std::alloc::_::__rust_alloc_error_handler` at RUSTLIB/std/src/alloc.rs:LL:CC
= note: inside `std::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `std::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
note: inside `main`

View file

@ -7,7 +7,7 @@ LL | core::intrinsics::abort();
|
= note: BACKTRACE:
= note: inside `alloc_error_handler` at tests/fail/alloc/alloc_error_handler_custom.rs:LL:CC
note: inside `_::__rg_oom`
note: inside `_::__rust_alloc_error_handler`
--> tests/fail/alloc/alloc_error_handler_custom.rs:LL:CC
|
LL | #[alloc_error_handler]

View file

@ -9,7 +9,7 @@ LL | core::intrinsics::abort();
|
= note: BACKTRACE:
= note: inside `panic_handler` at tests/fail/alloc/alloc_error_handler_no_std.rs:LL:CC
= note: inside `alloc::alloc::__alloc_error_handler::__rdl_oom` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `alloc::alloc::__alloc_error_handler::__rdl_alloc_error_handler` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `alloc::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
= note: inside `alloc::alloc::handle_alloc_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
note: inside `miri_start`

View file

@ -14,5 +14,5 @@ fn main() {
let out = llvm_readobj().input("app.o").arg("--symbols").run();
out.assert_stdout_contains("rust_begin_unwind");
out.assert_stdout_contains("rust_eh_personality");
out.assert_stdout_contains("__rg_oom");
out.assert_stdout_contains("__rust_alloc_error_handler");
}

View file

@ -490,16 +490,6 @@ fun:__dfso_*=uninstrumented
fun:__dfso_*=discard
# Rust functions.
fun:__rdl_alloc=uninstrumented
fun:__rdl_alloc_zeroed=uninstrumented
fun:__rdl_dealloc=uninstrumented
fun:__rdl_realloc=uninstrumented
fun:__rg_oom=uninstrumented
fun:__rust_alloc=uninstrumented
fun:__rust_alloc_error_handler=uninstrumented
fun:__rust_alloc_zeroed=uninstrumented
fun:__rust_dealloc=uninstrumented
fun:__rust_realloc=uninstrumented
fun:_ZN4core*=uninstrumented
fun:_ZN3std*=uninstrumented
fun:rust_eh_personality=uninstrumented