]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #90846 - cuviper:weak, r=dtolnay
authorbors <bors@rust-lang.org>
Sat, 27 Nov 2021 07:58:00 +0000 (07:58 +0000)
committerbors <bors@rust-lang.org>
Sat, 27 Nov 2021 07:58:00 +0000 (07:58 +0000)
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.

library/std/src/sys/unix/android.rs
library/std/src/sys/unix/fd.rs
library/std/src/sys/unix/fs.rs
library/std/src/sys/unix/kernel_copy.rs
library/std/src/sys/unix/net.rs
library/std/src/sys/unix/os.rs
library/std/src/sys/unix/os/tests.rs
library/std/src/sys/unix/process/process_unix.rs
library/std/src/sys/unix/thread.rs
library/std/src/sys/unix/weak.rs

index 6a46525f682c4a69860c8f1232112519e291e708..73ff10ab8a221ccad212d9f231caa13fbc5b333d 100644 (file)
 
 #![cfg(target_os = "android")]
 
-use libc::{c_int, c_void, sighandler_t, size_t, ssize_t};
-use libc::{ftruncate, pread, pwrite};
+use libc::{c_int, sighandler_t};
 
-use super::{cvt, cvt_r, weak::weak};
-use crate::io;
+use super::weak::weak;
 
 // The `log2` and `log2f` functions apparently appeared in android-18, or at
 // 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 {
     let f = f.expect("neither `signal` nor `bsd_signal` symbols found");
     f(signum, handler)
 }
-
-// The `ftruncate64` symbol apparently appeared in android-12, so we do some
-// dynamic detection to see if we can figure out whether `ftruncate64` exists.
-//
-// If it doesn't we just fall back to `ftruncate`, generating an error for
-// too-large values.
-#[cfg(target_pointer_width = "32")]
-pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
-    weak!(fn ftruncate64(c_int, i64) -> c_int);
-
-    unsafe {
-        match ftruncate64.get() {
-            Some(f) => cvt_r(|| f(fd, size as i64)).map(drop),
-            None => {
-                if size > i32::MAX as u64 {
-                    Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"cannot truncate >2GB"))
-                } else {
-                    cvt_r(|| ftruncate(fd, size as i32)).map(drop)
-                }
-            }
-        }
-    }
-}
-
-#[cfg(target_pointer_width = "64")]
-pub fn ftruncate64(fd: c_int, size: u64) -> io::Result<()> {
-    unsafe { cvt_r(|| ftruncate(fd, size as i64)).map(drop) }
-}
-
-#[cfg(target_pointer_width = "32")]
-pub unsafe fn cvt_pread64(
-    fd: c_int,
-    buf: *mut c_void,
-    count: size_t,
-    offset: i64,
-) -> io::Result<ssize_t> {
-    use crate::convert::TryInto;
-    weak!(fn pread64(c_int, *mut c_void, size_t, i64) -> ssize_t);
-    pread64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| {
-        if let Ok(o) = offset.try_into() {
-            cvt(pread(fd, buf, count, o))
-        } else {
-            Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"cannot pread >2GB"))
-        }
-    })
-}
-
-#[cfg(target_pointer_width = "32")]
-pub unsafe fn cvt_pwrite64(
-    fd: c_int,
-    buf: *const c_void,
-    count: size_t,
-    offset: i64,
-) -> io::Result<ssize_t> {
-    use crate::convert::TryInto;
-    weak!(fn pwrite64(c_int, *const c_void, size_t, i64) -> ssize_t);
-    pwrite64.get().map(|f| cvt(f(fd, buf, count, offset))).unwrap_or_else(|| {
-        if let Ok(o) = offset.try_into() {
-            cvt(pwrite(fd, buf, count, o))
-        } else {
-            Err(io::Error::new_const(io::ErrorKind::InvalidInput, &"cannot pwrite >2GB"))
-        }
-    })
-}
-
-#[cfg(target_pointer_width = "64")]
-pub unsafe fn cvt_pread64(
-    fd: c_int,
-    buf: *mut c_void,
-    count: size_t,
-    offset: i64,
-) -> io::Result<ssize_t> {
-    cvt(pread(fd, buf, count, offset))
-}
-
-#[cfg(target_pointer_width = "64")]
-pub unsafe fn cvt_pwrite64(
-    fd: c_int,
-    buf: *const c_void,
-    count: size_t,
-    offset: i64,
-) -> io::Result<ssize_t> {
-    cvt(pwrite(fd, buf, count, offset))
-}
index 0956726084e02a21f7783841e7ec17bea3faa973..ea688571a9891ff907fc96b2351ae217d936a660 100644 (file)
@@ -99,30 +99,18 @@ pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
     }
 
     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
