Skip to content

Commit de0f81e

Browse files
committed
std: reorganize the UNIX-internal weak module
1 parent f3fd3ef commit de0f81e

File tree

7 files changed

+131
-153
lines changed

7 files changed

+131
-153
lines changed

library/std/src/sys/pal/unix/mod.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@
22

33
use crate::io::ErrorKind;
44

5-
#[cfg(not(target_os = "espidf"))]
6-
#[macro_use]
7-
pub mod weak;
8-
95
#[cfg(target_os = "fuchsia")]
106
pub mod fuchsia;
117
pub mod futex;
@@ -19,6 +15,7 @@ pub mod stack_overflow;
1915
pub mod sync;
2016
pub mod thread_parking;
2117
pub mod time;
18+
pub mod weak;
2219

2320
#[cfg(target_os = "espidf")]
2421
pub fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}

library/std/src/sys/pal/unix/stack_overflow.rs

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ mod imp {
6969
use super::Handler;
7070
use super::thread_info::{delete_current_info, set_current_info, with_current_info};
7171
use crate::ops::Range;
72-
use crate::sync::OnceLock;
7372
use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering};
7473
use crate::sys::pal::unix::os;
7574
use crate::{io, mem, panic, ptr};
@@ -396,6 +395,10 @@ mod imp {
396395
} else if cfg!(all(target_os = "linux", target_env = "musl")) {
397396
install_main_guard_linux_musl(page_size)
398397
} else if cfg!(target_os = "freebsd") {
398+
#[cfg(not(target_os = "freebsd"))]
399+
return None;
400+
// The FreeBSD code cannot be checked on non-BSDs.
401+
#[cfg(target_os = "freebsd")]
399402
install_main_guard_freebsd(page_size)
400403
} else if cfg!(any(target_os = "netbsd", target_os = "openbsd")) {
401404
install_main_guard_bsds(page_size)
@@ -432,6 +435,7 @@ mod imp {
432435
}
433436

434437
#[forbid(unsafe_op_in_unsafe_fn)]
438+
#[cfg(target_os = "freebsd")]
435439
unsafe fn install_main_guard_freebsd(page_size: usize) -> Option<Range<usize>> {
436440
// FreeBSD's stack autogrows, and optionally includes a guard page
437441
// at the bottom. If we try to remap the bottom of the stack
@@ -443,38 +447,23 @@ mod imp {
443447
// by the security.bsd.stack_guard_page sysctl.
444448
// By default it is 1, checking once is enough since it is
445449
// a boot time config value.
446-
static PAGES: OnceLock<usize> = OnceLock::new();
450+
static PAGES: crate::sync::OnceLock<usize> = crate::sync::OnceLock::new();
447451

448452
let pages = PAGES.get_or_init(|| {
449-
use crate::sys::weak::dlsym;
450-
dlsym!(
451-
fn sysctlbyname(
452-
name: *const libc::c_char,
453-
oldp: *mut libc::c_void,
454-
oldlenp: *mut libc::size_t,
455-
newp: *const libc::c_void,
456-
newlen: libc::size_t,
457-
) -> libc::c_int;
458-
);
459453
let mut guard: usize = 0;
460454
let mut size = size_of_val(&guard);
461455
let oid = c"security.bsd.stack_guard_page";
462-
match sysctlbyname.get() {
463-
Some(fcn)
464-
if unsafe {
465-
fcn(
466-
oid.as_ptr(),
467-
(&raw mut guard).cast(),
468-
&raw mut size,
469-
ptr::null_mut(),
470-
0,
471-
) == 0
472-
} =>
473-
{
474-
guard
475-
}
476-
_ => 1,
477-
}
456+
457+
let r = unsafe {
458+
libc::sysctlbyname(
459+
oid.as_ptr(),
460+
(&raw mut guard).cast(),
461+
&raw mut size,
462+
ptr::null_mut(),
463+
0,
464+
)
465+
};
466+
if r == 0 { guard } else { 1 }
478467
});
479468
Some(guardaddr..guardaddr + pages * page_size)
480469
}
Lines changed: 4 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,15 @@
1-
//! Support for "weak linkage" to symbols on Unix
2-
//!
3-
//! Some I/O operations we do in std require newer versions of OSes but we need
4-
//! to maintain binary compatibility with older releases for now. In order to
5-
//! use the new functionality when available we use this module for detection.
6-
//!
7-
//! One option to use here is weak linkage, but that is unfortunately only
8-
//! really workable with ELF. Otherwise, use dlsym to get the symbol value at
9-
//! runtime. This is also done for compatibility with older versions of glibc,
10-
//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that
11-
//! we've been dynamically linked to the library the symbol comes from, but that
12-
//! is currently always the case for things like libpthread/libc.
13-
//!
14-
//! A long time ago this used weak linkage for the __pthread_get_minstack
15-
//! symbol, but that caused Debian to detect an unnecessarily strict versioned
16-
//! dependency on libc6 (#23628) because it is GLIBC_PRIVATE. We now use `dlsym`
17-
//! for a runtime lookup of that symbol to avoid the ELF versioned dependency.
18-
19-
// There are a variety of `#[cfg]`s controlling which targets are involved in
20-
// each instance of `weak!` and `syscall!`. Rather than trying to unify all of
21-
// that, we'll just allow that some unix targets don't use this module at all.
22-
#![allow(dead_code, unused_macros)]
23-
#![forbid(unsafe_op_in_unsafe_fn)]
24-
251
use crate::ffi::{CStr, c_char, c_void};
262
use crate::marker::{FnPtr, PhantomData};
273
use crate::sync::atomic::{Atomic, AtomicPtr, Ordering};
284
use crate::{mem, ptr};
295

30-
// We currently only test `dlsym!`, but that doesn't work on all platforms, so
31-
// we gate the tests to only the platforms where it is actually used.
32-
//
33-
// FIXME(joboet): add more tests, reorganise the whole module and get rid of
34-
// `#[allow(dead_code, unused_macros)]`.
35-
#[cfg(any(
36-
target_vendor = "apple",
37-
all(target_os = "linux", target_env = "gnu"),
38-
target_os = "freebsd",
39-
))]
406
#[cfg(test)]
7+
#[path = "./tests.rs"]
418
mod tests;
429

