diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT index ac8db62e0..4188c1518 100644 --- a/libc/SYSCALLS.TXT +++ b/libc/SYSCALLS.TXT @@ -94,6 +94,16 @@ ssize_t pread64(int, void*, size_t, off64_t) arm,mips,x86 ssize_t pread64|pread(int, void*, size_t, off_t) arm64,mips64,x86_64 ssize_t pwrite64(int, void*, size_t, off64_t) arm,mips,x86 ssize_t pwrite64|pwrite(int, void*, size_t, off_t) arm64,mips64,x86_64 + +# On LP32, preadv/pwritev don't use off64_t --- they use pairs of 32-bit +# arguments to avoid problems on architectures like ARM where 64-bit arguments +# must be in a register pair starting with an even-numbered register. +# See linux/fs/read_write.c and https://lwn.net/Articles/311630/. +ssize_t __preadv64:preadv(int, const struct iovec*, int, long, long) arm,mips,x86 +ssize_t preadv|preadv64(int, const struct iovec*, int, off_t) arm64,mips64,x86_64 +ssize_t __pwritev64:pwritev(int, const struct iovec*, int, long, long) arm,mips,x86 +ssize_t pwritev|pwritev64(int, const struct iovec*, int, off_t) arm64,mips64,x86_64 + int ___close:close(int) all pid_t __getpid:getpid() all int munmap(void*, size_t) all diff --git a/libc/arch-arm/syscalls/__preadv64.S b/libc/arch-arm/syscalls/__preadv64.S new file mode 100644 index 000000000..19eaaa54e --- /dev/null +++ b/libc/arch-arm/syscalls/__preadv64.S @@ -0,0 +1,22 @@ +/* Generated by gensyscalls.py. Do not edit. */ + +#include + +ENTRY(__preadv64) + mov ip, sp + stmfd sp!, {r4, r5, r6, r7} + .cfi_def_cfa_offset 16 + .cfi_rel_offset r4, 0 + .cfi_rel_offset r5, 4 + .cfi_rel_offset r6, 8 + .cfi_rel_offset r7, 12 + ldmfd ip, {r4, r5, r6} + ldr r7, =__NR_preadv + swi #0 + ldmfd sp!, {r4, r5, r6, r7} + .cfi_def_cfa_offset 0 + cmn r0, #(MAX_ERRNO + 1) + bxls lr + neg r0, r0 + b __set_errno_internal +END(__preadv64) diff --git a/libc/arch-arm/syscalls/__pwritev64.S b/libc/arch-arm/syscalls/__pwritev64.S new file mode 100644 index 000000000..afcbe403e --- /dev/null +++ b/libc/arch-arm/syscalls/__pwritev64.S @@ -0,0 +1,22 @@ +/* Generated by gensyscalls.py. Do not edit. */ + +#include + +ENTRY(__pwritev64) + mov ip, sp + stmfd sp!, {r4, r5, r6, r7} + .cfi_def_cfa_offset 16 + .cfi_rel_offset r4, 0 + .cfi_rel_offset r5, 4 + .cfi_rel_offset r6, 8 + .cfi_rel_offset r7, 12 + ldmfd ip, {r4, r5, r6} + ldr r7, =__NR_pwritev + swi #0 + ldmfd sp!, {r4, r5, r6, r7} + .cfi_def_cfa_offset 0 + cmn r0, #(MAX_ERRNO + 1) + bxls lr + neg r0, r0 + b __set_errno_internal +END(__pwritev64) diff --git a/libc/arch-arm64/syscalls/preadv.S b/libc/arch-arm64/syscalls/preadv.S new file mode 100644 index 000000000..cb8300d51 --- /dev/null +++ b/libc/arch-arm64/syscalls/preadv.S @@ -0,0 +1,16 @@ +/* Generated by gensyscalls.py. Do not edit. */ + +#include + +ENTRY(preadv) + mov x8, __NR_preadv + svc #0 + + cmn x0, #(MAX_ERRNO + 1) + cneg x0, x0, hi + b.hi __set_errno_internal + + ret +END(preadv) + +ALIAS_SYMBOL(preadv64, preadv) diff --git a/libc/arch-arm64/syscalls/pwritev.S b/libc/arch-arm64/syscalls/pwritev.S new file mode 100644 index 000000000..621466a6a --- /dev/null +++ b/libc/arch-arm64/syscalls/pwritev.S @@ -0,0 +1,16 @@ +/* Generated by gensyscalls.py. Do not edit. */ + +#include + +ENTRY(pwritev) + mov x8, __NR_pwritev + svc #0 + + cmn x0, #(MAX_ERRNO + 1) + cneg x0, x0, hi + b.hi __set_errno_internal + + ret +END(pwritev) + +ALIAS_SYMBOL(pwritev64, pwritev) diff --git a/libc/arch-mips/syscalls/__preadv64.S b/libc/arch-mips/syscalls/__preadv64.S new file mode 100644 index 000000000..a46b86983 --- /dev/null +++ b/libc/arch-mips/syscalls/__preadv64.S @@ -0,0 +1,19 @@ +/* Generated by gensyscalls.py. Do not edit. */ + +#include + +ENTRY(__preadv64) + .set noreorder + .cpload t9 + li v0, __NR_preadv + syscall + bnez a3, 1f + move a0, v0 + j ra + nop +1: + la t9,__set_errno_internal + j t9 + nop + .set reorder +END(__preadv64) diff --git a/libc/arch-mips/syscalls/__pwritev64.S b/libc/arch-mips/syscalls/__pwritev64.S new file mode 100644 index 000000000..1222942f2 --- /dev/null +++ b/libc/arch-mips/syscalls/__pwritev64.S @@ -0,0 +1,19 @@ +/* Generated by gensyscalls.py. Do not edit. */ + +#include + +ENTRY(__pwritev64) + .set noreorder + .cpload t9 + li v0, __NR_pwritev + syscall + bnez a3, 1f + move a0, v0 + j ra + nop +1: + la t9,__set_errno_internal + j t9 + nop + .set reorder +END(__pwritev64) diff --git a/libc/arch-mips64/syscalls/preadv.S b/libc/arch-mips64/syscalls/preadv.S new file mode 100644 index 000000000..df43a8dc1 --- /dev/null +++ b/libc/arch-mips64/syscalls/preadv.S @@ -0,0 +1,27 @@ +/* Generated by gensyscalls.py. Do not edit. */ + +#include + +ENTRY(preadv) + .set push + .set noreorder + li v0, __NR_preadv + syscall + bnez a3, 1f + move a0, v0 + j ra + nop +1: + move t0, ra + bal 2f + nop +2: + .cpsetup ra, t1, 2b + LA t9,__set_errno_internal + .cpreturn + j t9 + move ra, t0 + .set pop +END(preadv) + +ALIAS_SYMBOL(preadv64, preadv) diff --git a/libc/arch-mips64/syscalls/pwritev.S b/libc/arch-mips64/syscalls/pwritev.S new file mode 100644 index 000000000..f55cec02b --- /dev/null +++ b/libc/arch-mips64/syscalls/pwritev.S @@ -0,0 +1,27 @@ +/* Generated by gensyscalls.py. Do not edit. */ + +#include + +ENTRY(pwritev) + .set push + .set noreorder + li v0, __NR_pwritev + syscall + bnez a3, 1f + move a0, v0 + j ra + nop +1: + move t0, ra + bal 2f + nop +2: + .cpsetup ra, t1, 2b + LA t9,__set_errno_internal + .cpreturn + j t9 + move ra, t0 + .set pop +END(pwritev) + +ALIAS_SYMBOL(pwritev64, pwritev) diff --git a/libc/arch-x86/syscalls/__preadv64.S b/libc/arch-x86/syscalls/__preadv64.S new file mode 100644 index 000000000..3ce4fc3d9 --- /dev/null +++ b/libc/arch-x86/syscalls/__preadv64.S @@ -0,0 +1,41 @@ +/* Generated by gensyscalls.py. Do not edit. */ + +#include + +ENTRY(__preadv64) + pushl %ebx + .cfi_def_cfa_offset 8 + .cfi_rel_offset ebx, 0 + pushl %ecx + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset ecx, 0 + pushl %edx + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset edx, 0 + pushl %esi + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset esi, 0 + pushl %edi + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset edi, 0 + mov 24(%esp), %ebx + mov 28(%esp), %ecx + mov 32(%esp), %edx + mov 36(%esp), %esi + mov 40(%esp), %edi + movl $__NR_preadv, %eax + int $0x80 + cmpl $-MAX_ERRNO, %eax + jb 1f + negl %eax + pushl %eax + call __set_errno_internal + addl $4, %esp +1: + popl %edi + popl %esi + popl %edx + popl %ecx + popl %ebx + ret +END(__preadv64) diff --git a/libc/arch-x86/syscalls/__pwritev64.S b/libc/arch-x86/syscalls/__pwritev64.S new file mode 100644 index 000000000..2ce5b0ee5 --- /dev/null +++ b/libc/arch-x86/syscalls/__pwritev64.S @@ -0,0 +1,41 @@ +/* Generated by gensyscalls.py. Do not edit. */ + +#include + +ENTRY(__pwritev64) + pushl %ebx + .cfi_def_cfa_offset 8 + .cfi_rel_offset ebx, 0 + pushl %ecx + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset ecx, 0 + pushl %edx + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset edx, 0 + pushl %esi + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset esi, 0 + pushl %edi + .cfi_adjust_cfa_offset 4 + .cfi_rel_offset edi, 0 + mov 24(%esp), %ebx + mov 28(%esp), %ecx + mov 32(%esp), %edx + mov 36(%esp), %esi + mov 40(%esp), %edi + movl $__NR_pwritev, %eax + int $0x80 + cmpl $-MAX_ERRNO, %eax + jb 1f + negl %eax + pushl %eax + call __set_errno_internal + addl $4, %esp +1: + popl %edi + popl %esi + popl %edx + popl %ecx + popl %ebx + ret +END(__pwritev64) diff --git a/libc/arch-x86_64/syscalls/preadv.S b/libc/arch-x86_64/syscalls/preadv.S new file mode 100644 index 000000000..7771a4447 --- /dev/null +++ b/libc/arch-x86_64/syscalls/preadv.S @@ -0,0 +1,18 @@ +/* Generated by gensyscalls.py. Do not edit. */ + +#include + +ENTRY(preadv) + movq %rcx, %r10 + movl $__NR_preadv, %eax + syscall + cmpq $-MAX_ERRNO, %rax + jb 1f + negl %eax + movl %eax, %edi + call __set_errno_internal +1: + ret +END(preadv) + +ALIAS_SYMBOL(preadv64, preadv) diff --git a/libc/arch-x86_64/syscalls/pwritev.S b/libc/arch-x86_64/syscalls/pwritev.S new file mode 100644 index 000000000..d3bd8facb --- /dev/null +++ b/libc/arch-x86_64/syscalls/pwritev.S @@ -0,0 +1,18 @@ +/* Generated by gensyscalls.py. Do not edit. */ + +#include + +ENTRY(pwritev) + movq %rcx, %r10 + movl $__NR_pwritev, %eax + syscall + cmpq $-MAX_ERRNO, %rax + jb 1f + negl %eax + movl %eax, %edi + call __set_errno_internal +1: + ret +END(pwritev) + +ALIAS_SYMBOL(pwritev64, pwritev) diff --git a/libc/bionic/legacy_32_bit_support.cpp b/libc/bionic/legacy_32_bit_support.cpp index a10766471..20d258d7d 100644 --- a/libc/bionic/legacy_32_bit_support.cpp +++ b/libc/bionic/legacy_32_bit_support.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -43,6 +44,8 @@ // System calls we need. extern "C" int __fcntl64(int, int, void*); extern "C" int __llseek(int, unsigned long, unsigned long, off64_t*, int); +extern "C" int __preadv64(int, const struct iovec*, int, long, long); +extern "C" int __pwritev64(int, const struct iovec*, int, long, long); // For fcntl we use the fcntl64 system call to signal that we're using struct flock64. int fcntl(int fd, int cmd, ...) { @@ -77,6 +80,23 @@ ssize_t pwrite(int fd, const void* buf, size_t byte_count, off_t offset) { return pwrite64(fd, buf, byte_count, static_cast(offset)); } +// On LP32, there is no off_t preadv/pwritev, and even the 64-bit preadv/pwritev +// don't use off64_t (see SYSCALLS.TXT for more). Here, this means that we need +// to implement all four functions because the two system calls don't match any +// of the userspace functions. Unlike llseek, the pair is split lo-hi, not hi-lo. +ssize_t preadv(int fd, const struct iovec* ios, int count, off_t offset) { + return __preadv64(fd, ios, count, offset, 0); +} +ssize_t preadv64(int fd, const struct iovec* ios, int count, off64_t offset) { + return __preadv64(fd, ios, count, offset, offset >> 32); +} +ssize_t pwritev(int fd, const struct iovec* ios, int count, off_t offset) { + return __pwritev64(fd, ios, count, offset, 0); +} +ssize_t pwritev64(int fd, const struct iovec* ios, int count, off64_t offset) { + return __pwritev64(fd, ios, count, offset, offset >> 32); +} + // There is no fallocate for 32-bit off_t, so we need to widen and call fallocate64. int fallocate(int fd, int mode, off_t offset, off_t length) { return fallocate64(fd, mode, static_cast(offset), static_cast(length)); diff --git a/libc/include/sys/uio.h b/libc/include/sys/uio.h index 187ec22a4..72675d127 100644 --- a/libc/include/sys/uio.h +++ b/libc/include/sys/uio.h @@ -37,6 +37,18 @@ __BEGIN_DECLS int readv(int, const struct iovec*, int); int writev(int, const struct iovec*, int); +#if defined(__USE_GNU) +#if defined(__USE_FILE_OFFSET64) +ssize_t preadv(int, const struct iovec*, int, off_t) __RENAME(preadv64); +ssize_t pwritev(int, const struct iovec*, int, off_t) __RENAME(pwritev64); +#else +ssize_t preadv(int, const struct iovec*, int, off_t); +ssize_t pwritev(int, const struct iovec*, int, off_t); +#endif +ssize_t preadv64(int, const struct iovec*, int, off64_t); +ssize_t pwritev64(int, const struct iovec*, int, off64_t); +#endif + #if defined(__USE_GNU) ssize_t process_vm_readv(pid_t, const struct iovec*, unsigned long, const struct iovec*, unsigned long, unsigned long); ssize_t process_vm_writev(pid_t, const struct iovec*, unsigned long, const struct iovec*, unsigned long, unsigned long); diff --git a/libc/libc.map b/libc/libc.map index 945692050..fbceb832a 100644 --- a/libc/libc.map +++ b/libc/libc.map @@ -1342,6 +1342,10 @@ LIBC_N { __write_chk; getgrgid_r; getgrnam_r; + preadv; + preadv64; + pwritev; + pwritev64; strchrnul; } LIBC; diff --git a/tests/sys_uio_test.cpp b/tests/sys_uio_test.cpp index c7af8a77c..569d4fbad 100644 --- a/tests/sys_uio_test.cpp +++ b/tests/sys_uio_test.cpp @@ -18,10 +18,60 @@ #include -TEST(sys_uio, process_vm_readv_ESRCH) { +#include "TemporaryFile.h" + +TEST(sys_uio, readv_writev) { + TemporaryFile tf; + + char buf1[] = "hello"; + char buf2[] = "world"; + iovec ios[] = { { buf1, 5 }, { buf2, 5 } }; + + ASSERT_EQ(10, writev(tf.fd, ios, 2)); + + ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)); + + memset(buf1, '1', sizeof(buf1)); + memset(buf2, '2', sizeof(buf2)); + + ASSERT_EQ(10, readv(tf.fd, ios, 2)); + buf1[5] = buf2[5] = '\0'; + ASSERT_STREQ("hello", buf1); + ASSERT_STREQ("world", buf2); +} + +template +void TestPreadVPwriteV(ReadFn read_fn, WriteFn write_fn) { + TemporaryFile tf; + + char buf[] = "world"; + iovec ios[] = { { buf, 5 } }; + + ASSERT_EQ(5, write_fn(tf.fd, ios, 1, 5)); + ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_CUR)); + + strcpy(buf, "hello"); + ASSERT_EQ(5, write_fn(tf.fd, ios, 1, 0)); + ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_CUR)); + + ASSERT_EQ(5, read_fn(tf.fd, ios, 1, 5)); + ASSERT_STREQ("world", buf); + ASSERT_EQ(5, read_fn(tf.fd, ios, 1, 0)); + ASSERT_STREQ("hello", buf); +} + +TEST(sys_uio, preadv_pwritev) { + TestPreadVPwriteV(preadv, pwritev); +} + +TEST(sys_uio, preadv64_pwritev64) { + TestPreadVPwriteV(preadv64, pwritev64); +} + +TEST(sys_uio, process_vm_readv) { ASSERT_EQ(0, process_vm_readv(0, nullptr, 0, nullptr, 0, 0)); } -TEST(sys_uio, process_vm_writev_ESRCH) { +TEST(sys_uio, process_vm_writev) { ASSERT_EQ(0, process_vm_writev(0, nullptr, 0, nullptr, 0, 0)); }