-        #[cfg(target_os = "android")]
-        use super::android::cvt_pread64;
-
-        #[cfg(not(target_os = "android"))]
-        unsafe fn cvt_pread64(
-            fd: c_int,
-            buf: *mut c_void,
-            count: usize,
-            offset: i64,
-        ) -> io::Result<isize> {
-            #[cfg(not(target_os = "linux"))]
-            use libc::pread as pread64;
-            #[cfg(target_os = "linux")]
-            use libc::pread64;
-            cvt(pread64(fd, buf, count, offset))
-        }
+        #[cfg(not(any(target_os = "linux", target_os = "android")))]
+        use libc::pread as pread64;
+        #[cfg(any(target_os = "linux", target_os = "android"))]
+        use libc::pread64;
 
         unsafe {
-            cvt_pread64(
+            cvt(pread64(
                 self.as_raw_fd(),
                 buf.as_mut_ptr() as *mut c_void,
                 cmp::min(buf.len(), READ_LIMIT),
                 offset as i64,
-            )
+            ))
             .map(|n| n as usize)
         }
     }
@@ -161,30 +149,18 @@ pub fn is_write_vectored(&self) -> bool {
     }
 
     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
-        #[cfg(target_os = "android")]
-        use super::android::cvt_pwrite64;
-
-        #[cfg(not(target_os = "android"))]
-        unsafe fn cvt_pwrite64(
-            fd: c_int,
-            buf: *const c_void,
-            count: usize,
-            offset: i64,
-        ) -> io::Result<isize> {
-            #[cfg(not(target_os = "linux"))]
-            use libc::pwrite as pwrite64;
-            #[cfg(target_os = "linux")]
-            use libc::pwrite64;
-            cvt(pwrite64(fd, buf, count, offset))
-        }
+        #[cfg(not(any(target_os = "linux", target_os = "android")))]
+        use libc::pwrite as pwrite64;
+        #[cfg(any(target_os = "linux", target_os = "android"))]
+        use libc::pwrite64;
 
         unsafe {
-            cvt_pwrite64(
+            cvt(pwrite64(
                 self.as_raw_fd(),
                 buf.as_ptr() as *const c_void,
                 cmp::min(buf.len(), READ_LIMIT),
                 offset as i64,
-            )
+            ))
             .map(|n| n as usize)
         }
     }
index a4fff9b2e64731c7f775fcff82075bbf3b2c3784..d77e5cae3ad7366964f9e51738580cf8e8879c89 100644 (file)
@@ -46,8 +46,8 @@
 use libc::readdir_r as readdir64_r;
 #[cfg(target_os = "android")]
 use libc::{
-    dirent as dirent64, fstat as fstat64, fstatat as fstatat64, lseek64, lstat as lstat64,
-    open as open64, stat as stat64,
+    dirent as dirent64, fstat as fstat64, fstatat as fstatat64, ftruncate64, lseek64,
+    lstat as lstat64, off64_t, open as open64, stat as stat64,
 };
 #[cfg(not(any(
     target_os = "linux",
