Repository: rust
Issue: rustc doesn't handle libc dependencies it introduces on its own
Labels: A-linkage,T-compiler,C-bug

As of currently nightly, a minimalist `no_std` program can look like the following (panic is purposefully badly handled):

```rust
#![no_std]
#![no_main]
#![feature(panic_implementation)]

#[panic_implementation]
#[no_mangle]
pub fn panic_impl(_: &core::panic::PanicInfo) -> ! { loop {}}

#[no_mangle]
pub extern "C" fn main(_argc: isize, _arg: *const *const u8) -> isize {
    0
}
```

This actually fails to link with:
```
  = note: /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o: In function `_start':
          (.text+0x12): undefined reference to `__libc_csu_fini'
          (.text+0x19): undefined reference to `__libc_csu_init'
          (.text+0x26): undefined reference to `__libc_start_main'
          collect2: error: ld returned 1 exit status
```
(using `-C panic=abort`)

`Scrt1.o` comes from the ld command line the rust compiler emits, so it should arguably handle the libc dependency itself.

Somehow, when using the `alloc` library (which may be stabilized soon per RFC 2480), libc gets pulled in, so the following builds:

```rust
#![no_std]
#![no_main]
#![feature(alloc, lang_items, panic_implementation)]

extern crate alloc;

// won't be necessary after PR #51607
#[lang = "oom"]
#[no_mangle]
pub fn oom() -> ! { loop {}}

#[panic_implementation]
#[no_mangle]
pub fn panic_impl(_: &core::panic::PanicInfo) -> ! { loop {}}

#[no_mangle]
pub extern "C" fn main(_argc: isize, _arg: *const *const u8) -> isize {
    0
}
```

Presumably, this happens because, without your own `#[global_allocator]`, you end up linking jemalloc in, which pulls libc.

So, let's now see what happens when you set your own `#[global_allocator]`:

```rust
#![no_std]
#![no_main]
#![feature(alloc, lang_items, panic_implementation)]

extern crate alloc;

use alloc::alloc::{GlobalAlloc, Layout};

// won't be necessary after PR #51607
#[lang = "oom"]
#[no_mangle]
pub fn oom() -> ! { loop {}}

#[panic_implementation]
#[no_mangle]
pub fn panic_impl(_: &core::panic::PanicInfo) -> ! { loop {}}

#[no_mangle]
pub extern "C" fn main(_argc: isize, _arg: *const *const u8) -> isize {
    0
}

struct MyAlloc;

unsafe impl GlobalAlloc for MyAlloc {
    unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
        core::ptr::null_mut()
    }

    unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
    }
}

#[global_allocator]
static GLOBAL: MyAlloc = MyAlloc;
```

Now we're back to the same linkage error as originally, because we're not pulling jemalloc anymore.

But then, some `core` operations can also actually use libc functions, and that adds more undefined references to symbols. For instance, adding the following to `main`:
```rust
unsafe { GLOBAL.alloc_zeroed(Layout::new::<[u8; 42]>()); }
```
(which you'd normally get from using e.g. `Vec`)

adds the following error:
```
r6-317d481089b8c8fe83113de504472633.rs:(.text._ZN4core5alloc11GlobalAlloc12alloc_zeroed17h8bac7be5ef64acd6E+0x6c): undefined reference to `memset'
```

Because `GlobalAlloc::alloc_zeroed` uses `ptr::write_bytes`, which is really `intrinsics::write_bytes`, which apparently uses `memset` under the hood.