Skip to content

Commit 5ff6ac4

Browse files
committedNov 12, 2021
Refactor weak symbols in std::sys::unix
This makes a few changes to the weak symbol macros in `sys::unix`: - `dlsym!` is added to keep the functionality for runtime `dlsym` lookups, like for `__pthread_get_minstack@GLIBC_PRIVATE` that we don't want to show up in ELF symbol tables. - `weak!` now uses `#[linkage = "extern_weak"]` symbols, so its runtime behavior is just a simple null check. This is also used by `syscall!`. - On non-ELF targets (macos/ios) where that linkage is not known to behave, `weak!` is just an alias to `dlsym!` for the old behavior. - `raw_syscall!` is added to always call `libc::syscall` on linux and android, for cases like `clone3` that have no known libc wrapper. The new `weak!` linkage does mean that you'll get versioned symbols if you build with a newer glibc, like `WEAK DEFAULT UND statx@GLIBC_2.28`. This might seem problematic, but old non-weak symbols can tie the build to new versions too, like `dlsym@GLIBC_2.34` from their recent library unification. If you build with an old glibc like `dist-x86_64-linux` does, you'll still get unversioned `WEAK DEFAULT UND statx`, which may be resolved based on the runtime glibc. I also found a few functions that don't need to be weak anymore: - Android can directly use `ftruncate64`, `pread64`, and `pwrite64`, as these were added in API 12, and our baseline is API 14. - Linux can directly use `splice`, added way back in glibc 2.5 and similarly old musl. Android only added it in API 21 though.
1 parent e90c5fb commit 5ff6ac4

File tree

10 files changed

+134
-191
lines changed

10 files changed

+134
-191
lines changed
 

Diff for: ‎library/std/src/sys/unix/android.rs

+2-88
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,9 @@
1818
1919
#![cfg(target_os = "android")]
2020

21-
use libc::{c_int, c_void, sighandler_t, size_t, ssize_t};
22-
use libc::{ftruncate, pread, pwrite};
21+
use libc::{c_int, sighandler_t};
2322

24-
use super::{cvt, cvt_r, weak::weak};
25-
use crate::io;
23+
use super::weak::weak;
2624

2725
// The `log2` and `log2f` functions apparently appeared in android-18, or at
2826
// least you can see they're not present in the android-17 header [1] and they
@@ -81,87 +79,3 @@ pub unsafe fn signal(signum: c_int, handler: sighandler_t) -> sighandler_t {
8179
let f = f.expect("neither `signal` nor `bsd_signal` symbols found");
8280
f(signum, handler)
8381
}
84-
85-
// The `ftruncate64` symbol apparently appeared in android-12, so we do some
86-
// dynamic detection to see if we can figure out whether `ftruncate64` exists.
87-
//
88-
// If it doesn't we just fall back to `ftruncate`, generating an error for
89-
// too-large values.
90-
#[cfg(target_pointer_width = "32")]
91-
pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
92-
weak!(fn ftruncate64(c_int, i64) -> c_int);
93-
94-
unsafe {
95-
match ftruncate64.get() {
96-
Some(f) => cvt_r(|| f(fd, size as i64)).map(drop),
97-
None => {
98-
if size > i32::MAX as u64 {
99-
Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"cannot truncate >2GB"))
100-
} else {
101-
cvt_r(|| ftruncate(fd, size as i32)).map(drop)
102-
}
103-
}
104-
}
105-
}
106-
}
107-
108-
#[cfg(target_pointer_width = "64")]
109-
pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
110-
unsafe { cvt_r(|| ftruncate(fd, size as i64)).map(drop) }
111-
}
112-
113-
#[cfg(target_pointer_width = "32")]
114-
pub unsafe fn cvt_pread64(
115-
fd: c_int,
116-
buf: *mut c_void,
117-
count: size_t,
118-
offset: i64,
119-
) -> io::Result<ssize_t> {
120-
use crate::convert::TryInto;
121-
weak!(fn pread64(c_int, *mut c_void, size_t, i64) -> ssize_t);
122-
pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| {
123-
if let Ok(o) = offset.try_into() {
124-
cvt(pread(fd, buf, count, o))
125-
} else {
126-
Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"cannot pread >2GB"))
127-
}
128-
})
129-
}
130-
131-
#[cfg(target_pointer_width = "32")]
132-
pub unsafe fn cvt_pwrite64(
133-
fd: c_int,
134-
buf: *const c_void,
135-
count: size_t,
136-
offset: i64,
137-
) -> io::Result<ssize_t> {
138-
use crate::convert::TryInto;
139-
weak!(fn pwrite64(c_int, *const c_void, size_t, i64) -> ssize_t);
140-
pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| {
141-
if let Ok(o) = offset.try_into() {
142-
cvt(pwrite(fd, buf, count, o))
143-
} else {
144-
Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"cannot pwrite >2GB"))
145-
}
146-
})
147-
}
148-
149-
#[cfg(target_pointer_width = "64")]
150-
pub unsafe fn cvt_pread64(
151-
fd: c_int,
152-
buf: *mut c_void,
153-
count: size_t,
154-
offset: i64,
155-
) -> io::Result<ssize_t> {
156-
cvt(pread(fd, buf, count, offset))
157-
}
158-
159-
#[cfg(target_pointer_width = "64")]
160-
pub unsafe fn cvt_pwrite64(
161-
fd: c_int,
162-
buf: *const c_void,
163-
count: size_t,
164-
offset: i64,
165-
) -> io::Result<ssize_t> {
166-
cvt(pwrite(fd, buf, count, offset))
167-
}

