Skip to content

Commit 57cea6c

Browse files
committed
Auto merge of #86844 - bjorn3:global_alloc_improvements, r=pnkfelix
Support #[global_allocator] without the allocator shim This makes it possible to use liballoc/libstd in combination with `--emit obj` if you use `#[global_allocator]`. This is what rust-for-linux uses right now and systemd may use in the future. Currently they have to depend on the exact implementation of the allocator shim to create one themself as `--emit obj` doesn't create an allocator shim. Note that currently the allocator shim also defines the oom error handler, which is normally required too. Once `#![feature(default_alloc_error_handler)]` becomes the only option, this can be avoided. In addition when using only fallible allocator methods and either `--cfg no_global_oom_handling` for liballoc (like rust-for-linux) or `--gc-sections` no references to the oom error handler will exist. To avoid this feature being insta-stable, you will have to define `__rust_no_alloc_shim_is_unstable` to avoid linker errors. (Labeling this with both T-compiler and T-lang as it originally involved both an implementation detail and had an insta-stable user facing change. As noted above, the `__rust_no_alloc_shim_is_unstable` symbol requirement should prevent unintended dependence on this unstable feature.)
2 parents 1d8d72d + 4239a5c commit 57cea6c

File tree

6 files changed

+51
-38
lines changed

6 files changed

+51
-38
lines changed

src/machine.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,10 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
651651

