Merge "Don't re-export libunwind_llvm."
diff --git a/libc/Android.mk b/libc/Android.mk
index c75b825..d6b208c 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -214,6 +214,7 @@
bionic/socket.cpp \
bionic/stat.cpp \
bionic/statvfs.cpp \
+ bionic/strchrnul.cpp \
bionic/strerror.cpp \
bionic/strerror_r.cpp \
bionic/strsignal.cpp \
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index ac8db62..4188c15 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -94,6 +94,16 @@
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 0000000..19eaaa5
--- /dev/null
+++ b/libc/arch-arm/syscalls/__preadv64.S
@@ -0,0 +1,22 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+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 0000000..afcbe40
--- /dev/null
+++ b/libc/arch-arm/syscalls/__pwritev64.S
@@ -0,0 +1,22 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+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 0000000..cb8300d
--- /dev/null
+++ b/libc/arch-arm64/syscalls/preadv.S
@@ -0,0 +1,16 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+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 0000000..621466a
--- /dev/null
+++ b/libc/arch-arm64/syscalls/pwritev.S
@@ -0,0 +1,16 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+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 0000000..a46b869
--- /dev/null
+++ b/libc/arch-mips/syscalls/__preadv64.S
@@ -0,0 +1,19 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+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 0000000..1222942
--- /dev/null
+++ b/libc/arch-mips/syscalls/__pwritev64.S
@@ -0,0 +1,19 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+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 0000000..df43a8d
--- /dev/null
+++ b/libc/arch-mips64/syscalls/preadv.S
@@ -0,0 +1,27 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+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 0000000..f55cec0
--- /dev/null
+++ b/libc/arch-mips64/syscalls/pwritev.S
@@ -0,0 +1,27 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+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 0000000..3ce4fc3
--- /dev/null
+++ b/libc/arch-x86/syscalls/__preadv64.S
@@ -0,0 +1,41 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+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 0000000..2ce5b0e
--- /dev/null
+++ b/libc/arch-x86/syscalls/__pwritev64.S
@@ -0,0 +1,41 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+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 0000000..7771a44
--- /dev/null
+++ b/libc/arch-x86_64/syscalls/preadv.S
@@ -0,0 +1,18 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+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 0000000..d3bd8fa
--- /dev/null
+++ b/libc/arch-x86_64/syscalls/pwritev.S
@@ -0,0 +1,18 @@
+/* Generated by gensyscalls.py. Do not edit. */
+
+#include <private/bionic_asm.h>
+
+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/debug_stacktrace.cpp b/libc/bionic/debug_stacktrace.cpp
index 4100911..b49a42e 100644
--- a/libc/bionic/debug_stacktrace.cpp
+++ b/libc/bionic/debug_stacktrace.cpp
@@ -90,15 +90,22 @@
// Modify the pc to point at the real function.
if (ip != 0) {
#if defined(__arm__)
- // We need to do a quick check here to find out if the previous
- // instruction is a Thumb-mode BLX(2). If so subtract 2 otherwise
- // 4 from PC.
- short* ptr = reinterpret_cast<short*>(ip);
- // Thumb BLX(2)
- if ((*(ptr-1) & 0xff80) == 0x4780) {
- ip -= 2;
- } else {
- ip -= 4;
+ // If the ip is suspiciously low, do nothing to avoid a segfault trying
+ // to access this memory.
+ if (ip >= 4096) {
+ // Check bits [15:11] of the first halfword assuming the instruction
+ // is 32 bits long. If the bits are any of these values, then our
+ // assumption was correct:
+ // b11101
+ // b11110
+ // b11111
+ // Otherwise, this is a 16 bit instruction.
+ uint16_t value = (*reinterpret_cast<uint16_t*>(ip - 2)) >> 11;
+ if (value == 0x1f || value == 0x1e || value == 0x1d) {
+ ip -= 4;
+ } else {
+ ip -= 2;
+ }
}
#elif defined(__aarch64__)
// All instructions are 4 bytes long, skip back one instruction.
diff --git a/libc/bionic/legacy_32_bit_support.cpp b/libc/bionic/legacy_32_bit_support.cpp
index a107664..20d258d 100644
--- a/libc/bionic/legacy_32_bit_support.cpp
+++ b/libc/bionic/legacy_32_bit_support.cpp
@@ -33,6 +33,7 @@
#include <stdarg.h>
#include <sys/resource.h>
#include <sys/types.h>
+#include <sys/uio.h>
#include <sys/vfs.h>
#include <unistd.h>
@@ -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 @@
return pwrite64(fd, buf, byte_count, static_cast<off64_t>(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<off64_t>(offset), static_cast<off64_t>(length));
diff --git a/libc/bionic/strchrnul.cpp b/libc/bionic/strchrnul.cpp
new file mode 100644
index 0000000..55422e0
--- /dev/null
+++ b/libc/bionic/strchrnul.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern "C" const char* strchrnul(const char* s, int ch) {
+ while (*s && *s != ch) {
+ ++s;
+ }
+ return s;
+}
diff --git a/libc/include/string.h b/libc/include/string.h
index d32c164..2c50cfa 100644
--- a/libc/include/string.h
+++ b/libc/include/string.h
@@ -53,6 +53,14 @@
extern char* strchr(const char *, int) __purefunc;
extern char* __strchr_chk(const char *, int, size_t);
+#if defined(__USE_GNU)
+#if defined(__cplusplus)
+extern "C++" char* strchrnul(char*, int) __RENAME(strchrnul) __purefunc;
+extern "C++" const char* strchrnul(const char*, int) __RENAME(strchrnul) __purefunc;
+#else
+char* strchrnul(const char*, int) __purefunc;
+#endif
+#endif
extern char* strrchr(const char *, int) __purefunc;
extern char* __strrchr_chk(const char *, int, size_t);
diff --git a/libc/include/sys/ucontext.h b/libc/include/sys/ucontext.h
index 399458e..9e3c653 100644
--- a/libc/include/sys/ucontext.h
+++ b/libc/include/sys/ucontext.h
@@ -82,12 +82,6 @@
#define NGREG 34 /* x0..x30 + sp + pc + pstate */
typedef unsigned long greg_t;
typedef greg_t gregset_t[NGREG];
-
-struct user_fpsimd_struct {
- long double vregs[32];
- uint32_t fpsr;
- uint32_t fpcr;
-};
typedef struct user_fpsimd_struct fpregset_t;
#include <asm/sigcontext.h>
diff --git a/libc/include/sys/uio.h b/libc/include/sys/uio.h
index 187ec22..72675d1 100644
--- a/libc/include/sys/uio.h
+++ b/libc/include/sys/uio.h
@@ -38,6 +38,18 @@
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);
#endif
diff --git a/libc/include/sys/user.h b/libc/include/sys/user.h
index ce4c07c..3312981 100644
--- a/libc/include/sys/user.h
+++ b/libc/include/sys/user.h
@@ -232,7 +232,17 @@
#elif defined(__aarch64__)
-// There are no user structures for 64 bit arm.
+struct user_regs_struct {
+ uint64_t regs[31];
+ uint64_t sp;
+ uint64_t pc;
+ uint64_t pstate;
+};
+struct user_fpsimd_struct {
+ __uint128_t vregs[32];
+ uint32_t fpsr;
+ uint32_t fpcr;
+};
#else
diff --git a/libc/libc.map b/libc/libc.map
index 4670079..fbceb83 100644
--- a/libc/libc.map
+++ b/libc/libc.map
@@ -1342,6 +1342,11 @@
__write_chk;
getgrgid_r;
getgrnam_r;
+ preadv;
+ preadv64;
+ pwritev;
+ pwritev64;
+ strchrnul;
} LIBC;
LIBC_PRIVATE {
diff --git a/tests/string_test.cpp b/tests/string_test.cpp
index cf90045..80190d7 100644
--- a/tests/string_test.cpp
+++ b/tests/string_test.cpp
@@ -454,6 +454,13 @@
}
}
+TEST(string, strchrnul) {
+ const char* s = "01234222";
+ EXPECT_TRUE(strchrnul(s, '2') == &s[2]);
+ EXPECT_TRUE(strchrnul(s, '8') == (s + strlen(s)));
+ EXPECT_TRUE(strchrnul(s, '\0') == (s + strlen(s)));
+}
+
TEST(string, strcmp) {
StringTestState<char> state(SMALL);
for (size_t i = 1; i < state.n; i++) {
diff --git a/tests/sys_uio_test.cpp b/tests/sys_uio_test.cpp
index c7af8a7..569d4fb 100644
--- a/tests/sys_uio_test.cpp
+++ b/tests/sys_uio_test.cpp
@@ -18,10 +18,60 @@
#include <sys/uio.h>
-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 <typename ReadFn, typename WriteFn>
+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));
}
diff --git a/tests/thread_local_test.cpp b/tests/thread_local_test.cpp
index 5dead32..aeba2ba 100644
--- a/tests/thread_local_test.cpp
+++ b/tests/thread_local_test.cpp
@@ -15,6 +15,8 @@
*/
#include <gtest/gtest.h>
+#include <stdint.h>
+#include <string.h>
#ifdef __GNUC__
// Gcc has a bug with -O -fdata-section for the arm target: http://b/22772147.
@@ -166,3 +168,92 @@
ASSERT_EQ(local_triangle[1].y, 4);
ASSERT_EQ(shared_triangle[1].y, 3);
}
+
+// Test emutls runtime data structures and __emutls_get_address function.
+typedef unsigned int gcc_word __attribute__((mode(word)));
+typedef unsigned int gcc_pointer __attribute__((mode(pointer)));
+struct gcc_emutls_object { // for libgcc
+ gcc_word size;
+ gcc_word align;
+ union {
+ gcc_pointer offset;
+ void* ptr;
+ } loc;
+ void* templ;
+};
+
+typedef struct __emutls_control { // for clang/llvm
+ size_t size;
+ size_t align;
+ union {
+ uintptr_t index;
+ void* address;
+ } object;
+ void* value;
+} __emutls_control;
+
+TEST(thread_local_storage, type_size) {
+ static_assert(sizeof(size_t) == sizeof(gcc_word),
+ "size_t != gcc_word");
+ static_assert(sizeof(uintptr_t) == sizeof(gcc_pointer),
+ "uintptr_t != gcc_pointer");
+ static_assert(sizeof(uintptr_t) == sizeof(void*),
+ "sizoeof(uintptr_t) != sizeof(void*)");
+ static_assert(sizeof(__emutls_control) == sizeof(struct gcc_emutls_object),
+ "sizeof(__emutls_control) != sizeof(struct gcc_emutls_object)");
+}
+
+extern "C" void* __emutls_get_address(__emutls_control*);
+
+TEST(thread_local_storage, init_value) {
+ char tls_value1[] = "123456789";
+ char tls_value2[] = "abcdefghi";
+ constexpr size_t num_saved_values = 10;
+ __emutls_control tls_var[num_saved_values];
+ size_t prev_index = 0;
+ void* saved_gap[num_saved_values];
+ void* saved_p[num_saved_values];
+ ASSERT_TRUE(strlen(tls_value2) <= strlen(tls_value1));
+ __emutls_control c =
+ {strlen(tls_value1) + 1, 1, {0}, tls_value1};
+ for (size_t n = 0; n < num_saved_values; n++) {
+ memcpy(&tls_var[n], &c, sizeof(c));
+ tls_var[n].align = (1 << n);
+ }
+ for (size_t n = 0; n < num_saved_values; n++) {
+ // Try to mess up malloc space so that the next malloc will not have the
+ // required alignment, but __emutls_get_address should still return an
+ // aligned address.
+ saved_gap[n] = malloc(1);
+ void* p = __emutls_get_address(&tls_var[n]);
+ saved_p[n] = p;
+ ASSERT_TRUE(p != nullptr);
+ ASSERT_TRUE(tls_var[n].object.index != 0);
+ // check if p is a new object.
+ if (n > 0) {
+ // In single-thread environment, object.address == p.
+ // In multi-threads environment, object.index is increased.
+ ASSERT_TRUE(prev_index + 1 == tls_var[n].object.index ||
+ p == tls_var[n].object.address);
+ ASSERT_TRUE(p != saved_p[n - 1]);
+ }
+ prev_index = tls_var[n].object.index;
+ // check if p is aligned
+ uintptr_t align = (1 << n);
+ uintptr_t address= reinterpret_cast<uintptr_t>(p);
+ ASSERT_EQ((address & ~(align - 1)), address);
+ // check if *p is initialized
+ ASSERT_STREQ(tls_value1, static_cast<char*>(p));
+ // change value in *p
+ memcpy(p, tls_value2, strlen(tls_value2) + 1);
+ }
+ for (size_t n = 0; n < num_saved_values; n++) {
+ free(saved_gap[n]);
+ }
+ for (size_t n = 0; n < num_saved_values; n++) {
+ void* p = __emutls_get_address(&tls_var[n]);
+ ASSERT_EQ(p, saved_p[n]);
+ // check if *p has the new value
+ ASSERT_STREQ(tls_value2, static_cast<char*>(p));
+ }
+}