Skip to content

Commit

Permalink
Use weak! for setns, preadv64v2, and pwritev64v2 on GLIBC. (#556
Browse files Browse the repository at this point in the history
)

`setns`, `preadv64v2`, and `pwritev64v2` were introduced more recently
than the earliest glibc we support, so use the `weak!` mechanism for
them, with fallbacks using `syscall`.
  • Loading branch information
sunfishcode committed Mar 4, 2023
1 parent 8a7cbfa commit e64fc41
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 15 deletions.
123 changes: 109 additions & 14 deletions src/backend/libc/offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,21 @@ pub(super) use c::posix_fadvise64 as libc_posix_fadvise;
pub(super) use c::{pread as libc_pread, pwrite as libc_pwrite};
#[cfg(any(target_os = "android", target_os = "linux", target_os = "emscripten"))]
pub(super) use c::{pread64 as libc_pread, pwrite64 as libc_pwrite};
#[cfg(not(any(
windows,
target_os = "android",
target_os = "emscripten",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "redox",
target_os = "solaris",
)))]
pub(super) use c::{preadv as libc_preadv, pwritev as libc_pwritev};
#[cfg(any(target_os = "linux", target_os = "emscripten"))]
pub(super) use c::{preadv64 as libc_preadv, pwritev64 as libc_pwritev};

#[cfg(target_os = "android")]
mod readwrite_pv64 {
use super::c;
Expand Down Expand Up @@ -302,20 +315,9 @@ mod readwrite_pv64 {
}
}
}
#[cfg(not(any(
windows,
target_os = "android",
target_os = "emscripten",
target_os = "haiku",
target_os = "ios",
target_os = "linux",
target_os = "macos",
target_os = "redox",
target_os = "solaris",
)))]
pub(super) use c::{preadv as libc_preadv, pwritev as libc_pwritev};
#[cfg(target_os = "android")]
pub(super) use readwrite_pv64::{preadv64 as libc_preadv, pwritev64 as libc_pwritev};

// macOS added preadv and pwritev in version 11.0
#[cfg(any(target_os = "ios", target_os = "macos"))]
mod readwrite_pv {
Expand All @@ -337,11 +339,104 @@ mod readwrite_pv {
) -> c::ssize_t
}
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub(super) use c::{preadv64v2 as libc_preadv2, pwritev64v2 as libc_pwritev2};
#[cfg(any(target_os = "ios", target_os = "macos"))]
pub(super) use readwrite_pv::{preadv as libc_preadv, pwritev as libc_pwritev};

// GLIBC added `preadv64v2` and `pwritev64v2` in version 2.26.
#[cfg(all(target_os = "linux", target_env = "gnu"))]
mod readwrite_pv64v2 {
use super::c;

// 64-bit offsets on 32-bit platforms are passed in endianness-specific
// lo/hi pairs. See src/backend/linux_raw/conv.rs for details.
#[cfg(all(target_endian = "little", target_pointer_width = "32"))]
fn lo(x: u64) -> usize {
(x >> 32) as usize
}
#[cfg(all(target_endian = "little", target_pointer_width = "32"))]
fn hi(x: u64) -> usize {
(x & 0xffff_ffff) as usize
}
#[cfg(all(target_endian = "big", target_pointer_width = "32"))]
fn lo(x: u64) -> usize {
(x & 0xffff_ffff) as usize
}
#[cfg(all(target_endian = "big", target_pointer_width = "32"))]
fn hi(x: u64) -> usize {
(x >> 32) as usize
}

pub(in super::super) unsafe fn preadv64v2(
fd: c::c_int,
iov: *const c::iovec,
iovcnt: c::c_int,
offset: c::off64_t,
flags: c::c_int,
) -> c::ssize_t {
// Older GLIBC lacks `preadv64v2`, so use the `weak!` mechanism to
// test for it, and call back to `c::syscall`. We don't use
// `weak_or_syscall` here because we need to pass the 64-bit offset
// specially.
weak! {
fn preadv64v2(c::c_int, *const c::iovec, c::c_int, c::off64_t, c::c_int) -> c::ssize_t
}
if let Some(fun) = preadv64v2.get() {
fun(fd, iov, iovcnt, offset, flags)
} else {
#[cfg(target_pointer_width = "32")]
{
c::syscall(
c::SYS_preadv,
fd,
iov,
iovcnt,
hi(offset as u64),
lo(offset as u64),
flags,
) as c::ssize_t
}
#[cfg(target_pointer_width = "64")]
{
c::syscall(c::SYS_preadv2, fd, iov, iovcnt, offset, flags) as c::ssize_t
}
}
}
pub(in super::super) unsafe fn pwritev64v2(
fd: c::c_int,
iov: *const c::iovec,
iovcnt: c::c_int,
offset: c::off64_t,
flags: c::c_int,
) -> c::ssize_t {
// See the comments in `preadv64v2`.
weak! {
fn pwritev64v2(c::c_int, *const c::iovec, c::c_int, c::off64_t, c::c_int) -> c::ssize_t
}
if let Some(fun) = pwritev64v2.get() {
fun(fd, iov, iovcnt, offset, flags)
} else {
#[cfg(target_pointer_width = "32")]
{
c::syscall(
c::SYS_pwritev,
fd,
iov,
iovcnt,
hi(offset as u64),
lo(offset as u64),
flags,
) as c::ssize_t
}
#[cfg(target_pointer_width = "64")]
{
c::syscall(c::SYS_pwritev2, fd, iov, iovcnt, offset, flags) as c::ssize_t
}
}
}
}
#[cfg(all(target_os = "linux", target_env = "gnu"))]
pub(super) use readwrite_pv64v2::{preadv64v2 as libc_preadv2, pwritev64v2 as libc_pwritev2};

#[cfg(not(any(
windows,
target_os = "aix",
Expand Down
8 changes: 7 additions & 1 deletion src/backend/libc/thread/syscalls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,13 @@ pub(crate) fn gettid() -> Pid {
#[cfg(any(target_os = "android", target_os = "linux"))]
#[inline]
pub(crate) fn setns(fd: BorrowedFd, nstype: c::c_int) -> io::Result<c::c_int> {
unsafe { ret_c_int(c::setns(borrowed_fd(fd), nstype)) }
// `setns` wasn't supported in glibc until 2.14, and musl until 0.9.5,
// so use `syscall`.
weak_or_syscall! {
fn setns(fd: c::c_int, nstype: c::c_int) via SYS_setns -> c::c_int
}

unsafe { ret_c_int(setns(borrowed_fd(fd), nstype)) }
}

#[cfg(any(target_os = "android", target_os = "linux"))]
Expand Down

0 comments on commit e64fc41

Please sign in to comment.