Merge "Add a few missing <time.h> tests."
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/android-changes-for-ndk-developers.md b/android-changes-for-ndk-developers.md
index 0620d9e..48800ca 100644
--- a/android-changes-for-ndk-developers.md
+++ b/android-changes-for-ndk-developers.md
@@ -360,23 +360,30 @@
## Enable logging of dlopen/dlsym and library loading errors for apps (Available in Android O)
-Starting with Android O it is possible to enable logging of all dlsym/dlopen calls
-for debuggable apps. Here is short instruction on how to do that:
+Starting with Android O it is possible to enable logging of dynamic
+linker activity for debuggable apps by setting a property corresponding
+to the fully-qualified name of the specific app:
```
-adb shell setprop debug.ld.app.com.example.myapp dlsym,dlopen,dlerror
+adb shell setprop debug.ld.app.com.example.myapp dlerror,dlopen,dlsym
adb logcat
```
-Any subset of (dlsym,dlopen,dlerror) can be used.
+Any combination of `dlerror`, `dlopen`, and `dlsym` can be used. There's
+no separate `dlclose` option: `dlopen` covers both loading and unloading
+of libraries. Note also that `dlerror` doesn't correspond to actual
+calls of dlerror(3) but to any time the dynamic linker writes to its
+internal error buffer, so you'll see any errors the dynamic linker would
+have reported, even if the code you're debugging doesn't actually call
+dlerror(3) itself.
-On userdebug and eng builds it is possible to enable tracing for the whole system
-by using debug.ld.all system property instead of app-specific one:
+On userdebug and eng builds it is possible to enable tracing for the
+whole system by using the `debug.ld.all` system property instead of
+app-specific one. For example, to enable logging of all dlopen(3)
+(and thus dclose(3)) calls, and all failures, but not dlsym(3) calls:
```
adb shell setprop debug.ld.all dlerror,dlopen
```
-enables logging of all errors and dlopen calls
-
## dlclose interacts badly with thread local variables with non-trivial destructors
Android allows `dlclose` to unload a library even if there are still
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..3020c27 100644
--- a/benchmarks/tests/interface_test.cpp
+++ b/benchmarks/tests/interface_test.cpp
@@ -435,7 +435,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/arch-arm/syscalls/clock_getres.S b/libc/arch-arm/syscalls/__clock_getres.S
similarity index 82%
rename from libc/arch-arm/syscalls/clock_getres.S
rename to libc/arch-arm/syscalls/__clock_getres.S
index e101127..a81cf00 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,5 @@
bxls lr
neg r0, r0
b __set_errno_internal
-END(clock_getres)
+END(__clock_getres)
+.hidden __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 81%
rename from libc/arch-mips/syscalls/clock_getres.S
rename to libc/arch-mips/syscalls/__clock_getres.S
index 153f629..c5eed97 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,5 @@
j $t9
nop
.set reorder
-END(clock_getres)
+END(__clock_getres)
+.hidden __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 90%
rename from libc/arch-x86/syscalls/clock_getres.S
rename to libc/arch-x86/syscalls/__clock_getres.S
index 9466e08..b8c5374 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,5 @@
popl %ecx
popl %ebx
ret
-END(clock_getres)
+END(__clock_getres)
+.hidden __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/include/elf.h b/libc/include/elf.h
index 5c2503a..37450b2 100644
--- a/libc/include/elf.h
+++ b/libc/include/elf.h
@@ -41,6 +41,7 @@
#include <linux/elf-em.h>
/* http://www.sco.com/developers/gabi/latest/ch4.intro.html */
+typedef __u64 Elf32_Xword;
typedef __s64 Elf32_Sxword;
typedef struct {
@@ -70,9 +71,64 @@
Elf64_Xword ch_addralign;
} Elf64_Chdr;
+typedef struct {
+ Elf32_Word l_name;
+ Elf32_Word l_time_stamp;
+ Elf32_Word l_checksum;
+ Elf32_Word l_version;
+ Elf32_Word l_flags;
+} Elf32_Lib;
+typedef struct {
+ Elf64_Word l_name;
+ Elf64_Word l_time_stamp;
+ Elf64_Word l_checksum;
+ Elf64_Word l_version;
+ Elf64_Word l_flags;
+} Elf64_Lib;
+/* ElfW(Lib)::l_flags values. */
+#define LL_NONE 0x0
+#define LL_EXACT_MATCH 0x1
+#define LL_IGNORE_INT_VER 0x2
+#define LL_REQUIRE_MINOR 0x4
+#define LL_EXPORTS 0x8
+#define LL_DELAY_LOAD 0x10
+#define LL_DELTA 0x20
+
+typedef struct {
+ Elf32_Xword m_value;
+ Elf32_Word m_info;
+ Elf32_Word m_poffset;
+ Elf32_Half m_repeat;
+ Elf32_Half m_stride;
+} Elf32_Move;
+typedef struct {
+ Elf64_Xword m_value;
+ Elf64_Xword m_info;
+ Elf64_Xword m_poffset;
+ Elf64_Half m_repeat;
+ Elf64_Half m_stride;
+} Elf64_Move;
+
typedef __u16 Elf32_Section;
typedef __u16 Elf64_Section;
+typedef struct {
+ Elf32_Half si_boundto;
+ Elf32_Half si_flags;
+} Elf32_Syminfo;
+typedef struct {
+ Elf64_Half si_boundto;
+ Elf64_Half si_flags;
+} Elf64_Syminfo;
+/* ElfW(Syminfo)::si_boundto values. */
+#define SYMINFO_BT_SELF 0xffff
+#define SYMINFO_BT_PARENT 0xfffe
+/* ElfW(Syminfo)::si_flags values. */
+#define SYMINFO_FLG_DIRECT 0x1
+#define SYMINFO_FLG_PASSTHRU 0x2
+#define SYMINFO_FLG_COPY 0x4
+#define SYMINFO_FLG_LAZYLOAD 0x8
+
typedef Elf32_Half Elf32_Versym;
typedef Elf64_Half Elf64_Versym;
diff --git a/libc/include/paths.h b/libc/include/paths.h
index d2c5956..922d1ce 100644
--- a/libc/include/paths.h
+++ b/libc/include/paths.h
@@ -38,7 +38,7 @@
#define _PATH_BSHELL "/system/bin/sh"
#endif
#define _PATH_CONSOLE "/dev/console"
-#define _PATH_DEFPATH "/sbin:/system/sbin:/system/bin:/system/xbin:/vendor/bin:/vendor/xbin"
+#define _PATH_DEFPATH "/sbin:/system/sbin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin"
#define _PATH_DEV "/dev/"
#define _PATH_DEVNULL "/dev/null"
#define _PATH_KLOG "/proc/kmsg"
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 66ae191..abfd00f 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -90,13 +90,17 @@
#if defined(__LP64__)
static const char* const kSystemLibDir = "/system/lib64";
+static const char* const kOdmLibDir = "/odm/lib64";
static const char* const kVendorLibDir = "/vendor/lib64";
static const char* const kAsanSystemLibDir = "/data/asan/system/lib64";
+static const char* const kAsanOdmLibDir = "/data/asan/odm/lib64";
static const char* const kAsanVendorLibDir = "/data/asan/vendor/lib64";
#else
static const char* const kSystemLibDir = "/system/lib";
+static const char* const kOdmLibDir = "/odm/lib";
static const char* const kVendorLibDir = "/vendor/lib";
static const char* const kAsanSystemLibDir = "/data/asan/system/lib";
+static const char* const kAsanOdmLibDir = "/data/asan/odm/lib";
static const char* const kAsanVendorLibDir = "/data/asan/vendor/lib";
#endif
@@ -104,6 +108,7 @@
static const char* const kDefaultLdPaths[] = {
kSystemLibDir,
+ kOdmLibDir,
kVendorLibDir,
nullptr
};
@@ -111,6 +116,8 @@
static const char* const kAsanDefaultLdPaths[] = {
kAsanSystemLibDir,
kSystemLibDir,
+ kAsanOdmLibDir,
+ kOdmLibDir,
kAsanVendorLibDir,
kVendorLibDir,
nullptr
@@ -1156,6 +1163,27 @@
return *candidate != nullptr;
}
+static bool find_loaded_library_by_realpath(android_namespace_t* ns, const char* realpath,
+ bool search_linked_namespaces, soinfo** candidate) {
+ auto predicate = [&](soinfo* si) { return strcmp(realpath, si->get_realpath()) == 0; };
+
+ *candidate = ns->soinfo_list().find_if(predicate);
+
+ if (*candidate == nullptr && search_linked_namespaces) {
+ for (auto& link : ns->linked_namespaces()) {
+ android_namespace_t* linked_ns = link.linked_namespace();
+ soinfo* si = linked_ns->soinfo_list().find_if(predicate);
+
+ if (si != nullptr && link.is_accessible(si->get_soname())) {
+ *candidate = si;
+ return true;
+ }
+ }
+ }
+
+ return *candidate != nullptr;
+}
+
static bool load_library(android_namespace_t* ns,
LoadTask* task,
LoadTaskList* load_tasks,
@@ -2008,12 +2036,18 @@
const char* translated_name = name;
if (g_is_asan && translated_name != nullptr && translated_name[0] == '/') {
- char translated_path[PATH_MAX];
- if (realpath(translated_name, translated_path) != nullptr) {
- asan_name_holder = std::string(kAsanLibDirPrefix) + translated_path;
+ char original_path[PATH_MAX];
+ if (realpath(name, original_path) != nullptr) {
+ asan_name_holder = std::string(kAsanLibDirPrefix) + original_path;
if (file_exists(asan_name_holder.c_str())) {
- translated_name = asan_name_holder.c_str();
- PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
+ soinfo* si = nullptr;
+ if (find_loaded_library_by_realpath(ns, original_path, true, &si)) {
+ PRINT("linker_asan dlopen NOT translating \"%s\" -> \"%s\": library already loaded", name,
+ asan_name_holder.c_str());
+ } else {
+ PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
+ translated_name = asan_name_holder.c_str();
+ }
}
}
}
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 3f6da59..249d341 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -1270,29 +1270,49 @@
std::string shared_libs = g_core_shared_libs + ":" + g_public_lib;
+ // create a parent namespace to use instead of the default namespace. This is
+ // to make this test be independent from the configuration of the default
+ // namespace.
+ android_namespace_t* ns_parent =
+ android_create_namespace("parent",
+ nullptr,
+ nullptr,
+ ANDROID_NAMESPACE_TYPE_REGULAR,
+ nullptr,
+ nullptr);
+ ASSERT_TRUE(ns_parent != nullptr) << dlerror();
+ ASSERT_TRUE(android_link_namespaces(ns_parent, nullptr, g_core_shared_libs.c_str())) << dlerror();
+
+ android_dlextinfo extinfo;
+ extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
+ extinfo.library_namespace = ns_parent;
+
const std::string lib_public_path = get_testlib_root() + "/public_namespace_libs/" + g_public_lib;
- void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
+ void* handle_public = android_dlopen_ext(lib_public_path.c_str(), RTLD_NOW, &extinfo);
ASSERT_TRUE(handle_public != nullptr) << dlerror();
android_set_application_target_sdk_version(42U); // something > 23
ASSERT_TRUE(android_init_anonymous_namespace(shared_libs.c_str(), nullptr)) << dlerror();
- // preload this library to the default namespace to check if it
+ // preload this library to the parent namespace to check if it
// is shared later on.
void* handle_dlopened =
- dlopen((get_testlib_root() + "/private_namespace_libs/libnstest_dlopened.so").c_str(), RTLD_NOW);
+ android_dlopen_ext((get_testlib_root() + "/private_namespace_libs/libnstest_dlopened.so").c_str(), RTLD_NOW, &extinfo);
ASSERT_TRUE(handle_dlopened != nullptr) << dlerror();
+ // create two child namespaces of 'ns_parent'. One with regular, the other
+ // with isolated & shared.
android_namespace_t* ns_not_isolated =
android_create_namespace("private",
nullptr,
(get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_REGULAR,
nullptr,
- nullptr);
+ ns_parent);
ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror();
- ASSERT_TRUE(android_link_namespaces(ns_not_isolated, nullptr, shared_libs.c_str())) << dlerror();
+ ASSERT_TRUE(android_link_namespaces(ns_not_isolated, ns_parent, g_public_lib)) << dlerror();
+ ASSERT_TRUE(android_link_namespaces(ns_not_isolated, nullptr, g_core_shared_libs.c_str())) << dlerror();
android_namespace_t* ns_isolated_shared =
android_create_namespace("private_isolated_shared",
@@ -1300,23 +1320,22 @@
(get_testlib_root() + "/private_namespace_libs").c_str(),
ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED,
nullptr,
- nullptr);
+ ns_parent);
ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror();
- ASSERT_TRUE(android_link_namespaces(ns_isolated_shared, nullptr, shared_libs.c_str())) << dlerror();
+ ASSERT_TRUE(android_link_namespaces(ns_isolated_shared, ns_parent, g_public_lib)) << dlerror();
+ ASSERT_TRUE(android_link_namespaces(ns_isolated_shared, nullptr, g_core_shared_libs.c_str())) << dlerror();
- ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr);
+ ASSERT_TRUE(android_dlopen_ext(root_lib, RTLD_NOW, &extinfo) == nullptr);
ASSERT_STREQ("dlopen failed: library \"libnstest_root_not_isolated.so\" not found", dlerror());
std::string lib_private_external_path =
get_testlib_root() + "/private_namespace_libs_external/libnstest_private_external.so";
- // Load lib_private_external_path to default namespace
+ // Load lib_private_external_path to the parent namespace
// (it should remain invisible for the isolated namespaces after this)
- void* handle = dlopen(lib_private_external_path.c_str(), RTLD_NOW);
+ void* handle = android_dlopen_ext(lib_private_external_path.c_str(), RTLD_NOW, &extinfo);
ASSERT_TRUE(handle != nullptr) << dlerror();
- android_dlextinfo extinfo;
- extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
extinfo.library_namespace = ns_not_isolated;
void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo);
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/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;