Merge "Add support for serialized property contexts"
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..2ea4125
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,11 @@
+enh@google.com
+
+cferris@google.com
+danalbert@google.com
+jmgao@google.com
+rprichard@google.com
+tomcherry@google.com
+yabinc@google.com
+
+# Still the best reviewer for changes related to the dynamic linker.
+dimitry@google.com
diff --git a/benchmarks/stdio_benchmark.cpp b/benchmarks/stdio_benchmark.cpp
index d547fed..872725d 100644
--- a/benchmarks/stdio_benchmark.cpp
+++ b/benchmarks/stdio_benchmark.cpp
@@ -15,6 +15,7 @@
*/
#include <err.h>
+#include <inttypes.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
@@ -205,3 +206,33 @@
}
}
BIONIC_BENCHMARK(BM_stdio_printf_1$s);
+
+static void BM_stdio_scanf_s(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ char s[BUFSIZ];
+ if (sscanf("file /etc/passwd", "file %s", s) != 1) abort();
+ }
+}
+BIONIC_BENCHMARK(BM_stdio_scanf_s);
+
+static void BM_stdio_scanf_d(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ int i;
+ if (sscanf("size 12345", "size %d", &i) != 1) abort();
+ }
+}
+BIONIC_BENCHMARK(BM_stdio_scanf_d);
+
+static void BM_stdio_scanf_maps(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ uintptr_t start;
+ uintptr_t end;
+ uintptr_t offset;
+ char permissions[5];
+ int name_pos;
+ if (sscanf("6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so",
+ "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %*d %n",
+ &start, &end, permissions, &offset, &name_pos) != 4) abort();
+ }
+}
+BIONIC_BENCHMARK(BM_stdio_scanf_maps);
diff --git a/benchmarks/suites/vdso.xml b/benchmarks/suites/vdso.xml
new file mode 100644
index 0000000..1f5958c
--- /dev/null
+++ b/benchmarks/suites/vdso.xml
@@ -0,0 +1,51 @@
+<fn>
+ <name>BM_time_clock_getres</name>
+</fn>
+<fn>
+ <name>BM_time_clock_getres_BOOTTIME</name>
+</fn>
+<fn>
+ <name>BM_time_clock_getres_MONOTONIC_COARSE</name>
+</fn>
+<fn>
+ <name>BM_time_clock_getres_MONOTONIC_RAW</name>
+</fn>
+<fn>
+ <name>BM_time_clock_getres_REALTIME</name>
+</fn>
+<fn>
+ <name>BM_time_clock_getres_REALTIME_COARSE</name>
+</fn>
+<fn>
+ <name>BM_time_clock_getres_syscall</name>
+</fn>
+<fn>
+ <name>BM_time_clock_gettime</name>
+</fn>
+<fn>
+ <name>BM_time_clock_gettime_BOOTTIME</name>
+</fn>
+<fn>
+ <name>BM_time_clock_gettime_MONOTONIC_COARSE</name>
+</fn>
+<fn>
+ <name>BM_time_clock_gettime_MONOTONIC_RAW</name>
+</fn>
+<fn>
+ <name>BM_time_clock_gettime_REALTIME</name>
+</fn>
+<fn>
+ <name>BM_time_clock_gettime_REALTIME_COARSE</name>
+</fn>
+<fn>
+ <name>BM_time_clock_gettime_syscall</name>
+</fn>
+<fn>
+ <name>BM_time_gettimeofday</name>
+</fn>
+<fn>
+ <name>BM_time_gettimeofday_syscall</name>
+</fn>
+<fn>
+ <name>BM_time_time</name>
+</fn>
diff --git a/benchmarks/tests/interface_test.cpp b/benchmarks/tests/interface_test.cpp
index 33e506e..64629e6 100644
--- a/benchmarks/tests/interface_test.cpp
+++ b/benchmarks/tests/interface_test.cpp
@@ -298,6 +298,9 @@
"BM_stdio_printf_d/iterations:1\n"
"BM_stdio_printf_literal/iterations:1\n"
"BM_stdio_printf_s/iterations:1\n"
+ "BM_stdio_scanf_d/iterations:1\n"
+ "BM_stdio_scanf_maps/iterations:1\n"
+ "BM_stdio_scanf_s/iterations:1\n"
"BM_stdlib_malloc_free/8/iterations:1\n"
"BM_stdlib_malloc_free/64/iterations:1\n"
"BM_stdlib_malloc_free/512/iterations:1\n"
@@ -435,7 +438,19 @@
"BM_string_strstr/32768/0/0/iterations:1\n"
"BM_string_strstr/65536/0/0/iterations:1\n"
"BM_string_strstr/131072/0/0/iterations:1\n"
+ "BM_time_clock_getres/iterations:1\n"
+ "BM_time_clock_getres_BOOTTIME/iterations:1\n"
+ "BM_time_clock_getres_MONOTONIC_COARSE/iterations:1\n"
+ "BM_time_clock_getres_MONOTONIC_RAW/iterations:1\n"
+ "BM_time_clock_getres_REALTIME/iterations:1\n"
+ "BM_time_clock_getres_REALTIME_COARSE/iterations:1\n"
+ "BM_time_clock_getres_syscall/iterations:1\n"
"BM_time_clock_gettime/iterations:1\n"
+ "BM_time_clock_gettime_BOOTTIME/iterations:1\n"
+ "BM_time_clock_gettime_MONOTONIC_COARSE/iterations:1\n"
+ "BM_time_clock_gettime_MONOTONIC_RAW/iterations:1\n"
+ "BM_time_clock_gettime_REALTIME/iterations:1\n"
+ "BM_time_clock_gettime_REALTIME_COARSE/iterations:1\n"
"BM_time_clock_gettime_syscall/iterations:1\n"
"BM_time_gettimeofday/iterations:1\n"
"BM_time_gettimeofday_syscall/iterations:1\n"
diff --git a/benchmarks/time_benchmark.cpp b/benchmarks/time_benchmark.cpp
index 4c6d5dd..437dc78 100644
--- a/benchmarks/time_benchmark.cpp
+++ b/benchmarks/time_benchmark.cpp
@@ -23,6 +23,7 @@
#include "util.h"
static void BM_time_clock_gettime(benchmark::State& state) {
+ // CLOCK_MONOTONIC is required supported in vdso
timespec t;
while (state.KeepRunning()) {
clock_gettime(CLOCK_MONOTONIC, &t);
@@ -31,6 +32,7 @@
BIONIC_BENCHMARK(BM_time_clock_gettime);
static void BM_time_clock_gettime_syscall(benchmark::State& state) {
+ // CLOCK_MONOTONIC is required supported in vdso
timespec t;
while (state.KeepRunning()) {
syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &t);
@@ -38,6 +40,114 @@
}
BIONIC_BENCHMARK(BM_time_clock_gettime_syscall);
+static void BM_time_clock_gettime_MONOTONIC_COARSE(benchmark::State& state) {
+ // CLOCK_MONOTONIC_COARSE is required supported in vdso
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &t);
+ }
+}
+BIONIC_BENCHMARK(BM_time_clock_gettime_MONOTONIC_COARSE);
+
+static void BM_time_clock_gettime_MONOTONIC_RAW(benchmark::State& state) {
+ // CLOCK_MONOTONIC_RAW is required supported in vdso
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_gettime(CLOCK_MONOTONIC_RAW, &t);
+ }
+}
+BIONIC_BENCHMARK(BM_time_clock_gettime_MONOTONIC_RAW);
+
+static void BM_time_clock_gettime_REALTIME(benchmark::State& state) {
+ // CLOCK_REALTIME is required supported in vdso
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_gettime(CLOCK_REALTIME, &t);
+ }
+}
+BIONIC_BENCHMARK(BM_time_clock_gettime_REALTIME);
+
+static void BM_time_clock_gettime_REALTIME_COARSE(benchmark::State& state) {
+ // CLOCK_REALTIME_COARSE is required supported in vdso
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_gettime(CLOCK_REALTIME_COARSE, &t);
+ }
+}
+BIONIC_BENCHMARK(BM_time_clock_gettime_REALTIME_COARSE);
+
+static void BM_time_clock_gettime_BOOTTIME(benchmark::State& state) {
+ // CLOCK_BOOTTIME is optionally supported in vdso
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_gettime(CLOCK_BOOTTIME, &t);
+ }
+}
+BIONIC_BENCHMARK(BM_time_clock_gettime_BOOTTIME);
+
+static void BM_time_clock_getres(benchmark::State& state) {
+ // CLOCK_MONOTONIC is required supported in vdso
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_getres(CLOCK_MONOTONIC, &t);
+ }
+}
+BIONIC_BENCHMARK(BM_time_clock_getres);
+
+static void BM_time_clock_getres_syscall(benchmark::State& state) {
+ // CLOCK_MONOTONIC is required supported in vdso
+ timespec t;
+ while (state.KeepRunning()) {
+ syscall(__NR_clock_getres, CLOCK_MONOTONIC, &t);
+ }
+}
+BIONIC_BENCHMARK(BM_time_clock_getres_syscall);
+
+static void BM_time_clock_getres_MONOTONIC_COARSE(benchmark::State& state) {
+ // CLOCK_MONOTONIC_COARSE is required supported in vdso
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_getres(CLOCK_MONOTONIC_COARSE, &t);
+ }
+}
+BIONIC_BENCHMARK(BM_time_clock_getres_MONOTONIC_COARSE);
+
+static void BM_time_clock_getres_MONOTONIC_RAW(benchmark::State& state) {
+ // CLOCK_MONOTONIC_RAW is required supported in vdso
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_getres(CLOCK_MONOTONIC_RAW, &t);
+ }
+}
+BIONIC_BENCHMARK(BM_time_clock_getres_MONOTONIC_RAW);
+
+static void BM_time_clock_getres_REALTIME(benchmark::State& state) {
+ // CLOCK_REALTIME is required supported in vdso
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_getres(CLOCK_REALTIME, &t);
+ }
+}
+BIONIC_BENCHMARK(BM_time_clock_getres_REALTIME);
+
+static void BM_time_clock_getres_REALTIME_COARSE(benchmark::State& state) {
+ // CLOCK_REALTIME_COARSE is required supported in vdso
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_getres(CLOCK_REALTIME_COARSE, &t);
+ }
+}
+BIONIC_BENCHMARK(BM_time_clock_getres_REALTIME_COARSE);
+
+static void BM_time_clock_getres_BOOTTIME(benchmark::State& state) {
+ // CLOCK_BOOTTIME is optionally supported in vdso
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_getres(CLOCK_BOOTTIME, &t);
+ }
+}
+BIONIC_BENCHMARK(BM_time_clock_getres_BOOTTIME);
+
static void BM_time_gettimeofday(benchmark::State& state) {
timeval tv;
while (state.KeepRunning()) {
diff --git a/libc/Android.bp b/libc/Android.bp
index cba86dd..52e53e2 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -2038,6 +2038,7 @@
license: "NOTICE",
}
+// Not actually used in the NDK, but needed to build AOSP for mips.
ndk_headers {
name: "libc_asm_mips",
from: "kernel/uapi/asm-mips",
@@ -2046,6 +2047,7 @@
license: "NOTICE",
}
+// Not actually used in the NDK, but needed to build AOSP for mips64.
ndk_headers {
name: "libc_asm_mips64",
from: "kernel/uapi/asm-mips",
diff --git a/libc/SYSCALLS.TXT b/libc/SYSCALLS.TXT
index f8be0b0..5c9a284 100644
--- a/libc/SYSCALLS.TXT
+++ b/libc/SYSCALLS.TXT
@@ -212,7 +212,6 @@
clock_t times(struct tms*) all
int nanosleep(const struct timespec*, struct timespec*) all
int clock_settime(clockid_t, const struct timespec*) all
-int clock_getres(clockid_t, struct timespec*) all
int ___clock_nanosleep:clock_nanosleep(clockid_t, int, const struct timespec*, struct timespec*) all
int getitimer(int, const struct itimerval*) all
int setitimer(int, const struct itimerval*, struct itimerval*) all
@@ -351,6 +350,7 @@
int __set_thread_area:set_thread_area(void*) x86
# vdso stuff.
+int __clock_getres:clock_getres(clockid_t, struct timespec*) all
int __clock_gettime:clock_gettime(clockid_t, timespec*) all
int __gettimeofday:gettimeofday(timeval*, timezone*) all
diff --git a/libc/arch-arm/syscalls/clock_getres.S b/libc/arch-arm/syscalls/__clock_getres.S
similarity index 88%
rename from libc/arch-arm/syscalls/clock_getres.S
rename to libc/arch-arm/syscalls/__clock_getres.S
index e101127..439b5b8 100644
--- a/libc/arch-arm/syscalls/clock_getres.S
+++ b/libc/arch-arm/syscalls/__clock_getres.S
@@ -2,7 +2,7 @@
#include <private/bionic_asm.h>
-ENTRY(clock_getres)
+ENTRY(__clock_getres)
mov ip, r7
.cfi_register r7, ip
ldr r7, =__NR_clock_getres
@@ -13,4 +13,4 @@
bxls lr
neg r0, r0
b __set_errno_internal
-END(clock_getres)
+END(__clock_getres)
diff --git a/libc/arch-arm64/syscalls/clock_getres.S b/libc/arch-arm64/syscalls/__clock_getres.S
similarity index 78%
rename from libc/arch-arm64/syscalls/clock_getres.S
rename to libc/arch-arm64/syscalls/__clock_getres.S
index 33fda8f..9d0deaa 100644
--- a/libc/arch-arm64/syscalls/clock_getres.S
+++ b/libc/arch-arm64/syscalls/__clock_getres.S
@@ -2,7 +2,7 @@
#include <private/bionic_asm.h>
-ENTRY(clock_getres)
+ENTRY(__clock_getres)
mov x8, __NR_clock_getres
svc #0
@@ -11,4 +11,5 @@
b.hi __set_errno_internal
ret
-END(clock_getres)
+END(__clock_getres)
+.hidden __clock_getres
diff --git a/libc/arch-mips/syscalls/clock_getres.S b/libc/arch-mips/syscalls/__clock_getres.S
similarity index 87%
rename from libc/arch-mips/syscalls/clock_getres.S
rename to libc/arch-mips/syscalls/__clock_getres.S
index 153f629..d4e67c5 100644
--- a/libc/arch-mips/syscalls/clock_getres.S
+++ b/libc/arch-mips/syscalls/__clock_getres.S
@@ -2,7 +2,7 @@
#include <private/bionic_asm.h>
-ENTRY(clock_getres)
+ENTRY(__clock_getres)
.set noreorder
.cpload $t9
li $v0, __NR_clock_getres
@@ -16,4 +16,4 @@
j $t9
nop
.set reorder
-END(clock_getres)
+END(__clock_getres)
diff --git a/libc/arch-mips64/syscalls/clock_getres.S b/libc/arch-mips64/syscalls/__clock_getres.S
similarity index 85%
rename from libc/arch-mips64/syscalls/clock_getres.S
rename to libc/arch-mips64/syscalls/__clock_getres.S
index 32fac1a..fb871c9 100644
--- a/libc/arch-mips64/syscalls/clock_getres.S
+++ b/libc/arch-mips64/syscalls/__clock_getres.S
@@ -2,7 +2,7 @@
#include <private/bionic_asm.h>
-ENTRY(clock_getres)
+ENTRY(__clock_getres)
.set push
.set noreorder
li $v0, __NR_clock_getres
@@ -22,4 +22,5 @@
j $t9
move $ra, $t0
.set pop
-END(clock_getres)
+END(__clock_getres)
+.hidden __clock_getres
diff --git a/libc/arch-x86/syscalls/clock_getres.S b/libc/arch-x86/syscalls/__clock_getres.S
similarity index 93%
rename from libc/arch-x86/syscalls/clock_getres.S
rename to libc/arch-x86/syscalls/__clock_getres.S
index 9466e08..77b0aa9 100644
--- a/libc/arch-x86/syscalls/clock_getres.S
+++ b/libc/arch-x86/syscalls/__clock_getres.S
@@ -2,7 +2,7 @@
#include <private/bionic_asm.h>
-ENTRY(clock_getres)
+ENTRY(__clock_getres)
pushl %ebx
.cfi_def_cfa_offset 8
.cfi_rel_offset ebx, 0
@@ -31,4 +31,4 @@
popl %ecx
popl %ebx
ret
-END(clock_getres)
+END(__clock_getres)
diff --git a/libc/arch-x86_64/syscalls/clock_getres.S b/libc/arch-x86_64/syscalls/__clock_getres.S
similarity index 80%
rename from libc/arch-x86_64/syscalls/clock_getres.S
rename to libc/arch-x86_64/syscalls/__clock_getres.S
index f65d127..aff974e 100644
--- a/libc/arch-x86_64/syscalls/clock_getres.S
+++ b/libc/arch-x86_64/syscalls/__clock_getres.S
@@ -2,7 +2,7 @@
#include <private/bionic_asm.h>
-ENTRY(clock_getres)
+ENTRY(__clock_getres)
movl $__NR_clock_getres, %eax
syscall
cmpq $-MAX_ERRNO, %rax
@@ -12,4 +12,5 @@
call __set_errno_internal
1:
ret
-END(clock_getres)
+END(__clock_getres)
+.hidden __clock_getres
diff --git a/libc/bionic/vdso.cpp b/libc/bionic/vdso.cpp
index 969c39f..00d6490 100644
--- a/libc/bionic/vdso.cpp
+++ b/libc/bionic/vdso.cpp
@@ -42,6 +42,15 @@
return __clock_gettime(clock_id, tp);
}
+int clock_getres(int clock_id, timespec* tp) {
+ auto vdso_clock_getres = reinterpret_cast<decltype(&clock_getres)>(
+ __libc_globals->vdso[VDSO_CLOCK_GETRES].fn);
+ if (__predict_true(vdso_clock_getres)) {
+ return vdso_return(vdso_clock_getres(clock_id, tp));
+ }
+ return __clock_getres(clock_id, tp);
+}
+
int gettimeofday(timeval* tv, struct timezone* tz) {
auto vdso_gettimeofday = reinterpret_cast<decltype(&gettimeofday)>(
__libc_globals->vdso[VDSO_GETTIMEOFDAY].fn);
@@ -54,6 +63,7 @@
void __libc_init_vdso(libc_globals* globals, KernelArgumentBlock& args) {
auto&& vdso = globals->vdso;
vdso[VDSO_CLOCK_GETTIME] = { VDSO_CLOCK_GETTIME_SYMBOL, nullptr };
+ vdso[VDSO_CLOCK_GETRES] = { VDSO_CLOCK_GETRES_SYMBOL, nullptr };
vdso[VDSO_GETTIMEOFDAY] = { VDSO_GETTIMEOFDAY_SYMBOL, nullptr };
// Do we have a vdso?
diff --git a/libc/private/bionic_vdso.h b/libc/private/bionic_vdso.h
index 2d11cd6..ed11501 100644
--- a/libc/private/bionic_vdso.h
+++ b/libc/private/bionic_vdso.h
@@ -33,13 +33,16 @@
#if defined(__aarch64__)
#define VDSO_CLOCK_GETTIME_SYMBOL "__kernel_clock_gettime"
+#define VDSO_CLOCK_GETRES_SYMBOL "__kernel_clock_getres"
#define VDSO_GETTIMEOFDAY_SYMBOL "__kernel_gettimeofday"
#else
#define VDSO_CLOCK_GETTIME_SYMBOL "__vdso_clock_gettime"
+#define VDSO_CLOCK_GETRES_SYMBOL "__vdso_clock_getres"
#define VDSO_GETTIMEOFDAY_SYMBOL "__vdso_gettimeofday"
#endif
extern "C" int __clock_gettime(int, timespec*);
+extern "C" int __clock_getres(int, timespec*);
extern "C" int __gettimeofday(timeval*, struct timezone*);
struct vdso_entry {
@@ -49,6 +52,7 @@
enum {
VDSO_CLOCK_GETTIME = 0,
+ VDSO_CLOCK_GETRES,
VDSO_GETTIMEOFDAY,
VDSO_END
};
diff --git a/libc/stdio/vfscanf.c b/libc/stdio/vfscanf.c
index 853e0cf..887b435 100644
--- a/libc/stdio/vfscanf.c
+++ b/libc/stdio/vfscanf.c
@@ -83,9 +83,6 @@
#define CT_INT 3 /* integer, i.e., strtoimax or strtoumax */
#define CT_FLOAT 4 /* floating, i.e., strtod */
-#define u_char unsigned char
-#define u_long unsigned long
-
static u_char* __sccl(char*, u_char*);
/*
@@ -192,9 +189,6 @@
/*
* Conversions.
* Those marked `compat' are for 4.[123]BSD compatibility.
- *
- * (According to ANSI, E and X formats are supposed
- * to the same as e and x. Sorry about that.)
*/
case 'D': /* compat */
flags |= LONG;
diff --git a/libc/stdio/vfwscanf.c b/libc/stdio/vfwscanf.c
index 206f4a2..1030a62 100644
--- a/libc/stdio/vfwscanf.c
+++ b/libc/stdio/vfwscanf.c
@@ -84,12 +84,43 @@
#define CT_INT 3 /* integer, i.e., strtoimax or strtoumax */
#define CT_FLOAT 4 /* floating, i.e., strtod */
-#define u_char unsigned char
-#define u_long unsigned long
+// An interpretive version of __sccl from vfscanf.c --- a table of all wchar_t values would
+// be a little too expensive, and some kind of compressed version isn't worth the trouble.
+static inline bool in_ccl(wchar_t wc, const wchar_t* ccl) {
+ // Is this a negated set?
+ bool member_result = true;
+ if (*ccl == '^') {
+ member_result = false;
+ ++ccl;
+ }
-#define INCCL(_c) \
- (cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) \
- : (wmemchr(ccls, (_c), ccle - ccls) != NULL))
+ // The first character may be ']' or '-' without being special.
+ if (*ccl == '-' || *ccl == ']') {
+ // A literal match?
+ if (*ccl == wc) return member_result;
+ ++ccl;
+ }
+
+ while (*ccl && *ccl != ']') {
+ // The last character may be '-' without being special.
+ if (*ccl == '-' && ccl[1] != '\0' && ccl[1] != ']') {
+ wchar_t first = *(ccl - 1);
+ wchar_t last = *(ccl + 1);
+ if (first <= last) {
+ // In the range?
+ if (wc >= first && wc <= last) return member_result;
+ ccl += 2;
+ continue;
+ }
+ // A '-' is not considered to be part of a range if the character after
+ // is not greater than the character before, so fall through...
+ }
+ // A literal match?
+ if (*ccl == wc) return member_result;
+ ++ccl;
+ }
+ return !member_result;
+}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wframe-larger-than="
@@ -109,9 +140,7 @@
int nread; /* number of characters consumed from fp */
int base; /* base argument to strtoimax/strtouimax */
wchar_t buf[BUF]; /* buffer for numeric conversions */
- const wchar_t* ccls; /* character class start */
- const wchar_t* ccle; /* character class end */
- int cclcompl; /* ccl is complemented? */
+ const wchar_t* ccl;
wint_t wi; /* handy wint_t */
char* mbp; /* multibyte string pointer for %c %s %[ */
size_t nconv; /* number of bytes in mb. conversion */
@@ -127,7 +156,6 @@
nconversions = 0;
nread = 0;
base = 0; /* XXX just to keep gcc happy */
- ccls = ccle = NULL;
for (;;) {
c = *fmt++;
if (c == 0) {
@@ -210,9 +238,6 @@
/*
* Conversions.
* Those marked `compat' are for 4.[123]BSD compatibility.
- *
- * (According to ANSI, E and X formats are supposed
- * to the same as e and x. Sorry about that.)
*/
case 'D': /* compat */
flags |= LONG;
@@ -266,15 +291,10 @@
break;
case '[':
- ccls = fmt;
- if (*fmt == '^') {
- cclcompl = 1;
- fmt++;
- } else
- cclcompl = 0;
+ ccl = fmt;
+ if (*fmt == '^') fmt++;
if (*fmt == ']') fmt++;
while (*fmt != '\0' && *fmt != ']') fmt++;
- ccle = fmt;
fmt++;
flags |= NOSKIP;
c = CT_CCL;
@@ -387,12 +407,12 @@
/* take only those things in the class */
if ((flags & SUPPRESS) && (flags & LONG)) {
n = 0;
- while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && INCCL(wi)) n++;
+ while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && in_ccl(wi, ccl)) n++;
if (wi != WEOF) __ungetwc(wi, fp);
if (n == 0) goto match_failure;
} else if (flags & LONG) {
p0 = p = va_arg(ap, wchar_t*);
- while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && INCCL(wi))
+ while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && in_ccl(wi, ccl))
*p++ = (wchar_t)wi;
if (wi != WEOF) __ungetwc(wi, fp);
n = p - p0;
@@ -403,7 +423,7 @@
if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
n = 0;
memset(&mbs, 0, sizeof(mbs));
- while ((wi = __fgetwc_unlock(fp)) != WEOF && width != 0 && INCCL(wi)) {
+ while ((wi = __fgetwc_unlock(fp)) != WEOF && width != 0 && in_ccl(wi, ccl)) {
if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
nconv = wcrtomb(mbp, wi, &mbs);
if (nconv == (size_t)-1) goto input_failure;
@@ -418,6 +438,7 @@
n++;
}
if (wi != WEOF) __ungetwc(wi, fp);
+ if (n == 0) goto match_failure;
if (!(flags & SUPPRESS)) {
*mbp = 0;
nassigned++;
diff --git a/linker/linker.cpp b/linker/linker.cpp
index abfd00f..4ad44fa 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -328,9 +328,7 @@
TRACE("name %s: freeing soinfo @ %p", si->get_realpath(), si);
if (!solist_remove_soinfo(si)) {
- // TODO (dimitry): revisit this - for now preserving the logic
- // but it does not look right, abort if soinfo is not in the list instead?
- return;
+ async_safe_fatal("soinfo=%p is not in soinfo_list (double unload?)", si);
}
// clear links to/from si
@@ -718,13 +716,11 @@
// walk_dependencies_tree returns false if walk was terminated
// by the action and true otherwise.
template<typename F>
-static bool walk_dependencies_tree(soinfo* root_soinfos[], size_t root_soinfos_size, F action) {
+static bool walk_dependencies_tree(soinfo* root_soinfo, F action) {
SoinfoLinkedList visit_list;
SoinfoLinkedList visited;
- for (size_t i = 0; i < root_soinfos_size; ++i) {
- visit_list.push_back(root_soinfos[i]);
- }
+ visit_list.push_back(root_soinfo);
soinfo* si;
while ((si = visit_list.pop_front()) != nullptr) {
@@ -760,7 +756,7 @@
const ElfW(Sym)* result = nullptr;
bool skip_lookup = skip_until != nullptr;
- walk_dependencies_tree(&root, 1, [&](soinfo* current_soinfo) {
+ walk_dependencies_tree(root, [&](soinfo* current_soinfo) {
if (skip_lookup) {
skip_lookup = current_soinfo != skip_until;
return kWalkContinue;
@@ -1478,7 +1474,6 @@
if (load_library(linked_namespace.linked_namespace(), task, zip_archive_cache, load_tasks, rtld_flags, false)) {
return true;
}
- // lib was not found in the namespace. Try next linked namespace.
} else {
// lib is already loaded
return true;
@@ -1491,7 +1486,6 @@
}
static void soinfo_unload(soinfo* si);
-static void soinfo_unload(soinfo* soinfos[], size_t count);
static void shuffle(std::vector<LoadTask*>* v) {
for (size_t i = 0, size = v->size(); i < size; ++i) {
@@ -1516,9 +1510,9 @@
const android_dlextinfo* extinfo,
bool add_as_children,
bool search_linked_namespaces,
- std::unordered_map<const soinfo*, ElfReader>& readers_map,
std::vector<android_namespace_t*>* namespaces) {
// Step 0: prepare.
+ std::unordered_map<const soinfo*, ElfReader> readers_map;
LoadTaskList load_tasks;
for (size_t i = 0; i < library_names_count; ++i) {
@@ -1548,11 +1542,6 @@
}
});
- auto failure_guard = android::base::make_scope_guard([&]() {
- // Housekeeping
- soinfo_unload(soinfos, soinfos_count);
- });
-
ZipArchiveCache zip_archive_cache;
// Step 1: expand the list of load_tasks to include
@@ -1565,7 +1554,6 @@
task->set_extinfo(is_dt_needed ? nullptr : extinfo);
task->set_dt_needed(is_dt_needed);
- // try to find the load.
// Note: start from the namespace that is stored in the LoadTask. This namespace
// is different from the current namespace when the LoadTask is for a transitive
// dependency and the lib that created the LoadTask is not found in the
@@ -1583,10 +1571,6 @@
if (is_dt_needed) {
needed_by->add_child(si);
-
- if (si->is_linked()) {
- si->increment_ref_count();
- }
}
// When ld_preloads is not null, the first
@@ -1662,77 +1646,116 @@
}
}
- // Step 5: link libraries that are not destined to this namespace.
- // Do this by recursively calling find_libraries on the namespace where the lib
- // was found during Step 1.
+ // Step 5: Collect roots of local_groups.
+ // Whenever needed_by->si link crosses a namespace boundary it forms its own local_group.
+ // Here we collect new roots to link them separately later on. Note that we need to avoid
+ // collecting duplicates. Also the order is important. They need to be linked in the same
+ // BFS order we link individual libraries.
+ std::vector<soinfo*> local_group_roots;
+ if (start_with != nullptr && add_as_children) {
+ local_group_roots.push_back(start_with);
+ } else {
+ CHECK(soinfos_count == 1);
+ local_group_roots.push_back(soinfos[0]);
+ }
+
for (auto&& task : load_tasks) {
soinfo* si = task->get_soinfo();
- if (si->get_primary_namespace() != ns) {
- const char* name = task->get_name();
- if (find_libraries(si->get_primary_namespace(), task->get_needed_by(), &name, 1,
- nullptr /* soinfos */, nullptr /* ld_preloads */, 0 /* ld_preload_count */,
- rtld_flags, nullptr /* extinfo */, false /* add_as_children */,
- false /* search_linked_namespaces */, readers_map, namespaces)) {
- // If this lib is directly needed by one of the libs in this namespace,
- // then increment the count
- soinfo* needed_by = task->get_needed_by();
- if (needed_by != nullptr && needed_by->get_primary_namespace() == ns && si->is_linked()) {
- si->increment_ref_count();
- }
- } else {
- return false;
+ soinfo* needed_by = task->get_needed_by();
+ bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children);
+ android_namespace_t* needed_by_ns =
+ is_dt_needed ? needed_by->get_primary_namespace() : ns;
+
+ if (!si->is_linked() && si->get_primary_namespace() != needed_by_ns) {
+ auto it = std::find(local_group_roots.begin(), local_group_roots.end(), si);
+ LD_LOG(kLogDlopen,
+ "Crossing namespace boundary (si=%s@%p, si_ns=%s@%p, needed_by=%s@%p, ns=%s@%p, needed_by_ns=%s@%p) adding to local_group_roots: %s",
+ si->get_realpath(),
+ si,
+ si->get_primary_namespace()->get_name(),
+ si->get_primary_namespace(),
+ needed_by == nullptr ? "(nullptr)" : needed_by->get_realpath(),
+ needed_by,
+ ns->get_name(),
+ ns,
+ needed_by_ns->get_name(),
+ needed_by_ns,
+ it == local_group_roots.end() ? "yes" : "no");
+
+ if (it == local_group_roots.end()) {
+ local_group_roots.push_back(si);
}
}
}
- // Step 6: link libraries in this namespace
- soinfo_list_t local_group;
- walk_dependencies_tree(
- (start_with != nullptr && add_as_children) ? &start_with : soinfos,
- (start_with != nullptr && add_as_children) ? 1 : soinfos_count,
+ // Step 6: Link all local groups
+ for (auto root : local_group_roots) {
+ soinfo_list_t local_group;
+ android_namespace_t* local_group_ns = root->get_primary_namespace();
+
+ walk_dependencies_tree(root,
[&] (soinfo* si) {
- if (ns->is_accessible(si)) {
- local_group.push_back(si);
- return kWalkContinue;
- } else {
- return kWalkSkip;
- }
- });
+ if (local_group_ns->is_accessible(si)) {
+ local_group.push_back(si);
+ return kWalkContinue;
+ } else {
+ return kWalkSkip;
+ }
+ });
- soinfo_list_t global_group = ns->get_global_group();
- bool linked = local_group.visit([&](soinfo* si) {
- if (!si->is_linked()) {
- if (!si->link_image(global_group, local_group, extinfo) ||
- !get_cfi_shadow()->AfterLoad(si, solist_get_head())) {
- return false;
+ soinfo_list_t global_group = local_group_ns->get_global_group();
+ bool linked = local_group.visit([&](soinfo* si) {
+ // Even though local group may contain accessible soinfos from other namesapces
+ // we should avoid linking them (because if they are not linked -> they
+ // are in the local_group_roots and will be linked later).
+ if (!si->is_linked() && si->get_primary_namespace() == local_group_ns) {
+ if (!si->link_image(global_group, local_group, extinfo) ||
+ !get_cfi_shadow()->AfterLoad(si, solist_get_head())) {
+ return false;
+ }
}
- }
- return true;
- });
-
- if (linked) {
- local_group.for_each([](soinfo* si) {
- if (!si->is_linked()) {
- si->set_linked();
- }
+ return true;
});
- failure_guard.Disable();
+ if (!linked) {
+ return false;
+ }
}
- return linked;
+ // Step 7: Mark all load_tasks as linked and increment refcounts
+ // for references between load_groups (at this point it does not matter if
+ // referenced load_groups were loaded by previous dlopen or as part of this
+ // one on step 6)
+ if (start_with != nullptr && add_as_children) {
+ start_with->set_linked();
+ }
+
+ for (auto&& task : load_tasks) {
+ soinfo* si = task->get_soinfo();
+ si->set_linked();
+ }
+
+ for (auto&& task : load_tasks) {
+ soinfo* si = task->get_soinfo();
+ soinfo* needed_by = task->get_needed_by();
+ if (needed_by != nullptr &&
+ needed_by != start_with &&
+ needed_by->get_local_group_root() != si->get_local_group_root()) {
+ si->increment_ref_count();
+ }
+ }
+
+
+ return true;
}
static soinfo* find_library(android_namespace_t* ns,
const char* name, int rtld_flags,
const android_dlextinfo* extinfo,
soinfo* needed_by) {
- soinfo* si;
+ soinfo* si = nullptr;
- // readers_map is shared across recursive calls to find_libraries.
- // However, the map is not shared across different threads.
- std::unordered_map<const soinfo*, ElfReader> readers_map;
if (name == nullptr) {
si = solist_get_somain();
} else if (!find_libraries(ns,
@@ -1745,8 +1768,10 @@
rtld_flags,
extinfo,
false /* add_as_children */,
- true /* search_linked_namespaces */,
- readers_map)) {
+ true /* search_linked_namespaces */)) {
+ if (si != nullptr) {
+ soinfo_unload(si);
+ }
return nullptr;
}
@@ -1755,18 +1780,26 @@
return si;
}
-static void soinfo_unload(soinfo* si) {
- soinfo* root = si->is_linked() ? si->get_local_group_root() : si;
+static void soinfo_unload(soinfo* unload_si) {
+ // Note that the library can be loaded but not linked;
+ // in which case there is no root but we still need
+ // to walk the tree and unload soinfos involved.
+ //
+ // This happens on unsuccessful dlopen, when one of
+ // the DT_NEEDED libraries could not be linked/found.
+ bool is_linked = unload_si->is_linked();
+ soinfo* root = is_linked ? unload_si->get_local_group_root() : unload_si;
LD_LOG(kLogDlopen,
"... dlclose(realpath=\"%s\"@%p) ... load group root is \"%s\"@%p",
- si->get_realpath(),
- si,
+ unload_si->get_realpath(),
+ unload_si,
root->get_realpath(),
root);
ScopedTrace trace((std::string("unload ") + root->get_realpath()).c_str());
+ size_t ref_count = is_linked ? root->decrement_ref_count() : 0;
if (!root->can_unload()) {
LD_LOG(kLogDlopen,
"... dlclose(root=\"%s\"@%p) ... not unloading - the load group is flagged with NODELETE",
@@ -1775,48 +1808,17 @@
return;
}
- soinfo_unload(&root, 1);
-}
-
-static void soinfo_unload(soinfo* soinfos[], size_t count) {
- // Note that the library can be loaded but not linked;
- // in which case there is no root but we still need
- // to walk the tree and unload soinfos involved.
- //
- // This happens on unsuccessful dlopen, when one of
- // the DT_NEEDED libraries could not be linked/found.
- if (count == 0) {
+ if (ref_count > 0) {
+ LD_LOG(kLogDlopen,
+ "... dlclose(root=\"%s\"@%p) ... not unloading - decrementing ref_count to %zd",
+ root->get_realpath(),
+ root,
+ ref_count);
return;
}
soinfo_list_t unload_list;
- for (size_t i = 0; i < count; ++i) {
- soinfo* si = soinfos[i];
-
- if (si->can_unload()) {
- size_t ref_count = si->is_linked() ? si->decrement_ref_count() : 0;
- if (ref_count == 0) {
- unload_list.push_back(si);
- } else {
- LD_LOG(kLogDlopen,
- "... dlclose(root=\"%s\"@%p) ... not unloading - decrementing ref_count to %zd",
- si->get_realpath(),
- si,
- ref_count);
- }
- } else {
- LD_LOG(kLogDlopen,
- "... dlclose(root=\"%s\"@%p) ... not unloading - the load group is flagged with NODELETE",
- si->get_realpath(),
- si);
- return;
- }
- }
-
- // This is used to identify soinfos outside of the load-group
- // note that we cannot have > 1 in the array and have any of them
- // linked. This is why we can safely use the first one.
- soinfo* root = soinfos[0];
+ unload_list.push_back(root);
soinfo_list_t local_unload_list;
soinfo_list_t external_unload_list;
@@ -1900,12 +1902,17 @@
soinfo_free(si);
}
- while ((si = external_unload_list.pop_front()) != nullptr) {
- LD_LOG(kLogDlopen,
- "... dlclose: unloading external reference \"%s\"@%p ...",
- si->get_realpath(),
- si);
- soinfo_unload(si);
+ if (is_linked) {
+ while ((si = external_unload_list.pop_front()) != nullptr) {
+ LD_LOG(kLogDlopen,
+ "... dlclose: unloading external reference \"%s\"@%p ...",
+ si->get_realpath(),
+ si);
+ soinfo_unload(si);
+ }
+ } else {
+ LD_LOG(kLogDlopen,
+ "... dlclose: unload_si was not linked - not unloading external references ...");
}
}
@@ -3368,6 +3375,10 @@
bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group,
const android_dlextinfo* extinfo) {
+ if (is_image_linked()) {
+ // already linked.
+ return true;
+ }
local_group_root_ = local_group.front();
if (local_group_root_ == nullptr) {
@@ -3510,6 +3521,7 @@
}
notify_gdb_of_load(this);
+ set_image_linked();
return true;
}
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
index 317f0d2..dc1fa75 100644
--- a/linker/linker_main.cpp
+++ b/linker/linker_main.cpp
@@ -384,9 +384,6 @@
const char** needed_library_names = &needed_library_name_list[0];
size_t needed_libraries_count = needed_library_name_list.size();
- // readers_map is shared across recursive calls to find_libraries so that we
- // don't need to re-load elf headers.
- std::unordered_map<const soinfo*, ElfReader> readers_map;
if (needed_libraries_count > 0 &&
!find_libraries(&g_default_namespace,
si,
@@ -399,7 +396,6 @@
nullptr,
true /* add_as_children */,
true /* search_linked_namespaces */,
- readers_map,
&namespaces)) {
__linker_cannot_link(g_argv[0]);
} else if (needed_libraries_count == 0) {
diff --git a/linker/linker_main.h b/linker/linker_main.h
index 2cf30c2..5641696 100644
--- a/linker/linker_main.h
+++ b/linker/linker_main.h
@@ -65,7 +65,6 @@
const android_dlextinfo* extinfo,
bool add_as_children,
bool search_linked_namespaces,
- std::unordered_map<const soinfo*, ElfReader>& readers_map,
std::vector<android_namespace_t*>* namespaces = nullptr);
void solist_add_soinfo(soinfo* si);
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index fbff7cf..dd91752 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -657,6 +657,10 @@
return (flags_ & FLAG_LINKED) != 0;
}
+bool soinfo::is_image_linked() const {
+ return (flags_ & FLAG_IMAGE_LINKED) != 0;
+}
+
bool soinfo::is_main_executable() const {
return (flags_ & FLAG_EXE) != 0;
}
@@ -669,6 +673,10 @@
flags_ |= FLAG_LINKED;
}
+void soinfo::set_image_linked() {
+ flags_ |= FLAG_IMAGE_LINKED;
+}
+
void soinfo::set_linker_flag() {
flags_ |= FLAG_LINKER;
}
@@ -677,8 +685,8 @@
flags_ |= FLAG_EXE;
}
-void soinfo::increment_ref_count() {
- local_group_root_->ref_count_++;
+size_t soinfo::increment_ref_count() {
+ return ++local_group_root_->ref_count_;
}
size_t soinfo::decrement_ref_count() {
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index 6f472b5..273c36c 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -41,6 +41,17 @@
#define FLAG_GNU_HASH 0x00000040 // uses gnu hash
#define FLAG_MAPPED_BY_CALLER 0x00000080 // the map is reserved by the caller
// and should not be unmapped
+#define FLAG_IMAGE_LINKED 0x00000100 // Is image linked - this is a guard on link_image.
+ // The difference between this flag and
+ // FLAG_LINKED is that FLAG_LINKED
+ // means is set when load_group is
+ // successfully loaded whereas this
+ // flag is set to avoid linking image
+ // when link_image called for the
+ // second time. This situation happens
+ // when load group is crossing
+ // namespace boundary twice and second
+ // local group depends on the same libraries.
#define FLAG_NEW_SOINFO 0x40000000 // new soinfo format
#define SOINFO_VERSION 3
@@ -243,7 +254,7 @@
void set_main_executable();
void set_nodelete();
- void increment_ref_count();
+ size_t increment_ref_count();
size_t decrement_ref_count();
soinfo* get_local_group_root() const;
@@ -273,6 +284,9 @@
void* to_handle();
private:
+ bool is_image_linked() const;
+ void set_image_linked();
+
bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
ElfW(Sym)* elf_addr_lookup(const void* addr);
bool gnu_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const;
diff --git a/tests/Android.bp b/tests/Android.bp
index c045c1e..28df084 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -51,11 +51,13 @@
name: "libBionicStandardTests",
defaults: ["bionic_tests_defaults"],
srcs: [
+ "alloca_test.cpp",
"arpa_inet_test.cpp",
"async_safe_test.cpp",
"assert_test.cpp",
"buffer_tests.cpp",
"bug_26110743_test.cpp",
+ "byteswap_test.cpp",
"complex_test.cpp",
"complex_force_long_double_test.cpp",
"ctype_test.cpp",
diff --git a/tests/alloca_test.cpp b/tests/alloca_test.cpp
new file mode 100644
index 0000000..192157d
--- /dev/null
+++ b/tests/alloca_test.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <alloca.h>
+
+#include <string.h>
+
+#include <gtest/gtest.h>
+
+TEST(alloca, alloca) {
+ // These days, alloca is usually a builtin, so we can't really assert much.
+ void* p = alloca(1024);
+ ASSERT_NE(nullptr, p);
+ memset(p, 0, 1024);
+}
diff --git a/tests/byteswap_test.cpp b/tests/byteswap_test.cpp
new file mode 100644
index 0000000..a18cfa6
--- /dev/null
+++ b/tests/byteswap_test.cpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <byteswap.h>
+
+#include <gtest/gtest.h>
+
+static constexpr uint16_t le16 = 0x1234;
+static constexpr uint32_t le32 = 0x12345678;
+static constexpr uint64_t le64 = 0x123456789abcdef0;
+
+static constexpr uint16_t be16 = 0x3412;
+static constexpr uint32_t be32 = 0x78563412;
+static constexpr uint64_t be64 = 0xf0debc9a78563412;
+
+TEST(byteswap, bswap_16) {
+ EXPECT_EQ(le16, bswap_16(be16));
+ EXPECT_EQ(be16, bswap_16(le16));
+}
+
+TEST(byteswap, bswap_32) {
+ EXPECT_EQ(le32, bswap_32(be32));
+ EXPECT_EQ(be32, bswap_32(le32));
+}
+
+TEST(byteswap, bswap_64) {
+ EXPECT_EQ(le64, bswap_64(be64));
+ EXPECT_EQ(be64, bswap_64(le64));
+}
+
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 249d341..04b83f2 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -1051,6 +1051,87 @@
"\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror());
}
+TEST(dlext, ns_unload_between_namespaces_missing_symbol_direct) {
+ ASSERT_TRUE(android_init_anonymous_namespace(g_core_shared_libs.c_str(), nullptr));
+
+ const std::string public_ns_search_path = get_testlib_root() + "/public_namespace_libs";
+ const std::string private_ns_search_path = get_testlib_root() + "/private_namespace_libs";
+
+ android_namespace_t* ns_public =
+ android_create_namespace("public",
+ nullptr,
+ public_ns_search_path.c_str(),
+ ANDROID_NAMESPACE_TYPE_ISOLATED,
+ nullptr,
+ nullptr);
+
+ ASSERT_TRUE(android_link_namespaces(ns_public, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+ android_namespace_t* ns_private =
+ android_create_namespace("private",
+ nullptr,
+ private_ns_search_path.c_str(),
+ ANDROID_NAMESPACE_TYPE_ISOLATED,
+ nullptr,
+ nullptr);
+
+ ASSERT_TRUE(android_link_namespaces(ns_private, ns_public, "libtest_missing_symbol.so")) << dlerror();
+ ASSERT_TRUE(android_link_namespaces(ns_private, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+ android_dlextinfo extinfo;
+ extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+ extinfo.library_namespace = ns_private;
+
+ void* handle = android_dlopen_ext((public_ns_search_path + "/libtest_missing_symbol.so").c_str(),
+ RTLD_NOW,
+ &extinfo);
+ ASSERT_TRUE(handle == nullptr);
+ ASSERT_EQ(std::string("dlopen failed: cannot locate symbol \"dlopen_testlib_missing_symbol\" referenced by \"") +
+ public_ns_search_path + "/libtest_missing_symbol.so\"...",
+ dlerror());
+}
+
+TEST(dlext, ns_unload_between_namespaces_missing_symbol_indirect) {
+ ASSERT_TRUE(android_init_anonymous_namespace(g_core_shared_libs.c_str(), nullptr));
+
+ const std::string public_ns_search_path = get_testlib_root() + "/public_namespace_libs";
+ const std::string private_ns_search_path = get_testlib_root() + "/private_namespace_libs";
+
+ android_namespace_t* ns_public =
+ android_create_namespace("public",
+ nullptr,
+ public_ns_search_path.c_str(),
+ ANDROID_NAMESPACE_TYPE_ISOLATED,
+ nullptr,
+ nullptr);
+
+ ASSERT_TRUE(android_link_namespaces(ns_public, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+ android_namespace_t* ns_private =
+ android_create_namespace("private",
+ nullptr,
+ private_ns_search_path.c_str(),
+ ANDROID_NAMESPACE_TYPE_ISOLATED,
+ nullptr,
+ nullptr);
+
+ ASSERT_TRUE(android_link_namespaces(ns_private,
+ ns_public,
+ "libnstest_public.so:libtest_missing_symbol_child_public.so")
+ ) << dlerror();
+ ASSERT_TRUE(android_link_namespaces(ns_private, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+ android_dlextinfo extinfo;
+ extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+ extinfo.library_namespace = ns_private;
+
+ void* handle = android_dlopen_ext("libtest_missing_symbol_root.so", RTLD_NOW, &extinfo);
+ ASSERT_TRUE(handle == nullptr);
+ ASSERT_EQ(std::string("dlopen failed: cannot locate symbol \"dlopen_testlib_missing_symbol\" referenced by \"") +
+ private_ns_search_path + "/libtest_missing_symbol_root.so\"...",
+ dlerror());
+}
+
TEST(dlext, ns_greylist_enabled) {
ASSERT_TRUE(android_init_anonymous_namespace(g_core_shared_libs.c_str(), nullptr));
diff --git a/tests/inttypes_test.cpp b/tests/inttypes_test.cpp
index da12ec4..08258ac 100644
--- a/tests/inttypes_test.cpp
+++ b/tests/inttypes_test.cpp
@@ -109,6 +109,18 @@
EXPECT_EQ(L'x', *end);
}
+TEST(inttypes, strtoimax_dec) {
+ char* p;
+ EXPECT_EQ(-18737357, strtoimax("-18737357foobar12", &p, 10));
+ EXPECT_STREQ("foobar12", p);
+}
+
+TEST(inttypes, strtoimax_hex) {
+ char* p;
+ EXPECT_EQ(-0x18737357f, strtoimax("-18737357foobar12", &p, 16));
+ EXPECT_STREQ("oobar12", p);
+}
+
TEST(inttypes, strtoimax_EINVAL) {
errno = 0;
strtoimax("123", NULL, -1);
@@ -121,6 +133,24 @@
ASSERT_EQ(EINVAL, errno);
}
+TEST(inttypes, strtoumax_dec) {
+ char* p;
+ EXPECT_EQ(18737357U, strtoumax("18737357foobar12", &p, 10));
+ EXPECT_STREQ("foobar12", p);
+}
+
+TEST(inttypes, strtoumax_hex) {
+ char* p;
+ EXPECT_EQ(0x18737357fU, strtoumax("18737357foobar12", &p, 16));
+ EXPECT_STREQ("oobar12", p);
+}
+
+TEST(inttypes, strtoumax_negative) {
+ char* p;
+ EXPECT_EQ(UINTMAX_MAX - 18737357 + 1, strtoumax("-18737357foobar12", &p, 10));
+ EXPECT_STREQ("foobar12", p);
+}
+
TEST(inttypes, strtoumax_EINVAL) {
errno = 0;
strtoumax("123", NULL, -1);
@@ -158,30 +188,88 @@
}
TEST(inttypes, div) {
- div_t r = div(-5, 3);
- ASSERT_EQ(-1, r.quot);
- ASSERT_EQ(-2, r.rem);
+ div_t r;
+
+ r = div(5, 3);
+ EXPECT_EQ(1, r.quot);
+ EXPECT_EQ(2, r.rem);
+
+ r = div(5, -3);
+ EXPECT_EQ(-1, r.quot);
+ EXPECT_EQ(2, r.rem);
+
+ r = div(-5, 3);
+ EXPECT_EQ(-1, r.quot);
+ EXPECT_EQ(-2, r.rem);
+
+ r = div(-5, -3);
+ EXPECT_EQ(1, r.quot);
+ EXPECT_EQ(-2, r.rem);
}
TEST(inttypes, ldiv) {
- ldiv_t r = ldiv(-5, 3);
- ASSERT_EQ(-1, r.quot);
- ASSERT_EQ(-2, r.rem);
+ ldiv_t r;
+
+ r = ldiv(5, 3);
+ EXPECT_EQ(1, r.quot);
+ EXPECT_EQ(2, r.rem);
+
+ r = ldiv(5, -3);
+ EXPECT_EQ(-1, r.quot);
+ EXPECT_EQ(2, r.rem);
+
+ r = ldiv(-5, 3);
+ EXPECT_EQ(-1, r.quot);
+ EXPECT_EQ(-2, r.rem);
+
+ r = ldiv(-5, -3);
+ EXPECT_EQ(1, r.quot);
+ EXPECT_EQ(-2, r.rem);
}
TEST(inttypes, lldiv) {
- lldiv_t r = lldiv(-5, 3);
- ASSERT_EQ(-1, r.quot);
- ASSERT_EQ(-2, r.rem);
+ lldiv_t r;
+
+ r = lldiv(5, 3);
+ EXPECT_EQ(1, r.quot);
+ EXPECT_EQ(2, r.rem);
+
+ r = lldiv(5, -3);
+ EXPECT_EQ(-1, r.quot);
+ EXPECT_EQ(2, r.rem);
+
+ r = lldiv(-5, 3);
+ EXPECT_EQ(-1, r.quot);
+ EXPECT_EQ(-2, r.rem);
+
+ r = lldiv(-5, -3);
+ EXPECT_EQ(1, r.quot);
+ EXPECT_EQ(-2, r.rem);
}
TEST(inttypes, imaxdiv) {
- imaxdiv_t r = imaxdiv(-5, 3);
- ASSERT_EQ(-1, r.quot);
- ASSERT_EQ(-2, r.rem);
+ imaxdiv_t r;
+
+ r = imaxdiv(5, 3);
+ EXPECT_EQ(1, r.quot);
+ EXPECT_EQ(2, r.rem);
+
+ r = imaxdiv(5, -3);
+ EXPECT_EQ(-1, r.quot);
+ EXPECT_EQ(2, r.rem);
+
+ r = imaxdiv(-5, 3);
+ EXPECT_EQ(-1, r.quot);
+ EXPECT_EQ(-2, r.rem);
+
+ r = imaxdiv(-5, -3);
+ EXPECT_EQ(1, r.quot);
+ EXPECT_EQ(-2, r.rem);
}
TEST(inttypes, imaxabs) {
- ASSERT_EQ(INTMAX_MAX, imaxabs(-INTMAX_MAX));
- ASSERT_EQ(INTMAX_MAX, imaxabs(INTMAX_MAX));
+ EXPECT_EQ(672423489, imaxabs(672423489));
+ EXPECT_EQ(672423489, imaxabs(-672423489));
+ EXPECT_EQ(INTMAX_MAX, imaxabs(-INTMAX_MAX));
+ EXPECT_EQ(INTMAX_MAX, imaxabs(INTMAX_MAX));
}
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 8d0271a..e45eb6e 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -113,6 +113,62 @@
}
// -----------------------------------------------------------------------------
+// Library used by dlext direct unload on the namespace boundary tests
+// -----------------------------------------------------------------------------
+cc_test_library {
+ name: "libtest_missing_symbol",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["dlopen_testlib_missing_symbol.cpp"],
+ allow_undefined_symbols: true,
+ relative_install_path: "bionic-loader-test-libs/public_namespace_libs",
+}
+
+
+// -----------------------------------------------------------------------------
+// Library used by dlext indirect unload on the namespace boundary tests
+//
+// These libraries produce following dependency graph:
+// libtest_missing_symbol_root (private ns)
+// +-> libbnstest_public (public ns)
+// +-> libtest_missing_symbol_child_public (public ns)
+// +-> libnstest_public (public ns)
+// +-> libtest_missing_symbol_child_private (private_ns)
+// +-> libnstest_public (public_ns)
+//
+// All libraries except libtest_missing_symbol are located in
+// private_namespace_libs/
+// -----------------------------------------------------------------------------
+cc_test_library {
+ name: "libtest_missing_symbol_child_public",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["empty.cpp"],
+ relative_install_path: "bionic-loader-test-libs/public_namespace_libs",
+ shared_libs: ["libnstest_public"],
+}
+
+cc_test_library {
+ name: "libtest_missing_symbol_child_private",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["empty.cpp"],
+ relative_install_path: "bionic-loader-test-libs/private_namespace_libs",
+ shared_libs: ["libnstest_public"],
+}
+
+cc_test_library {
+ name: "libtest_missing_symbol_root",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["dlopen_testlib_missing_symbol.cpp"],
+ relative_install_path: "bionic-loader-test-libs/private_namespace_libs",
+ allow_undefined_symbols: true,
+ shared_libs: [
+ "libnstest_public",
+ "libtest_missing_symbol_child_public",
+ "libtest_missing_symbol_child_private",
+ ],
+}
+
+// -----------------------------------------------------------------------------
+// -----------------------------------------------------------------------------
// Library used by dlfcn nodelete tests
// -----------------------------------------------------------------------------
cc_test_library {
@@ -142,8 +198,65 @@
// -----------------------------------------------------------------------------
// Build test helper libraries for linker namespaces
+//
+// This set of libraries is used to verify linker namespaces.
+//
+// Test cases
+// 1. Check that private libraries loaded in different namespaces are
+// different. Check that dlsym does not confuse them.
+// 2. Check that public libraries loaded in different namespaces are shared
+// between them.
+// 3. Check that namespace sticks on dlopen
+// 4. Check that having access to shared library (libnstest_public.so)
+// does not expose symbols from dependent library (libnstest_public_internal.so)
+//
+// Dependency tree (visibility)
+// libnstest_root.so (this should be local to the namespace)
+// +-> libnstest_public.so
+// +-> libnstest_public_internal.so
+// +-> libnstest_private.so
+//
+// libnstest_dlopened.so (library in private namespace dlopened from libnstest_root.so)
// -----------------------------------------------------------------------------
-// include $(LOCAL_PATH)/Android.build.linker_namespaces.mk
+cc_test_library {
+ name: "libnstest_root",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["namespaces_root.cpp"],
+ relative_install_path: "bionic-loader-test-libs/private_namespace_libs",
+ shared_libs: [
+ "libnstest_public",
+ "libnstest_private",
+ ],
+}
+
+cc_test_library {
+ name: "libnstest_public_internal",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["namespaces_public_internal.cpp"],
+ relative_install_path: "bionic-loader-test-libs/public_namespace_libs",
+}
+
+cc_test_library {
+ name: "libnstest_public",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["namespaces_public.cpp"],
+ relative_install_path: "bionic-loader-test-libs/public_namespace_libs",
+ shared_libs: ["libnstest_public_internal"],
+}
+
+cc_test_library {
+ name: "libnstest_private",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["namespaces_private.cpp"],
+ relative_install_path: "bionic-loader-test-libs/private_namespace_libs",
+}
+
+cc_test_library {
+ name: "libnstest_dlopened",
+ defaults: ["bionic_testlib_defaults"],
+ srcs: ["namespaces_dlopened.cpp"],
+ relative_install_path: "bionic-loader-test-libs/private_namespace_libs",
+}
// -----------------------------------------------------------------------------
// Build DT_RUNPATH test helper libraries
diff --git a/tests/libs/Android.build.linker_namespaces.mk b/tests/libs/Android.build.linker_namespaces.mk
index cd9d7f1..e2b01a7 100644
--- a/tests/libs/Android.build.linker_namespaces.mk
+++ b/tests/libs/Android.build.linker_namespaces.mk
@@ -19,51 +19,6 @@
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
-# Test cases
-# 1. Check that private libraries loaded in different namespaces are
-# different. Check that dlsym does not confuse them.
-# 2. Check that public libraries loaded in different namespaces are shared
-# between them.
-# 3. Check that namespace sticks on dlopen
-# 4. Check that having access to shared library (libnstest_public.so)
-# does not expose symbols from dependent library (libnstest_public_internal.so)
-#
-# Dependency tree (visibility)
-# libnstest_root.so (this should be local to the namespace)
-# +-> libnstest_public.so
-# +-> libnstest_public_internal.so
-# +-> libnstest_private.so
-#
-# libnstest_dlopened.so (library in private namespace dlopened from libnstest_root.so)
-# -----------------------------------------------------------------------------
-libnstest_root_src_files := namespaces_root.cpp
-libnstest_root_shared_libraries := libnstest_public libnstest_private
-libnstest_root_relative_install_path := private_namespace_libs
-module := libnstest_root
-include $(LOCAL_PATH)/Android.build.testlib.target.mk
-
-libnstest_public_internal_src_files := namespaces_public_internal.cpp
-module := libnstest_public_internal
-libnstest_public_internal_relative_install_path := public_namespace_libs
-include $(LOCAL_PATH)/Android.build.testlib.target.mk
-
-libnstest_public_src_files := namespaces_public.cpp
-libnstest_public_shared_libraries := libnstest_public_internal
-module := libnstest_public
-libnstest_public_relative_install_path := public_namespace_libs
-include $(LOCAL_PATH)/Android.build.testlib.target.mk
-
-libnstest_private_src_files := namespaces_private.cpp
-libnstest_private_relative_install_path := private_namespace_libs
-module := libnstest_private
-include $(LOCAL_PATH)/Android.build.testlib.target.mk
-
-libnstest_dlopened_src_files := namespaces_dlopened.cpp
-libnstest_dlopened_relative_install_path := private_namespace_libs
-module := libnstest_dlopened
-include $(LOCAL_PATH)/Android.build.testlib.target.mk
-
-# -----------------------------------------------------------------------------
# This set of libraries is to test isolated namespaces
#
# Isolated namespaces do not allow loading of the library outside of
diff --git a/tests/libs/dlopen_testlib_missing_symbol.cpp b/tests/libs/dlopen_testlib_missing_symbol.cpp
new file mode 100644
index 0000000..0f73c60
--- /dev/null
+++ b/tests/libs/dlopen_testlib_missing_symbol.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+extern "C" void dlopen_testlib_missing_symbol();
+
+extern "C" bool dlopen_testlib_simple_func() {
+ dlopen_testlib_missing_symbol();
+ return true;
+}
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
old mode 100755
new mode 100644
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 05a19d1..f0e0ab6 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -923,33 +923,99 @@
TEST(STDIO_TEST, sscanf_swscanf) {
struct stuff {
char s1[123];
- int i1;
+ int i1, i2;
+ char cs1[3];
+ char s2[3];
+ char c1;
double d1;
float f1;
- char s2[123];
+ char s3[123];
void Check() {
- ASSERT_STREQ("hello", s1);
- ASSERT_EQ(123, i1);
- ASSERT_DOUBLE_EQ(1.23, d1);
- ASSERT_FLOAT_EQ(9.0f, f1);
- ASSERT_STREQ("world", s2);
+ EXPECT_STREQ("hello", s1);
+ EXPECT_EQ(123, i1);
+ EXPECT_EQ(456, i2);
+ EXPECT_EQ('a', cs1[0]);
+ EXPECT_EQ('b', cs1[1]);
+ EXPECT_EQ('x', cs1[2]); // No terminating NUL.
+ EXPECT_STREQ("AB", s2); // Terminating NUL.
+ EXPECT_EQ('!', c1);
+ EXPECT_DOUBLE_EQ(1.23, d1);
+ EXPECT_FLOAT_EQ(9.0f, f1);
+ EXPECT_STREQ("world", s3);
}
} s;
- memset(&s, 0, sizeof(s));
- ASSERT_EQ(5, sscanf(" hello 123 1.23 0x1.2p3 world",
- "%s %i %lf %f %s",
- s.s1, &s.i1, &s.d1, &s.f1, s.s2));
+ memset(&s, 'x', sizeof(s));
+ ASSERT_EQ(9, sscanf(" hello 123 456abAB! 1.23 0x1.2p3 world",
+ "%s %i%i%2c%[A-Z]%c %lf %f %s",
+ s.s1, &s.i1, &s.i2, s.cs1, s.s2, &s.c1, &s.d1, &s.f1, s.s3));
s.Check();
- memset(&s, 0, sizeof(s));
- ASSERT_EQ(5, swscanf(L" hello 123 1.23 0x1.2p3 world",
- L"%s %i %lf %f %s",
- s.s1, &s.i1, &s.d1, &s.f1, s.s2));
+ memset(&s, 'x', sizeof(s));
+ ASSERT_EQ(9, swscanf(L" hello 123 456abAB! 1.23 0x1.2p3 world",
+ L"%s %i%i%2c%[A-Z]%c %lf %f %s",
+ s.s1, &s.i1, &s.i2, s.cs1, s.s2, &s.c1, &s.d1, &s.f1, s.s3));
s.Check();
}
+template <typename T>
+static void CheckScanf(int sscanf_fn(const T*, const T*, ...),
+ const T* input, const T* fmt,
+ int expected_count, const char* expected_string) {
+ char buf[256] = {};
+ ASSERT_EQ(expected_count, sscanf_fn(input, fmt, &buf)) << fmt;
+ ASSERT_STREQ(expected_string, buf) << fmt;
+}
+
+TEST(STDIO_TEST, sscanf_ccl) {
+ // `abc` is just those characters.
+ CheckScanf(sscanf, "abcd", "%[abc]", 1, "abc");
+ // `a-c` is the range 'a' .. 'c'.
+ CheckScanf(sscanf, "abcd", "%[a-c]", 1, "abc");
+ CheckScanf(sscanf, "-d", "%[a-c]", 0, "");
+ CheckScanf(sscanf, "ac-bAd", "%[a--c]", 1, "ac-bA");
+ // `a-c-e` is equivalent to `a-e`.
+ CheckScanf(sscanf, "abcdefg", "%[a-c-e]", 1, "abcde");
+ // `e-a` is equivalent to `ae-` (because 'e' > 'a').
+ CheckScanf(sscanf, "-a-e-b", "%[e-a]", 1, "-a-e-");
+ // An initial '^' negates the set.
+ CheckScanf(sscanf, "abcde", "%[^d]", 1, "abc");
+ CheckScanf(sscanf, "abcdefgh", "%[^c-d]", 1, "ab");
+ CheckScanf(sscanf, "hgfedcba", "%[^c-d]", 1, "hgfe");
+ // The first character may be ']' or '-' without being special.
+ CheckScanf(sscanf, "[[]]x", "%[][]", 1, "[[]]");
+ CheckScanf(sscanf, "-a-x", "%[-a]", 1, "-a-");
+ // The last character may be '-' without being special.
+ CheckScanf(sscanf, "-a-x", "%[a-]", 1, "-a-");
+ // X--Y is [X--] + Y, not [X--] + [--Y] (a bug in my initial implementation).
+ CheckScanf(sscanf, "+,-/.", "%[+--/]", 1, "+,-/");
+}
+
+TEST(STDIO_TEST, swscanf_ccl) {
+ // `abc` is just those characters.
+ CheckScanf(swscanf, L"abcd", L"%[abc]", 1, "abc");
+ // `a-c` is the range 'a' .. 'c'.
+ CheckScanf(swscanf, L"abcd", L"%[a-c]", 1, "abc");
+ CheckScanf(swscanf, L"-d", L"%[a-c]", 0, "");
+ CheckScanf(swscanf, L"ac-bAd", L"%[a--c]", 1, "ac-bA");
+ // `a-c-e` is equivalent to `a-e`.
+ CheckScanf(swscanf, L"abcdefg", L"%[a-c-e]", 1, "abcde");
+ // `e-a` is equivalent to `ae-` (because 'e' > 'a').
+ CheckScanf(swscanf, L"-a-e-b", L"%[e-a]", 1, "-a-e-");
+ // An initial '^' negates the set.
+ CheckScanf(swscanf, L"abcde", L"%[^d]", 1, "abc");
+ CheckScanf(swscanf, L"abcdefgh", L"%[^c-d]", 1, "ab");
+ CheckScanf(swscanf, L"hgfedcba", L"%[^c-d]", 1, "hgfe");
+ // The first character may be ']' or '-' without being special.
+ CheckScanf(swscanf, L"[[]]x", L"%[][]", 1, "[[]]");
+ CheckScanf(swscanf, L"-a-x", L"%[-a]", 1, "-a-");
+ // The last character may be '-' without being special.
+ CheckScanf(swscanf, L"-a-x", L"%[a-]", 1, "-a-");
+ // X--Y is [X--] + Y, not [X--] + [--Y] (a bug in my initial implementation).
+ CheckScanf(swscanf, L"+,-/.", L"%[+--/]", 1, "+,-/");
+}
+
TEST(STDIO_TEST, cantwrite_EBADF) {
// If we open a file read-only...
FILE* fp = fopen("/proc/version", "r");
diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp
index 0c40380..ed5767c 100644
--- a/tests/stdlib_test.cpp
+++ b/tests/stdlib_test.cpp
@@ -138,6 +138,47 @@
EXPECT_EQ(795539493, mrand48());
}
+TEST(stdlib, jrand48_distribution) {
+ const int iterations = 4096;
+ const int pivot_low = 1536;
+ const int pivot_high = 2560;
+
+ unsigned short xsubi[3];
+ int bits[32] = {};
+
+ for (int iter = 0; iter < iterations; ++iter) {
+ long rand_val = jrand48(xsubi);
+ for (int bit = 0; bit < 32; ++bit) {
+ bits[bit] += (static_cast<unsigned long>(rand_val) >> bit) & 0x01;
+ }
+ }
+
+ // Check that bit probability is uniform
+ for (int bit = 0; bit < 32; ++bit) {
+ EXPECT_TRUE((pivot_low <= bits[bit]) && (bits[bit] <= pivot_high));
+ }
+}
+
+TEST(stdlib, mrand48_distribution) {
+ const int iterations = 4096;
+ const int pivot_low = 1536;
+ const int pivot_high = 2560;
+
+ int bits[32] = {};
+
+ for (int iter = 0; iter < iterations; ++iter) {
+ long rand_val = mrand48();
+ for (int bit = 0; bit < 32; ++bit) {
+ bits[bit] += (static_cast<unsigned long>(rand_val) >> bit) & 0x01;
+ }
+ }
+
+ // Check that bit probability is uniform
+ for (int bit = 0; bit < 32; ++bit) {
+ EXPECT_TRUE((pivot_low <= bits[bit]) && (bits[bit] <= pivot_high));
+ }
+}
+
TEST(stdlib, posix_memalign_sweep) {
void* ptr;
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index e1fae92..7a96760 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -43,6 +43,19 @@
ASSERT_EQ(1970, broken_down->tm_year + 1900);
}
+TEST(time, gmtime_r) {
+ struct tm tm = {};
+ time_t t = 0;
+ struct tm* broken_down = gmtime_r(&t, &tm);
+ ASSERT_EQ(broken_down, &tm);
+ ASSERT_EQ(0, broken_down->tm_sec);
+ ASSERT_EQ(0, broken_down->tm_min);
+ ASSERT_EQ(0, broken_down->tm_hour);
+ ASSERT_EQ(1, broken_down->tm_mday);
+ ASSERT_EQ(0, broken_down->tm_mon);
+ ASSERT_EQ(1970, broken_down->tm_year + 1900);
+}
+
static void* gmtime_no_stack_overflow_14313703_fn(void*) {
const char* original_tz = getenv("TZ");
// Ensure we'll actually have to enter tzload by using a time zone that doesn't exist.
@@ -790,3 +803,29 @@
// The BSDs agree with us, but glibc gets this wrong.
#endif
}
+
+TEST(time, asctime) {
+ const struct tm tm = {};
+ ASSERT_STREQ("Sun Jan 0 00:00:00 1900\n", asctime(&tm));
+}
+
+TEST(time, asctime_r) {
+ const struct tm tm = {};
+ char buf[256];
+ ASSERT_EQ(buf, asctime_r(&tm, buf));
+ ASSERT_STREQ("Sun Jan 0 00:00:00 1900\n", buf);
+}
+
+TEST(time, ctime) {
+ setenv("TZ", "UTC", 1);
+ const time_t t = 0;
+ ASSERT_STREQ("Thu Jan 1 00:00:00 1970\n", ctime(&t));
+}
+
+TEST(time, ctime_r) {
+ setenv("TZ", "UTC", 1);
+ const time_t t = 0;
+ char buf[256];
+ ASSERT_EQ(buf, ctime_r(&t, buf));
+ ASSERT_STREQ("Thu Jan 1 00:00:00 1970\n", buf);
+}