43-
// We can use true weak linkage on ELF targets.
44-
#[cfg(all(unix, not(target_vendor = "apple")))]
4510
pub(crate) macro weak {
4611
(fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => (
47-
let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
48-
unsafe extern "C" {
49-
#[linkage = "extern_weak"]
50-
static $name: Option<unsafe extern "C" fn($($t),*) -> $ret>;
51-
}
52-
#[allow(unused_unsafe)]
53-
ExternWeak::new(unsafe { $name })
54-
};
55-
)
56-
}
57-
58-
// On non-ELF targets, use the dlsym approximation of weak linkage.
59-
#[cfg(target_vendor = "apple")]
60-
pub(crate) use self::dlsym as weak;
61-
62-
pub(crate) struct ExternWeak<F: Copy> {
63-
weak_ptr: Option<F>,
64-
}
65-
66-
impl<F: Copy> ExternWeak<F> {
67-
#[inline]
68-
pub(crate) fn new(weak_ptr: Option<F>) -> Self {
69-
ExternWeak { weak_ptr }
70-
}
71-
72-
#[inline]
73-
pub(crate) fn get(&self) -> Option<F> {
74-
self.weak_ptr
75-
}
76-
}
77-
78-
pub(crate) macro dlsym {
79-
(fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => (
80-
dlsym!(
12+
weak!(
8113
#[link_name = stringify!($name)]
8214
fn $name($($param : $t),*) -> $ret;
8315
);
@@ -116,7 +48,7 @@ impl<F: FnPtr> DlsymWeak<F> {
11648
/// If the signature of `F` does not match the signature of the symbol (if
11749
/// it exists), calling the function pointer returned by `get()` is
11850
/// undefined behaviour.
119-
pub(crate) const unsafe fn new(name: &'static CStr) -> Self {
51+
pub const unsafe fn new(name: &'static CStr) -> Self {
12052
DlsymWeak {
12153
name: name.as_ptr(),
12254
func: AtomicPtr::new(ptr::without_provenance_mut(1)),
@@ -125,7 +57,7 @@ impl<F: FnPtr> DlsymWeak<F> {
12557
}
12658

12759
#[inline]
128-
pub(crate) fn get(&self) -> Option<F> {
60+
pub fn get(&self) -> Option<F> {
12961
// The caller is presumably going to read through this value
13062
// (by calling the function we've dlsymed). This means we'd
13163
// need to have loaded it with at least C11's consume
@@ -179,47 +111,3 @@ impl<F: FnPtr> DlsymWeak<F> {
179111

180112
unsafe impl<F> Send for DlsymWeak<F> {}
181113
unsafe impl<F> Sync for DlsymWeak<F> {}
182-
183-
#[cfg(not(any(target_os = "linux", target_os = "android")))]
184-
pub(crate) macro syscall {
185-
(fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => (
186-
unsafe fn $name($($param: $t),*) -> $ret {
187-
weak!(fn $name($($param: $t),*) -> $ret;);
188-
189-
if let Some(fun) = $name.get() {
190-
unsafe { fun($($param),*) }
191-
} else {
192-
super::os::set_errno(libc::ENOSYS);
193-
-1
194-
}
195-
}
196-
)
197-
}
198-
199-
#[cfg(any(target_os = "linux", target_os = "android"))]
200-
pub(crate) macro syscall {
201-
(
202-
fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;
203-
) => (
204-
unsafe fn $name($($param: $t),*) -> $ret {
205-
weak!(fn $name($($param: $t),*) -> $ret;);
206-
207-
// Use a weak symbol from libc when possible, allowing `LD_PRELOAD`
208-
// interposition, but if it's not found just use a raw syscall.
209-
if let Some(fun) = $name.get() {
210-
unsafe { fun($($param),*) }
211-
} else {
212-
unsafe { libc::syscall(libc::${concat(SYS_, $name)}, $($param),*) as $ret }
213-
}
214-
}
215-
)
216-
}
217-
218-
#[cfg(any(target_os = "linux", target_os = "android"))]
219-
pub(crate) macro raw_syscall {
220-
(fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => (
221-
unsafe fn $name($($param: $t),*) -> $ret {
222-
unsafe { libc::syscall(libc::${concat(SYS_, $name)}, $($param),*) as $ret }
223-
}
224-
)
225-
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//! Support for "weak linkage" to symbols on Unix
2+
//!
3+
//! Some I/O operations we do in std require newer versions of OSes but we need
4+
//! to maintain binary compatibility with older releases for now. In order to
5+
//! use the new functionality when available we use this module for detection.
6+
//!
7+
//! One option to use here is weak linkage, but that is unfortunately only
8+
//! really workable with ELF. Otherwise, use dlsym to get the symbol value at
9+
//! runtime. This is also done for compatibility with older versions of glibc,
10+
//! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that
11+
//! we've been dynamically linked to the library the symbol comes from, but that
12+
//! is currently always the case for things like libpthread/libc.
13+
//!
14+
//! A long time ago this used weak linkage for the __pthread_get_minstack
15+
//! symbol, but that caused Debian to detect an unnecessarily strict versioned
16+
//! dependency on libc6 (#23628) because it is GLIBC_PRIVATE. We now use `dlsym`
17+
//! for a runtime lookup of that symbol to avoid the ELF versioned dependency.
18+
19+
#![forbid(unsafe_op_in_unsafe_fn)]
20+
21+
cfg_select! {
22+
// On non-ELF targets, use the dlsym approximation of weak linkage.
23+
target_vendor = "apple" => {
24+
mod dlsym;
25+
pub(crate) use dlsym::weak;
26+
}
27+
28+
// Some targets don't need and support weak linkage at all...
29+
target_os = "espidf" => {}
30+
31+
// ... but ELF targets support true weak linkage.
32+
_ => {
33+
// There are a variety of `#[cfg]`s controlling which targets are involved in
34+
// each instance of `weak!`. Rather than trying to unify all of
35+
// that, we'll just allow that some unix targets don't use this macro at all.
36+
#[cfg_attr(not(target_os = "linux"), allow(unused_macros, dead_code))]
37+
mod weak_linkage;
38+
#[cfg_attr(not(target_os = "linux"), allow(unused_imports))]
39+
pub(crate) use weak_linkage::weak;
40+
}
41+
}
42+
43+
// GNU/Linux needs the `dlsym` variant to avoid linking to private glibc symbols.
44+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
45+
mod dlsym;
46+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
47+
pub(crate) use dlsym::weak as dlsym;
48+
49+
#[cfg(any(target_os = "android", target_os = "linux"))]
50+
mod syscall;
51+
#[cfg(any(target_os = "android", target_os = "linux"))]
52+
pub(crate) use syscall::syscall;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use super::weak;
2+
3+
pub(crate) macro syscall {
4+
(
5+
fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;
6+
) => (
7+
unsafe fn $name($($param: $t),*) -> $ret {
8+
weak!(fn $name($($param: $t),*) -> $ret;);
9+
10+
// Use a weak symbol from libc when possible, allowing `LD_PRELOAD`
11+
// interposition, but if it's not found just use a raw syscall.
12+
if let Some(fun) = $name.get() {
13+
unsafe { fun($($param),*) }
14+
} else {
15+
unsafe { libc::syscall(libc::${concat(SYS_, $name)}, $($param),*) as $ret }
16+
}
17+
}
18+
)
19+
}

library/std/src/sys/pal/unix/weak/tests.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1+
// This file is included by both implementations of `weak!`.
12
use super::*;
23

34
#[test]
4-
fn dlsym_existing() {
5+
fn weak_existing() {
56
const TEST_STRING: &'static CStr = c"Ferris!";
67

78
// Try to find a symbol that definitely exists.
8-
dlsym! {
9+
weak! {
910
fn strlen(cs: *const c_char) -> usize;
1011
}
1112

12-
dlsym! {
13+
weak! {
1314
#[link_name = "strlen"]
1415
fn custom_name(cs: *const c_char) -> usize;
1516
}
@@ -22,9 +23,9 @@ fn dlsym_existing() {
2223
}
2324

2425
#[test]
25-
fn dlsym_missing() {
26+
fn weak_missing() {
2627
// Try to find a symbol that definitely does not exist.
27-
dlsym! {
28+
weak! {
2829
fn test_symbol_that_does_not_exist() -> i32;
2930
}
3031

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#[cfg(test)]
2+
#[path = "./tests.rs"]
3+
mod tests;
4+
5+
pub(crate) macro weak {
6+
(fn $name:ident($($param:ident : $t:ty),* $(,)?) -> $ret:ty;) => (
7+
let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
8+
unsafe extern "C" {
9+
#[linkage = "extern_weak"]
10+
static $name: Option<unsafe extern "C" fn($($t),*) -> $ret>;
11+
}
12+
#[allow(unused_unsafe)]
13+
ExternWeak::new(unsafe { $name })
14+
};
15+
)
16+
}
17+
18+
pub(crate) struct ExternWeak<F: Copy> {
19+
weak_ptr: Option<F>,
20+
}
21+
22+
impl<F: Copy> ExternWeak<F> {
23+
#[inline]
24+
pub fn new(weak_ptr: Option<F>) -> Self {
25+
ExternWeak { weak_ptr }
26+
}
27+
28+
#[inline]
29+
pub fn get(&self) -> Option<F> {
30+
self.weak_ptr
31+
}
32+
}

0 commit comments

Comments
 (0)