Diff for: ‎library/std/src/sys/unix/fd.rs

+12-36
Original file line numberDiff line numberDiff line change
@@ -99,30 +99,18 @@ impl FileDesc {
9999
}
100100

101101
pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
102-
#[cfg(target_os = "android")]
103-
use super::android::cvt_pread64;
104-
105-
#[cfg(not(target_os = "android"))]
106-
unsafe fn cvt_pread64(
107-
fd: c_int,
108-
buf: *mut c_void,
109-
count: usize,
110-
offset: i64,
111-
) -> io::Result<isize> {
112-
#[cfg(not(target_os = "linux"))]
113-
use libc::pread as pread64;
114-
#[cfg(target_os = "linux")]
115-
use libc::pread64;
116-
cvt(pread64(fd, buf, count, offset))
117-
}
102+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
103+
use libc::pread as pread64;
104+
#[cfg(any(target_os = "linux", target_os = "android"))]
105+
use libc::pread64;
118106

119107
unsafe {
120-
cvt_pread64(
108+
cvt(pread64(
121109
self.as_raw_fd(),
122110
buf.as_mut_ptr() as *mut c_void,
123111
cmp::min(buf.len(), READ_LIMIT),
124112
offset as i64,
125-
)
113+
))
126114
.map(|n| n as usize)
127115
}
128116
}
@@ -161,30 +149,18 @@ impl FileDesc {
161149
}
162150

163151
pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
164-
#[cfg(target_os = "android")]
165-
use super::android::cvt_pwrite64;
166-
167-
#[cfg(not(target_os = "android"))]
168-
unsafe fn cvt_pwrite64(
169-
fd: c_int,
170-
buf: *const c_void,
171-
count: usize,
172-
offset: i64,
173-
) -> io::Result<isize> {
174-
#[cfg(not(target_os = "linux"))]
175-
use libc::pwrite as pwrite64;
176-
#[cfg(target_os = "linux")]
177-
use libc::pwrite64;
178-
cvt(pwrite64(fd, buf, count, offset))
179-
}
152+
#[cfg(not(any(target_os = "linux", target_os = "android")))]
153+
use libc::pwrite as pwrite64;
154+
#[cfg(any(target_os = "linux", target_os = "android"))]
155+
use libc::pwrite64;
180156

181157
unsafe {
182-
cvt_pwrite64(
158+
cvt(pwrite64(
183159
self.as_raw_fd(),
184160
buf.as_ptr() as *const c_void,
185161
cmp::min(buf.len(), READ_LIMIT),
186162
offset as i64,
187-
)
163+
))
188164
.map(|n| n as usize)
189165
}
190166
}

Diff for: ‎library/std/src/sys/unix/fs.rs

