Skip to content

Linker failure when global allocator overridden in lib.rs #112715

Closed

Description

I have a project that overrides the global allocator in lib.rs, and recently noticed that I can no longer link the test binary for my project with recent beta/nightly compilers. cargo bisect-rustc points to a2b1646 which certainly looks relevant. I reproduced the error using a slightly modified example from the global allocator documentation: https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html#example

If I move the allocator definition from lib.rs to main.rs, I no longer have a problem. Is that the right thing to do? If so, do I need to do anything else to make sure my tests use the desired allocator?

Code

I tried this code:

// main.rs
fn main() {
    testalloc::f();
}

// lib.rs
use std::alloc::{GlobalAlloc, Layout};
use std::cell::UnsafeCell;
use std::ptr::null_mut;
use std::sync::atomic::{
    AtomicUsize,
    Ordering::{Acquire, SeqCst},
};

const ARENA_SIZE: usize = 128 * 1024;
const MAX_SUPPORTED_ALIGN: usize = 4096;
#[repr(C, align(4096))] // 4096 == MAX_SUPPORTED_ALIGN
struct SimpleAllocator {
    arena: UnsafeCell<[u8; ARENA_SIZE]>,
    remaining: AtomicUsize, // we allocate from the top, counting down
}

pub const NO_ALLOC_SHIM_IS_UNSTABLE: &str = "__rust_no_alloc_shim_is_unstable";
#[global_allocator]
static ALLOCATOR: SimpleAllocator = SimpleAllocator {
    arena: UnsafeCell::new([0x55; ARENA_SIZE]),
    remaining: AtomicUsize::new(ARENA_SIZE),
};

unsafe impl Sync for SimpleAllocator {}

unsafe impl GlobalAlloc for SimpleAllocator {
    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
        let size = layout.size();
        let align = layout.align();

        // `Layout` contract forbids making a `Layout` with align=0, or align not power of 2.
        // So we can safely use a mask to ensure alignment without worrying about UB.
        let align_mask_to_round_down = !(align - 1);

        if align > MAX_SUPPORTED_ALIGN {
            return null_mut();
        }

        let mut allocated = 0;
        if self
            .remaining
            .fetch_update(SeqCst, SeqCst, |mut remaining| {
                if size > remaining {
                    return None;
                }
                remaining -= size;
                remaining &= align_mask_to_round_down;
                allocated = remaining;
                Some(remaining)
            })
            .is_err()
        {
            return null_mut();
        };
        self.arena.get().cast::<u8>().add(allocated)
    }
    unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
}

pub fn f() {
    let _s = format!("allocating a string!");
    let currently = ALLOCATOR.remaining.load(Acquire);
    println!("allocated so far: {}", ARENA_SIZE - currently);
}

#[cfg(test)]
mod test {}

I expected to see this happen:

$ cargo t
   Compiling testalloc v0.1.0 (/home/matt/src/testalloc)
    Finished test [unoptimized + debuginfo] target(s) in 0.36s

Instead, this happened:

$ cargo t
   Compiling testalloc v0.1.0 (/home/matt/src/testalloc)
error: linking with `cc` failed: exit status: 1

Version it worked on

searched nightlies: from nightly-2023-05-25 to nightly-2023-06-16
regressed nightly: nightly-2023-05-26
searched commit range: c373194...a2b1646
regressed commit: a2b1646

bisected with cargo-bisect-rustc v0.6.6

Host triple: x86_64-unknown-linux-gnu
Reproduce with:

cargo bisect-rustc -- test

@rustbot modify labels: +regression-from-stable-to-beta -regression-untriaged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    A-allocatorsArea: Custom and system allocatorsA-linkageArea: linking into static, shared libraries and binariesC-bugCategory: This is a bug.P-highHigh priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-betaPerformance or correctness regression from stable to beta.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions