Auto merge of #122268 - ChrisDenton:no-libc, r=Mark-Simulacrum

Link MSVC default lib in core

## The Problem

On Windows MSVC, Rust invokes the linker directly. This means only the objects and libraries Rust explicitly passes to the linker are used. In short, this is equivalent to passing `-nodefaultlibs`, `-nostartfiles`, etc for gnu compilers.

To compensate for this [the libc crate links to the necessary libraries](a0f5b4b213/src/windows/mod.rs (L258-L261)). The libc crate is then linked from std, thus when you use std you get the defaults back.or integrate with C/C++.

However, this has a few problems:

- For `no_std`, users are left to manually pass the default lib to the linker
- Whereas `std` has the opposite problem, using [`/nodefaultlib`](https://learn.microsoft.com/en-us/cpp/build/reference/nodefaultlib-ignore-libraries?view=msvc-170) doesn't work as expected because Rust treats them as normal libs. This is a particular problem when you want to use e.g. the debug CRT libraries in their place or integrate with C/C++..

## The solution

This PR fixes this in two ways:

- moves linking the default lib into `core`
- passes the lib to the linker using [`/defaultlib`](https://learn.microsoft.com/en-us/cpp/build/reference/defaultlib-specify-default-library?view=msvc-170). This allows users to override it in the normal way (i.e. with [`/nodefaultlib`](https://learn.microsoft.com/en-us/cpp/build/reference/nodefaultlib-ignore-libraries?view=msvc-170)).

This is more or less equivalent to what the MSVC C compiler does. You can see what this looks like in my second commit, which I'll reproduce here for convenience:

```rust
// In library/core
#[cfg(all(windows, target_env = "msvc"))]
#[link(
    name = "/defaultlib:msvcrt",
    modifiers = "+verbatim",
    cfg(not(target_feature = "crt-static"))
)]
#[link(name = "/defaultlib:libcmt", modifiers = "+verbatim", cfg(target_feature = "crt-static"))]
extern "C" {}
```

## Alternatives

- Add the above to `unwind` and `std` but not `core`
- The status quo
- Some other kind of compiler magic maybe

This bares some discussion so I've t-libs nominated it.
This commit is contained in:
bors 2024-04-14 13:28:21 +00:00
commit a8a88fe524
16 changed files with 46 additions and 15 deletions

View file

@ -609,3 +609,13 @@ fn drop(&mut self) {
#[rustc_nounwind]
fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
}
// Link the MSVC default lib
#[cfg(all(windows, target_env = "msvc"))]
#[link(
name = "/defaultlib:msvcrt",
modifiers = "+verbatim",
cfg(not(target_feature = "crt-static"))
)]
#[link(name = "/defaultlib:libcmt", modifiers = "+verbatim", cfg(target_feature = "crt-static"))]
extern "C" {}

View file

@ -178,6 +178,7 @@
#![feature(ip_bits)]
#![feature(is_ascii_octdigit)]
#![feature(isqrt)]
#![feature(link_cfg)]
#![feature(maybe_uninit_uninit_array)]
#![feature(non_null_convenience)]
#![feature(offset_of_enum)]

View file

@ -15,5 +15,7 @@ doc = false
alloc = { path = "../alloc" }
cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
core = { path = "../core" }
libc = { version = "0.2", default-features = false }
compiler_builtins = "0.1.0"
[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
libc = { version = "0.2", default-features = false }

View file

@ -14,7 +14,9 @@ doc = false
[dependencies]
alloc = { path = "../alloc" }
core = { path = "../core" }
libc = { version = "0.2", default-features = false }
unwind = { path = "../unwind" }
compiler_builtins = "0.1.0"
cfg-if = "1.0"
[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
libc = { version = "0.2", default-features = false }

View file

@ -48,9 +48,9 @@
use alloc::boxed::Box;
use core::any::Any;
use core::ffi::{c_int, c_uint, c_void};
use core::mem::{self, ManuallyDrop};
use core::ptr::{addr_of, addr_of_mut};
use libc::{c_int, c_uint, c_void};
// NOTE(nbdd0121): The `canary` field is part of stable ABI.
#[repr(C)]

View file

@ -17,7 +17,6 @@ cfg-if = { version = "1.0", features = ['rustc-dep-of-std'] }
panic_unwind = { path = "../panic_unwind", optional = true }
panic_abort = { path = "../panic_abort" }
core = { path = "../core", public = true }
libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true }
compiler_builtins = { version = "0.1.105" }
profiler_builtins = { path = "../profiler_builtins", optional = true }
unwind = { path = "../unwind" }
@ -31,6 +30,12 @@ rustc-demangle = { version = "0.1.21", features = ['rustc-dep-of-std'] }
miniz_oxide = { version = "0.7.0", optional = true, default-features = false }
addr2line = { version = "0.21.0", optional = true, default-features = false }
[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
libc = { version = "0.2.153", default-features = false, features = ['rustc-dep-of-std'], public = true }
[target.'cfg(all(windows, target_env = "msvc"))'.dependencies]
libc = { version = "0.2.153", default-features = false }
[target.'cfg(all(not(target_os = "aix"), not(all(windows, target_env = "msvc", not(target_vendor = "uwp")))))'.dependencies]
object = { version = "0.32.0", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] }

View file

@ -431,6 +431,9 @@
#[allow(unused_imports)] // macros from `alloc` are not used on all platforms
#[macro_use]
extern crate alloc as alloc_crate;
// Many compiler tests depend on libc being pulled in by std
// so include it here even if it's unused.
#[doc(masked)]
#[allow(unused_extern_crates)]
extern crate libc;

View file

@ -11,7 +11,7 @@
#[cfg(not(unix))]
#[allow(non_camel_case_types)]
mod libc {
pub use libc::c_int;
pub use core::ffi::c_int;
pub type socklen_t = u32;
pub struct sockaddr;
#[derive(Clone)]

View file

@ -20,7 +20,7 @@
))]
#[allow(non_camel_case_types)]
mod libc {
pub use libc::c_int;
pub use core::ffi::c_int;
pub struct ucred;
pub struct cmsghdr;
pub struct sockcred2;

View file

@ -34,7 +34,7 @@
target_os = "haiku",
target_os = "nto",
)))]
const MSG_NOSIGNAL: libc::c_int = 0x0;
const MSG_NOSIGNAL: core::ffi::c_int = 0x0;
/// A Unix datagram socket.
///
@ -317,7 +317,7 @@ pub fn peer_addr(&self) -> io::Result<SocketAddr> {
fn recv_from_flags(
&self,
buf: &mut [u8],
flags: libc::c_int,
flags: core::ffi::c_int,
) -> io::Result<(usize, SocketAddr)> {
let mut count = 0;
let addr = SocketAddr::new(|addr, len| unsafe {

View file

@ -79,14 +79,14 @@ pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> {
target_os = "espidf",
target_os = "horizon"
))]
const backlog: libc::c_int = 128;
const backlog: core::ffi::c_int = 128;
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "openbsd",
target_os = "macos"
))]
const backlog: libc::c_int = -1;
const backlog: core::ffi::c_int = -1;
#[cfg(not(any(
target_os = "windows",
target_os = "redox",
@ -138,9 +138,9 @@ pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result<UnixListener> {
unsafe {
let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?;
#[cfg(target_os = "linux")]
const backlog: libc::c_int = -1;
const backlog: core::ffi::c_int = -1;
#[cfg(not(target_os = "linux"))]
const backlog: libc::c_int = 128;
const backlog: core::ffi::c_int = 128;
cvt(libc::bind(
inner.as_raw_fd(),
core::ptr::addr_of!(socket_addr.addr) as *const _,

View file

@ -9,4 +9,6 @@ std = { path = "../std" }
core = { path = "../core" }
panic_unwind = { path = "../panic_unwind" }
panic_abort = { path = "../panic_abort" }
[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
libc = { version = "0.2.150", default-features = false }

View file

@ -15,10 +15,12 @@ doc = false
[dependencies]
core = { path = "../core" }
libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false }
compiler_builtins = "0.1.0"
cfg-if = "1.0"
[target.'cfg(not(all(windows, target_env = "msvc")))'.dependencies]
libc = { version = "0.2.140", features = ['rustc-dep-of-std'], default-features = false }
[target.'cfg(target_os = "xous")'.dependencies]
unwinding = { version = "0.2.1", features = ['rustc-dep-of-std', 'unwinder', 'fde-custom'], default-features = false }

View file

@ -12,6 +12,10 @@
)]
#![allow(internal_features)]
// Force libc to be included even if unused. This is required by many platforms.
#[cfg(not(all(windows, target_env = "msvc")))]
extern crate libc as _;
cfg_if::cfg_if! {
if #[cfg(target_env = "msvc")] {
// Windows MSVC no extra unwinder support needed

View file

@ -1,6 +1,6 @@
#![allow(nonstandard_style)]
use libc::{c_int, c_void};
use core::ffi::{c_int, c_void};
#[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq)]

View file

@ -1,6 +1,6 @@
#![allow(nonstandard_style)]
use libc::{c_int, c_void};
use core::ffi::{c_int, c_void};
#[repr(C)]
#[derive(Copy, Clone, PartialEq)]