rust-lang / wg-allocators

Home of the Allocators working group: Paving a path for a standard set of allocator traits to be used in collections!
http://bit.ly/hello-wg-allocators
203 stars 9 forks source link

Link against `alloc` without global allocator #123

Open wyager opened 4 months ago

wyager commented 4 months ago

Not sure if this is the right place to ask, but seems like a likely candidate.

Right now, if you link against alloc without having a global allocator, the linker fails with

no global memory allocator found but one is required; link to std or add `#[global_allocator]` to a static item that implements the GlobalAlloc trait

However, my use case of alloc is exclusively via the _in APIs; I'm only using (non-global) arena allocators.

I'm currently working around this by having a dummy global allocator, but this is not ideal for a couple reasons:

  1. I might accidentally use the global alloc new API for some alloc type in a random codepath, and it won't fail until runtime
  2. I might pull in a dependency that advertises no_std, but deep in its bowels it actually invokes the global allocator in a rarely-used codepath, causing a surprise panic at runtime. That's not hypothetical - it actually happened with one of my current project's dependencies and I had to fork it to remove any linkages against alloc.

Ideally, we would defer linker failure to the absolute last minute, so the compilation only fails if a global allocator is actually referenced in the linked binary, rather than pessimistically fail just because I'm pulling in alloc.

Is this on anyone's radar? I'm not sure if my use case (embedded programming with only non-global allocators) is representative - this might be kind of an uncommon failure mode when linking against alloc.

wyager commented 4 months ago

Someone on IRC suggested this strategy, which works perfectly.

struct FakeAllocator;

unsafe impl core::alloc::GlobalAlloc for FakeAllocator {
    unsafe fn alloc(&self, layout: core::alloc::Layout) -> *mut u8 {
        extern "Rust" {
            fn fake_alloc_this_doesnt_exist(layout: core::alloc::Layout) -> *mut u8;
        }
        fake_alloc_this_doesnt_exist(layout)
    }
    unsafe fn dealloc(&self, ptr: *mut u8, layout: core::alloc::Layout) {
        extern "Rust" {
            fn fake_dealloc_this_doesnt_exist(ptr: *mut u8, layout: core::alloc::Layout);
        }
        fake_dealloc_this_doesnt_exist(ptr, layout)
    }
}

#[global_allocator]
static ALLOCATOR: FakeAllocator = FakeAllocator;

If I don't have any global allocs, it links fine. If I add one, linking fails with note: rust-lld: error: undefined symbol: fake_dealloc_this_doesnt_exist

This makes me think that this might actually be an easier technical issue than I thought. Perhaps we can just eliminate whatever check is emitting the no global memory allocator found message and let the linker error out?