+7-13
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ use libc::fstatat64;
4646
use libc::readdir_r as readdir64_r;
4747
#[cfg(target_os = "android")]
4848
use libc::{
49-
dirent as dirent64, fstat as fstat64, fstatat as fstatat64, lseek64, lstat as lstat64,
50-
open as open64, stat as stat64,
49+
dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64,
50+
lstat as lstat64, off64_t, open as open64, stat as stat64,
5151
};
5252
#[cfg(not(any(
5353
target_os = "linux",
@@ -835,16 +835,10 @@ impl File {
835835
}
836836

837837
pub fn truncate(&self, size: u64) -> io::Result<()> {
838-
#[cfg(target_os = "android")]
839-
return crate::sys::android::ftruncate64(self.as_raw_fd(), size);
840-
841-
#[cfg(not(target_os = "android"))]
842-
{
843-
use crate::convert::TryInto;
844-
let size: off64_t =
845-
size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
846-
cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
847-
}
838+
use crate::convert::TryInto;
839+
let size: off64_t =
840+
size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
841+
cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
848842
}
849843

850844
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
@@ -1154,7 +1148,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> {
11541148
} else if #[cfg(target_os = "macos")] {
11551149
// On MacOS, older versions (<=10.9) lack support for linkat while newer
11561150
// versions have it. We want to use linkat if it is available, so we use weak!
1157-
// to check. `linkat` is preferable to `link` ecause it gives us a flag to
1151+
// to check. `linkat` is preferable to `link` because it gives us a flag to
11581152
// specify how symlinks should be handled. We pass 0 as the flags argument,
11591153
// meaning it shouldn't follow symlinks.
11601154
weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);

Diff for: ‎library/std/src/sys/unix/kernel_copy.rs

+6
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,9 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) ->
612612
static HAS_SENDFILE: AtomicBool = AtomicBool::new(true);
613613
static HAS_SPLICE: AtomicBool = AtomicBool::new(true);
614614

615+
// Android builds use feature level 14, but the libc wrapper for splice is
616+
// gated on feature level 21+, so we have to invoke the syscall directly.
617+
#[cfg(target_os = "android")]
615618
syscall! {
616619
fn splice(
617620
srcfd: libc::c_int,
@@ -623,6 +626,9 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) ->
623626
) -> libc::ssize_t
624627
}
625628