@@ -835,16 +835,10 @@ unsafe fn os_datasync(fd: c_int) -> c_int {
     }
 
     pub fn truncate(&self, size: u64) -> io::Result<()> {
-        #[cfg(target_os = "android")]
-        return crate::sys::android::ftruncate64(self.as_raw_fd(), size);
-
-        #[cfg(not(target_os = "android"))]
-        {
-            use crate::convert::TryInto;
-            let size: off64_t =
-                size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
-            cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
-        }
+        use crate::convert::TryInto;
+        let size: off64_t =
+            size.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
+        cvt_r(|| unsafe { ftruncate64(self.as_raw_fd(), size) }).map(drop)
     }
 
     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
@@ -1154,7 +1148,7 @@ pub fn link(original: &Path, link: &Path) -> io::Result<()> {
         } else if #[cfg(target_os = "macos")] {
             // On MacOS, older versions (<=10.9) lack support for linkat while newer
             // versions have it. We want to use linkat if it is available, so we use weak!
-            // to check. `linkat` is preferable to `link` ecause it gives us a flag to
+            // to check. `linkat` is preferable to `link` because it gives us a flag to
             // specify how symlinks should be handled. We pass 0 as the flags argument,
             // meaning it shouldn't follow symlinks.
             weak!(fn linkat(c_int, *const c_char, c_int, *const c_char, c_int) -> c_int);
index f3155fbc0620f1ad8dbd634fb24d3be328d4cbb1..241cf89d3145c6cfcd20dac0b125b31d14cd64b9 100644 (file)
@@ -614,6 +614,9 @@ fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) ->
     static HAS_SENDFILE: AtomicBool = AtomicBool::new(true);
     static HAS_SPLICE: AtomicBool = AtomicBool::new(true);
 