652652
/// Sets up the "extern statics" for this machine.
653653
fn init_extern_statics(this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
654+
// "__rust_no_alloc_shim_is_unstable"
655+
let val = ImmTy::from_int(0, this.machine.layouts.u8);
656+
Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?;
657+
654658
match this.tcx.sess.target.os.as_ref() {
655659
"linux" => {
656660
// "environ"

src/shims/foreign_items.rs

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
347347
/// Emulates calling the internal __rust_* allocator functions
348348
fn emulate_allocator(
349349
&mut self,
350-
symbol: Symbol,
351350
default: impl FnOnce(&mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx>,
352351
) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
353352
let this = self.eval_context_mut();
@@ -359,11 +358,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
359358

360359
match allocator_kind {
361360
AllocatorKind::Global => {
362-
let (body, instance) = this
363-
.lookup_exported_symbol(symbol)?
364-
.expect("symbol should be present if there is a global allocator");
365-
366-
Ok(EmulateByNameResult::MirBody(body, instance))
361+
// When `#[global_allocator]` is used, `__rust_*` is defined by the macro expansion
362+
// of this attribute. As such we have to call an exported Rust function,
363+
// and not execute any Miri shim. Somewhat unintuitively doing so is done
364+
// by returning `NotSupported`, which triggers the `lookup_exported_symbol`
365+
// fallback case in `emulate_foreign_item`.
366+
return Ok(EmulateByNameResult::NotSupported);
367367
}
368368
AllocatorKind::Default => {
369369
default(this)?;
@@ -558,11 +558,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
558558

559559
// Rust allocation
560560
"__rust_alloc" | "miri_alloc" => {
561-
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
562-
let size = this.read_target_usize(size)?;
563-
let align = this.read_target_usize(align)?;
564-
565561
let default = |this: &mut MiriInterpCx<'mir, 'tcx>| {
562+
// Only call `check_shim` when `#[global_allocator]` isn't used. When that
563+
// macro is used, we act like no shim exists, so that the exported function can run.
564+
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
565+
let size = this.read_target_usize(size)?;
566+
let align = this.read_target_usize(align)?;
567+
566568
Self::check_alloc_request(size, align)?;
567569

568570
let memory_kind = match link_name.as_str() {
@@ -581,8 +583,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
581583
};
582584

583585
match link_name.as_str() {
584-
"__rust_alloc" =>
585-
return this.emulate_allocator(Symbol::intern("__rg_alloc"), default),
586+
"__rust_alloc" => return this.emulate_allocator(default),
586587
"miri_alloc" => {
587588
default(this)?;
588589
return Ok(EmulateByNameResult::NeedsJumping);
@@ -591,11 +592,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
591592
}
592593
}
593594
"__rust_alloc_zeroed" => {
594-
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
595-
let size = this.read_target_usize(size)?;
596-
let align = this.read_target_usize(align)?;
595+
return this.emulate_allocator(|this| {
596+
// See the comment for `__rust_alloc` why `check_shim` is only called in the
597+
// default case.
598+
let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
599+
let size = this.read_target_usize(size)?;
600+
let align = this.read_target_usize(align)?;
597601

598-
return this.emulate_allocator(Symbol::intern("__rg_alloc_zeroed"), |this| {
599602
Self::check_alloc_request(size, align)?;
600603

601604
let ptr = this.allocate_ptr(
@@ -614,12 +617,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
614617
});
615618
}
616619
"__rust_dealloc" | "miri_dealloc" => {
617-
let [ptr, old_size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
618-
let ptr = this.read_pointer(ptr)?;
619-
let old_size = this.read_target_usize(old_size)?;
620-
let align = this.read_target_usize(align)?;
621-
622620
let default = |this: &mut MiriInterpCx<'mir, 'tcx>| {
621+
// See the comment for `__rust_alloc` why `check_shim` is only called in the
622+
// default case.
623+
let [ptr, old_size, align] =
624+
this.check_shim(abi, Abi::Rust, link_name, args)?;
625+
let ptr = this.read_pointer(ptr)?;
626+
let old_size = this.read_target_usize(old_size)?;
627+
let align = this.read_target_usize(align)?;
628+
623629
let memory_kind = match link_name.as_str() {
624630
"__rust_dealloc" => MiriMemoryKind::Rust,
625631
"miri_dealloc" => MiriMemoryKind::Miri,
@@ -635,8 +641,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
635641
};
636642

637643
match link_name.as_str() {
638-
"__rust_dealloc" =>
639-
return this.emulate_allocator(Symbol::intern("__rg_dealloc"), default),
644+
"__rust_dealloc" => {
645+
return this.emulate_allocator(default);
646+
}
640647
"miri_dealloc" => {
641648
default(this)?;
642649
return Ok(EmulateByNameResult::NeedsJumping);
@@ -645,15 +652,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
645652
}
646653
}
647654
"__rust_realloc" => {
648-
let [ptr, old_size, align, new_size] =
649-
this.check_shim(abi, Abi::Rust, link_name, args)?;
650-
let ptr = this.read_pointer(ptr)?;
651-
let old_size = this.read_target_usize(old_size)?;
652-
let align = this.read_target_usize(align)?;
653-
let new_size = this.read_target_usize(new_size)?;
654-
// No need to check old_size; we anyway check that they match the allocation.
655+
return this.emulate_allocator(|this| {
656+
// See the comment for `__rust_alloc` why `check_shim` is only called in the
657+
// default case.
658+
let [ptr, old_size, align, new_size] =
659+
this.check_shim(abi, Abi::Rust, link_name, args)?;
660+
let ptr = this.read_pointer(ptr)?;
661+
let old_size = this.read_target_usize(old_size)?;
662+
let align = this.read_target_usize(align)?;
663+
let new_size = this.read_target_usize(new_size)?;
664+
// No need to check old_size; we anyway check that they match the allocation.
655665

656-
return this.emulate_allocator(Symbol::intern("__rg_realloc"), |this| {
657666
Self::check_alloc_request(new_size, align)?;
658667

659668
let align = Align::from_bytes(align).unwrap();

tests/fail/memleak.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: memory leaked: ALLOC (Rust heap, size: 4, align: 4), allocated here:
22
--> RUSTLIB/alloc/src/alloc.rs:LL:CC
33
|
4-
LL | unsafe { __rust_alloc(layout.size(), layout.align()) }
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
LL | __rust_alloc(layout.size(), layout.align())
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
88
= note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC

tests/fail/memleak_rc.32bit.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: memory leaked: ALLOC (Rust heap, size: 16, align: 4), allocated here:
22
--> RUSTLIB/alloc/src/alloc.rs:LL:CC
33
|
4-
LL | unsafe { __rust_alloc(layout.size(), layout.align()) }
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
LL | __rust_alloc(layout.size(), layout.align())
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
88
= note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC

tests/fail/memleak_rc.64bit.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error: memory leaked: ALLOC (Rust heap, size: 32, align: 8), allocated here:
22
--> RUSTLIB/alloc/src/alloc.rs:LL:CC
33
|
4-
LL | unsafe { __rust_alloc(layout.size(), layout.align()) }
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4+
LL | __rust_alloc(layout.size(), layout.align())
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66
|
77
= note: inside `std::alloc::alloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
88
= note: inside `std::alloc::Global::alloc_impl` at RUSTLIB/alloc/src/alloc.rs:LL:CC

tests/pass/shims/fs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ fn main() {
3131
}
3232

3333
fn host_to_target_path(path: String) -> PathBuf {
34-
use std::ffi::{CStr, CString};
34+
use std::ffi::{c_char, CStr, CString};
3535

3636
let path = CString::new(path).unwrap();
3737
let mut out = Vec::with_capacity(1024);

0 commit comments

Comments
 (0)