629+
#[cfg(target_os = "linux")]
630+
use libc::splice;
631+
626632
match mode {
627633
SpliceMode::Sendfile if !HAS_SENDFILE.load(Ordering::Relaxed) => {
628634
return CopyResult::Fallback(0);

Diff for: ‎library/std/src/sys/unix/net.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ impl FromRawFd for Socket {
501501
// res_init unconditionally, we call it only when we detect we're linking
502502
// against glibc version < 2.26. (That is, when we both know its needed and
503503
// believe it's thread-safe).
504-
#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
504+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
505505
fn on_resolver_failure() {
506506
use crate::sys;
507507

@@ -513,5 +513,5 @@ fn on_resolver_failure() {
513513
}
514514
}
515515

516-
#[cfg(any(not(target_env = "gnu"), target_os = "vxworks"))]
516+
#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
517517
fn on_resolver_failure() {}

Diff for: ‎library/std/src/sys/unix/os.rs

+8-16
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
#![allow(unused_imports)] // lots of cfg code here
44

5-
#[cfg(all(test, target_env = "gnu"))]
5+
#[cfg(test)]
66
mod tests;
77

88
use crate::os::unix::prelude::*;
@@ -636,30 +636,22 @@ pub fn getppid() -> u32 {
636636
unsafe { libc::getppid() as u32 }
637637
}
638638

639-
#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
639+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
640640
pub fn glibc_version() -> Option<(usize, usize)> {
641-
if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
642-
parse_glibc_version(version_str)
643-
} else {
644-
None
645-
}
646-
}
647-
648-
#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
649-
fn glibc_version_cstr() -> Option<&'static CStr> {
650-
weak! {
651-
fn gnu_get_libc_version() -> *const libc::c_char
641+
extern "C" {
642+
fn gnu_get_libc_version() -> *const libc::c_char;
652643
}
653-
if let Some(f) = gnu_get_libc_version.get() {
654-
unsafe { Some(CStr::from_ptr(f())) }
644+
let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) };
645+
if let Ok(version_str) = version_cstr.to_str() {
646+
parse_glibc_version(version_str)
655647
} else {
656648
None
657649
}
658650
}
659651

660652
// Returns Some((major, minor)) if the string is a valid "x.y" version,
661653
// ignoring any extra dot-separated parts. Otherwise return None.
662-
#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
654+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
663655
fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
664656
let mut parsed_ints = version.split('.').map(str::parse::<usize>).fuse();
665657
match (parsed_ints.next(), parsed_ints.next()) {

Diff for: ‎library/std/src/sys/unix/os/tests.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
use super::*;
2-
31
#[test]
4-
#[cfg(not(target_os = "vxworks"))]
2+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
53
fn test_glibc_version() {
64
// This mostly just tests that the weak linkage doesn't panic wildly...
7-
glibc_version();
5+
super::glibc_version();
86
}
97

108
#[test]
11-
#[cfg(not(target_os = "vxworks"))]
9+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
1210
fn test_parse_glibc_version() {
1311
let cases = [
1412
("0.0", Some((0, 0))),
@@ -20,6 +18,6 @@ fn test_parse_glibc_version() {
2018
("foo.1", None),
2119
];
2220
for &(version_str, parsed) in cases.iter() {
23-
assert_eq!(parsed, parse_glibc_version(version_str));
21+
assert_eq!(parsed, super::parse_glibc_version(version_str));
2422
}
2523
}

Diff for: ‎library/std/src/sys/unix/process/process_unix.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::sys::process::process_common::*;
1313
use crate::os::linux::process::PidFd;
1414

1515
#[cfg(target_os = "linux")]
16-
use crate::sys::weak::syscall;
16+
use crate::sys::weak::raw_syscall;
1717

1818
#[cfg(any(
1919
target_os = "macos",
@@ -162,7 +162,7 @@ impl Command {
162162
cgroup: u64,
163163
}
164164

165-
syscall! {
165+
raw_syscall! {
166166
fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long
167167
}
168168

Diff for: ‎library/std/src/sys/unix/thread.rs

+10-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use crate::ptr;
77
use crate::sys::{os, stack_overflow};
88
use crate::time::Duration;
99

10-
#[cfg(any(target_os = "linux", target_os = "solaris", target_os = "illumos"))]
10+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
11+
use crate::sys::weak::dlsym;
12+
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
1113
use crate::sys::weak::weak;
1214
#[cfg(not(any(target_os = "l4re", target_os = "vxworks", target_os = "espidf")))]
1315
pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
@@ -627,20 +629,21 @@ pub mod guard {
627629
// We need that information to avoid blowing up when a small stack
628630
// is created in an application with big thread-local storage requirements.
629631
// See #6233 for rationale and details.
630-
#[cfg(target_os = "linux")]
631-
#[allow(deprecated)]
632+
#[cfg(all(target_os = "linux", target_env = "gnu"))]
632633
fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize {
633-
weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t);
634+
// We use dlsym to avoid an ELF version dependency on GLIBC_PRIVATE. (#23628)
635+
// We shouldn't really be using such an internal symbol, but there's currently
636+
// no other way to account for the TLS size.
637+
dlsym!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t);
634638

635639
match __pthread_get_minstack.get() {
636640
None => libc::PTHREAD_STACK_MIN,
637641
Some(f) => unsafe { f(attr) },
638642
}
639643
}
640644

641-
// No point in looking up __pthread_get_minstack() on non-glibc
642-
// platforms.
643-
#[cfg(all(not(target_os = "linux"), not(target_os = "netbsd")))]
645+
// No point in looking up __pthread_get_minstack() on non-glibc platforms.
646+
#[cfg(all(not(all(target_os = "linux", target_env = "gnu")), not(target_os = "netbsd")))]
644647
fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
645648
libc::PTHREAD_STACK_MIN
646649
}

0 commit comments

Comments
 (0)
Please sign in to comment.