+    // Android builds use feature level 14, but the libc wrapper for splice is
+    // gated on feature level 21+, so we have to invoke the syscall directly.
+    #[cfg(target_os = "android")]
     syscall! {
         fn splice(
             srcfd: libc::c_int,
@@ -625,6 +628,9 @@ fn splice(
         ) -> libc::ssize_t
     }
 
+    #[cfg(target_os = "linux")]
+    use libc::splice;
+
     match mode {
         SpliceMode::Sendfile if !HAS_SENDFILE.load(Ordering::Relaxed) => {
             return CopyResult::Fallback(0);
index 9ae6d12dcb95cfc9a30256f3185db31f74ee7004..a82a0172126d4fa51066b4b5313712cb61ffe812 100644 (file)
@@ -501,7 +501,7 @@ unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
 // res_init unconditionally, we call it only when we detect we're linking
 // against glibc version < 2.26. (That is, when we both know its needed and
 // believe it's thread-safe).
-#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
 fn on_resolver_failure() {
     use crate::sys;
 
@@ -513,5 +513,5 @@ fn on_resolver_failure() {
     }
 }
 
-#[cfg(any(not(target_env = "gnu"), target_os = "vxworks"))]
+#[cfg(not(all(target_os = "linux", target_env = "gnu")))]
 fn on_resolver_failure() {}
index 87893d269120bb0eb09adcbb253b9c5aa441c4c6..7686b61b67a892de9ae9bfa7d25ada0448c0efde 100644 (file)
@@ -2,7 +2,7 @@
 
 #![allow(unused_imports)] // lots of cfg code here
 
-#[cfg(all(test, target_env = "gnu"))]
+#[cfg(test)]
 mod tests;
 
 use crate::os::unix::prelude::*;
@@ -636,22 +636,14 @@ pub fn getppid() -> u32 {
     unsafe { libc::getppid() as u32 }
 }
 
-#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
 pub fn glibc_version() -> Option<(usize, usize)> {
-    if let Some(Ok(version_str)) = glibc_version_cstr().map(CStr::to_str) {
-        parse_glibc_version(version_str)
-    } else {
-        None
-    }
-}
-
-#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
-fn glibc_version_cstr() -> Option<&'static CStr> {
-    weak! {
-        fn gnu_get_libc_version() -> *const libc::c_char
+    extern "C" {
+        fn gnu_get_libc_version() -> *const libc::c_char;
     }
-    if let Some(f) = gnu_get_libc_version.get() {
-        unsafe { Some(CStr::from_ptr(f())) }
+    let version_cstr = unsafe { CStr::from_ptr(gnu_get_libc_version()) };
+    if let Ok(version_str) = version_cstr.to_str() {
+        parse_glibc_version(version_str)
     } else {
         None
     }
@@ -659,7 +651,7 @@ fn gnu_get_libc_version() -> *const libc::c_char
 
 // Returns Some((major, minor)) if the string is a valid "x.y" version,
 // ignoring any extra dot-separated parts. Otherwise return None.
-#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
 fn parse_glibc_version(version: &str) -> Option<(usize, usize)> {
     let mut parsed_ints = version.split('.').map(str::parse::<usize>).fuse();
     match (parsed_ints.next(), parsed_ints.next()) {
index c445acf2722c0dc2fc5d1ea63bc31dbf789f4084..efc29955b05fea7eac7ebe2c41a4ef6c796bc120 100644 (file)
@@ -1,14 +1,12 @@
-use super::*;
-
 #[test]
-#[cfg(not(target_os = "vxworks"))]
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
 fn test_glibc_version() {
     // This mostly just tests that the weak linkage doesn't panic wildly...
-    glibc_version();
+    super::glibc_version();
 }
 
 #[test]
-#[cfg(not(target_os = "vxworks"))]
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
 fn test_parse_glibc_version() {
     let cases = [
         ("0.0", Some((0, 0))),
@@ -20,6 +18,6 @@ fn test_parse_glibc_version() {
         ("foo.1", None),
     ];
     for &(version_str, parsed) in cases.iter() {
-        assert_eq!(parsed, parse_glibc_version(version_str));
+        assert_eq!(parsed, super::parse_glibc_version(version_str));
     }
 }
index 3bf1493f3b8cbd6860cadddd0f7f44789b47d95d..bce35b380e677075da4f2f2a43024787b5d6b378 100644 (file)
@@ -13,7 +13,7 @@
 use crate::os::linux::process::PidFd;
 
 #[cfg(target_os = "linux")]
-use crate::sys::weak::syscall;
+use crate::sys::weak::raw_syscall;
 
 #[cfg(any(
     target_os = "macos",
@@ -162,7 +162,7 @@ struct clone_args {
             cgroup: u64,
         }
 
-        syscall! {
+        raw_syscall! {
             fn clone3(cl_args: *mut clone_args, len: libc::size_t) -> libc::c_long
         }
 
index b99eb2e553f08908effdf7385961767f75b30c88..9e02966b57c0d1066486eca3c1fadd507055a364 100644 (file)
@@ -7,7 +7,9 @@
 use crate::sys::{os, stack_overflow};
 use crate::time::Duration;
 
-#[cfg(any(target_os = "linux", target_os = "solaris", target_os = "illumos"))]
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
+use crate::sys::weak::dlsym;
+#[cfg(any(target_os = "solaris", target_os = "illumos"))]
 use crate::sys::weak::weak;
 #[cfg(not(any(target_os = "l4re", target_os = "vxworks", target_os = "espidf")))]
 pub const DEFAULT_MIN_STACK_SIZE: usize = 2 * 1024 * 1024;
@@ -627,10 +629,12 @@ pub unsafe fn current() -> Option<Guard> {
 // We need that information to avoid blowing up when a small stack
 // is created in an application with big thread-local storage requirements.
 // See #6233 for rationale and details.
-#[cfg(target_os = "linux")]
-#[allow(deprecated)]
+#[cfg(all(target_os = "linux", target_env = "gnu"))]
 fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize {
-    weak!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t);
+    // We use dlsym to avoid an ELF version dependency on GLIBC_PRIVATE. (#23628)
+    // We shouldn't really be using such an internal symbol, but there's currently
+    // no other way to account for the TLS size.
+    dlsym!(fn __pthread_get_minstack(*const libc::pthread_attr_t) -> libc::size_t);
 
     match __pthread_get_minstack.get() {
         None => libc::PTHREAD_STACK_MIN,
@@ -638,9 +642,8 @@ fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize {
     }
 }
 
-// No point in looking up __pthread_get_minstack() on non-glibc
-// platforms.
-#[cfg(all(not(target_os = "linux"), not(target_os = "netbsd")))]
+// No point in looking up __pthread_get_minstack() on non-glibc platforms.
+#[cfg(all(not(all(target_os = "linux", target_env = "gnu")), not(target_os = "netbsd")))]
 fn min_stack_size(_: *const libc::pthread_attr_t) -> usize {
     libc::PTHREAD_STACK_MIN
 }
index ba432ec54941ce3140c3a2b58e50418ea6e8280a..32072affe8af45f20c8862e23f73fe7fa059b5a2 100644 (file)
@@ -6,7 +6,7 @@
 //! detection.
 //!
 //! One option to use here is weak linkage, but that is unfortunately only
-//! really workable on Linux. Hence, use dlsym to get the symbol value at
+//! really workable with ELF. Otherwise, use dlsym to get the symbol value at
 //! runtime. This is also done for compatibility with older versions of glibc,
 //! and to avoid creating dependencies on GLIBC_PRIVATE symbols. It assumes that
 //! we've been dynamically linked to the library the symbol comes from, but that
@@ -14,7 +14,8 @@
 //!
 //! A long time ago this used weak linkage for the __pthread_get_minstack
 //! symbol, but that caused Debian to detect an unnecessarily strict versioned
-//! dependency on libc6 (#23628).
+//! dependency on libc6 (#23628) because it is GLIBC_PRIVATE. We now use `dlsym`
+//! for a runtime lookup of that symbol to avoid the ELF versioned dependency.
 
 // There are a variety of `#[cfg]`s controlling which targets are involved in
 // each instance of `weak!` and `syscall!`. Rather than trying to unify all of
 #![allow(dead_code, unused_macros)]
 
 use crate::ffi::CStr;
-use crate::marker;
+use crate::marker::PhantomData;
 use crate::mem;
 use crate::sync::atomic::{self, AtomicUsize, Ordering};
 
+// We can use true weak linkage on ELF targets.
+#[cfg(not(any(target_os = "macos", target_os = "ios")))]
 pub(crate) macro weak {
     (fn $name:ident($($t:ty),*) -> $ret:ty) => (
-        #[allow(non_upper_case_globals)]
-        static $name: crate::sys::weak::Weak<unsafe extern "C" fn($($t),*) -> $ret> =
-            crate::sys::weak::Weak::new(concat!(stringify!($name), '\0'));
+        let ref $name: ExternWeak<unsafe extern "C" fn($($t),*) -> $ret> = {
+            extern "C" {
+                #[linkage = "extern_weak"]
+                static $name: *const libc::c_void;
+            }
+            #[allow(unused_unsafe)]
+            ExternWeak::new(unsafe { $name })
+        };
     )
 }
 
-pub struct Weak<F> {
+// On non-ELF targets, use the dlsym approximation of weak linkage.
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+pub(crate) use self::dlsym as weak;
+
+pub(crate) struct ExternWeak<F> {
+    weak_ptr: *const libc::c_void,
+    _marker: PhantomData<F>,
+}
+
+impl<F> ExternWeak<F> {
+    #[inline]
+    pub(crate) fn new(weak_ptr: *const libc::c_void) -> Self {
+        ExternWeak { weak_ptr, _marker: PhantomData }
+    }
+}
+
+impl<F> ExternWeak<F> {
+    #[inline]
+    pub(crate) fn get(&self) -> Option<F> {
+        unsafe {
+            if self.weak_ptr.is_null() {
+                None
+            } else {
+                Some(mem::transmute_copy::<*const libc::c_void, F>(&self.weak_ptr))
+            }
+        }
+    }
+}
+
+pub(crate) macro dlsym {
+    (fn $name:ident($($t:ty),*) -> $ret:ty) => (
+        static DLSYM: DlsymWeak<unsafe extern "C" fn($($t),*) -> $ret> =
+            DlsymWeak::new(concat!(stringify!($name), '\0'));
+        let $name = &DLSYM;
+    )
+}
+
+pub(crate) struct DlsymWeak<F> {
     name: &'static str,
     addr: AtomicUsize,
-    _marker: marker::PhantomData<F>,
+    _marker: PhantomData<F>,
 }
 
-impl<F> Weak<F> {
-    pub const fn new(name: &'static str) -> Weak<F> {
-        Weak { name, addr: AtomicUsize::new(1), _marker: marker::PhantomData }
+impl<F> DlsymWeak<F> {
+    pub(crate) const fn new(name: &'static str) -> Self {
+        DlsymWeak { name, addr: AtomicUsize::new(1), _marker: PhantomData }
     }
 
-    pub fn get(&self) -> Option<F> {
-        assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
+    #[inline]
+    pub(crate) fn get(&self) -> Option<F> {
         unsafe {
             // Relaxed is fine here because we fence before reading through the
             // pointer (see the comment below).
@@ -82,6 +127,8 @@ pub fn get(&self) -> Option<F> {
     // Cold because it should only happen during first-time initalization.
     #[cold]
     unsafe fn initialize(&self) -> Option<F> {
+        assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
+
         let val = fetch(self.name);
         // This synchronizes with the acquire fence in `get`.
         self.addr.store(val, Ordering::Release);
@@ -105,14 +152,12 @@ unsafe fn fetch(name: &str) -> usize {
 pub(crate) macro syscall {
     (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
         unsafe fn $name($($arg_name: $t),*) -> $ret {
-            use super::os;
-
             weak! { fn $name($($t),*) -> $ret }
 
             if let Some(fun) = $name.get() {
                 fun($($arg_name),*)
             } else {
-                os::set_errno(libc::ENOSYS);
+                super::os::set_errno(libc::ENOSYS);
                 -1
             }
         }
@@ -123,11 +168,6 @@ unsafe fn $name($($arg_name: $t),*) -> $ret {
 pub(crate) macro syscall {
     (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
         unsafe fn $name($($arg_name:$t),*) -> $ret {
-            use weak;
-            // This looks like a hack, but concat_idents only accepts idents
-            // (not paths).
-            use libc::*;
-
             weak! { fn $name($($t),*) -> $ret }
 
             // Use a weak symbol from libc when possible, allowing `LD_PRELOAD`
@@ -135,6 +175,10 @@ unsafe fn $name($($arg_name:$t),*) -> $ret {
             if let Some(fun) = $name.get() {
                 fun($($arg_name),*)
             } else {
+                // This looks like a hack, but concat_idents only accepts idents
+                // (not paths).
+                use libc::*;
+
                 syscall(
                     concat_idents!(SYS_, $name),
                     $($arg_name),*
@@ -143,3 +187,19 @@ unsafe fn $name($($arg_name:$t),*) -> $ret {
         }
     )
 }
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub(crate) macro raw_syscall {
+    (fn $name:ident($($arg_name:ident: $t:ty),*) -> $ret:ty) => (
+        unsafe fn $name($($arg_name:$t),*) -> $ret {
+            // This looks like a hack, but concat_idents only accepts idents
+            // (not paths).
+            use libc::*;
+
+            syscall(
+                concat_idents!(SYS_, $name),
+                $($arg_name),*
+            ) as $ret
+        }
+    )
+}