Merge "Initialize main thread TLS before the global stack guard."
diff --git a/libc/Android.bp b/libc/Android.bp
index 4fcd83e..f61cc46 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -15,9 +15,12 @@
     "bionic/sigsetmask.c",
     "bionic/system_properties_compat.c",
     "stdio/fread.c",
+    "stdio/parsefloat.c",
     "stdio/refill.c",
     "stdio/stdio.cpp",
     "stdio/stdio_ext.cpp",
+    "stdio/vfscanf.c",
+    "stdio/vfwscanf.c",
     "stdlib/atexit.c",
     "stdlib/exit.c",
 ]
@@ -397,11 +400,8 @@
         "upstream-openbsd/lib/libc/locale/mbstowcs.c",
         "upstream-openbsd/lib/libc/locale/mbtowc.c",
         "upstream-openbsd/lib/libc/locale/wcscoll.c",
-        "upstream-openbsd/lib/libc/locale/wcstod.c",
-        "upstream-openbsd/lib/libc/locale/wcstof.c",
         "upstream-openbsd/lib/libc/locale/wcstoimax.c",
         "upstream-openbsd/lib/libc/locale/wcstol.c",
-        "upstream-openbsd/lib/libc/locale/wcstold.c",
         "upstream-openbsd/lib/libc/locale/wcstoll.c",
         "upstream-openbsd/lib/libc/locale/wcstombs.c",
         "upstream-openbsd/lib/libc/locale/wcstoul.c",
@@ -453,7 +453,6 @@
         "upstream-openbsd/lib/libc/stdio/ungetwc.c",
         "upstream-openbsd/lib/libc/stdio/vasprintf.c",
         "upstream-openbsd/lib/libc/stdio/vdprintf.c",
-        "upstream-openbsd/lib/libc/stdio/vfscanf.c",
         "upstream-openbsd/lib/libc/stdio/vsscanf.c",
         "upstream-openbsd/lib/libc/stdio/vswprintf.c",
         "upstream-openbsd/lib/libc/stdio/vswscanf.c",
@@ -520,7 +519,6 @@
     srcs: [
         "upstream-openbsd/lib/libc/stdio/vfprintf.c",
         "upstream-openbsd/lib/libc/stdio/vfwprintf.c",
-        "upstream-openbsd/lib/libc/stdio/vfwscanf.c",
     ],
     cflags: [
         "-include openbsd-compat.h",
@@ -1365,6 +1363,7 @@
         "bionic/unlink.cpp",
         "bionic/wait.cpp",
         "bionic/wchar.cpp",
+        "bionic/wcstod.cpp",
         "bionic/wctype.cpp",
         "bionic/wmempcpy.cpp",
     ],
@@ -1990,4 +1989,125 @@
     defaults: ["crt_defaults"],
 }
 
+// The following module lives in prebuilts/ndk because we need to preprocess the
+// headers to include ifdef guards for __ANDROID_API__. Update with
+// bionic/tools/update_headers.sh.
+// ndk_headers {
+//     name: "common_libc",
+//     from: "include",
+//     to: "",
+//     srcs: ["include/**/*.h"],
+// }
+
+ndk_headers {
+    name: "libc_linux",
+    from: "kernel/uapi/linux",
+    to: "linux",
+    srcs: ["kernel/uapi/linux/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_android",
+    from: "kernel/android/uapi/linux",
+    to: "linux",
+    srcs: ["kernel/android/uapi/linux/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_asm_generic",
+    from: "kernel/uapi/asm-generic",
+    to: "asm-generic",
+    srcs: ["kernel/uapi/asm-generic/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_asm_arm",
+    from: "kernel/uapi/asm-arm",
+    to: "arm-linux-androideabi",
+    srcs: ["kernel/uapi/asm-arm/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_asm_arm64",
+    from: "kernel/uapi/asm-arm64",
+    to: "aarch64-linux-android",
+    srcs: ["kernel/uapi/asm-arm64/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_asm_mips",
+    from: "kernel/uapi/asm-mips",
+    to: "mipsel-linux-android",
+    srcs: ["kernel/uapi/asm-mips/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_asm_mips64",
+    from: "kernel/uapi/asm-mips",
+    to: "mips64el-linux-android",
+    srcs: ["kernel/uapi/asm-mips/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_asm_x86",
+    from: "kernel/uapi/asm-x86",
+    to: "i686-linux-android",
+    srcs: ["kernel/uapi/asm-x86/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_asm_x86_64",
+    from: "kernel/uapi/asm-x86",
+    to: "x86_64-linux-android",
+    srcs: ["kernel/uapi/asm-x86/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_machine_arm",
+    from: "arch-arm/include",
+    to: "arm-linux-androideabi",
+    srcs: ["arch-arm/include/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_machine_arm64",
+    from: "arch-arm64/include",
+    to: "aarch64-linux-android",
+    srcs: ["arch-arm64/include/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_machine_mips",
+    from: "arch-mips/include",
+    to: "mipsel-linux-android",
+    srcs: ["arch-mips/include/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_machine_mips64",
+    from: "arch-mips/include",
+    to: "mips64el-linux-android",
+    srcs: ["arch-mips/include/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_machine_x86",
+    from: "arch-x86/include",
+    to: "i686-linux-android",
+    srcs: ["arch-x86/include/**/*.h"],
+}
+
+ndk_headers {
+    name: "libc_machine_x86_64",
+    from: "arch-x86_64/include",
+    to: "x86_64-linux-android",
+    srcs: ["arch-x86_64/include/**/*.h"],
+}
+
+ndk_library {
+    name: "libc.ndk",
+    symbol_file: "libc.map.txt",
+    first_version: "9",
+}
+
 subdirs = ["malloc_debug"]
diff --git a/libc/arch-arm/bionic/__bionic_clone.S b/libc/arch-arm/bionic/__bionic_clone.S
index a85ea34..8836748 100644
--- a/libc/arch-arm/bionic/__bionic_clone.S
+++ b/libc/arch-arm/bionic/__bionic_clone.S
@@ -51,7 +51,7 @@
 
     # Are we the child?
     movs    r0, r0
-    beq     1f
+    beq     .L_bc_child
 
     # In the parent, reload saved registers then either return or set errno.
     ldmfd   sp!, {r4, r5, r6, r7}
@@ -60,9 +60,9 @@
     neg     r0, r0
     b       __set_errno_internal
 
-    # The child.
-    # Setting lr to 0 will make the unwinder stop at __start_thread
-1:  mov    lr, #0
+.L_bc_child:
+    # Setting lr to 0 will make the unwinder stop at __start_thread.
+    mov    lr, #0
     # Call __start_thread with the 'fn' and 'arg' we stored on the child stack.
     pop    {r0, r1}
     b      __start_thread
diff --git a/libc/bionic/sys_msg.cpp b/libc/bionic/sys_msg.cpp
index 462c83b..4880356 100644
--- a/libc/bionic/sys_msg.cpp
+++ b/libc/bionic/sys_msg.cpp
@@ -32,8 +32,9 @@
 #include <unistd.h>
 
 int msgctl(int id, int cmd, msqid_ds* buf) {
-#if !defined(__LP64__)
+#if !defined(__LP64__) || defined(__mips__)
   // Annoyingly, the kernel requires this for 32-bit but rejects it for 64-bit.
+  // Mips64 is an exception to this, it requires the flag.
   cmd |= IPC_64;
 #endif
 #if defined(SYS_msgctl)
diff --git a/libc/bionic/sys_sem.cpp b/libc/bionic/sys_sem.cpp
index 058cfef..5e2a05c 100644
--- a/libc/bionic/sys_sem.cpp
+++ b/libc/bionic/sys_sem.cpp
@@ -33,8 +33,9 @@
 #include <unistd.h>
 
 int semctl(int id, int num, int cmd, ...) {
-#if !defined(__LP64__)
+#if !defined(__LP64__) || defined(__mips__)
   // Annoyingly, the kernel requires this for 32-bit but rejects it for 64-bit.
+  // Mips64 is an exception to this, it requires the flag.
   cmd |= IPC_64;
 #endif
   va_list ap;
diff --git a/libc/bionic/sys_shm.cpp b/libc/bionic/sys_shm.cpp
index f780e04..f3b26e7 100644
--- a/libc/bionic/sys_shm.cpp
+++ b/libc/bionic/sys_shm.cpp
@@ -45,8 +45,9 @@
 }
 
 int shmctl(int id, int cmd, struct shmid_ds* buf) {
-#if !defined(__LP64__)
+#if !defined(__LP64__) || defined(__mips__)
   // Annoyingly, the kernel requires this for 32-bit but rejects it for 64-bit.
+  // Mips64 is an exception to this, it requires the flag.
   cmd |= IPC_64;
 #endif
 #if defined(SYS_shmctl)
diff --git a/libc/bionic/wcstod.cpp b/libc/bionic/wcstod.cpp
new file mode 100644
index 0000000..eb66ba0
--- /dev/null
+++ b/libc/bionic/wcstod.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 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 <wchar.h>
+
+#include <stdlib.h>
+
+#include "local.h"
+
+template <typename float_type> float_type wcstod(const wchar_t* str, wchar_t** end,
+                                                 float_type strtod_fn(const char*, char**)) {
+  // What's the longest span of the input that might be part of the float?
+  size_t max_len = wcsspn(str, L"-+0123456789.xXeEpP()nNaAiIfFtTyY");
+
+  // We know the only valid characters are ASCII, so convert them by brute force.
+  char* ascii_str = new char[max_len + 1];
+  if (!ascii_str) return float_type();
+  for (size_t i = 0; i < max_len; ++i) {
+    ascii_str[i] = str[i] & 0xff;
+  }
+  ascii_str[max_len] = 0;
+
+  // Set up a fake FILE that points to those ASCII characters, for `parsefloat`.
+  FILE f;
+  __sfileext fext;
+  _FILEEXT_SETUP(&f, &fext);
+  f._flags = __SRD;
+  f._bf._base = f._p = reinterpret_cast<unsigned char*>(ascii_str);
+  f._bf._size = f._r = max_len;
+  f._read = [](void*, char*, int) { return 0; }; // aka `eofread`, aka "no more data".
+  f._lb._base = NULL;
+
+  // Ask `parsefloat` to look at the same data more carefully.
+
+  // We can't just do this straight away because we can't construct a suitable FILE*
+  // in the absence of any `fwmemopen` analogous to `fmemopen`. And we don't want to
+  // duplicate the `parsefloat` logic. We also don't want to actually have to have wchar_t
+  // implementations of the ASCII `strtod` logic (though if you were designing a libc
+  // from scratch, you'd probably want to just make that more generic and lose all the
+  // cruft on top).
+  size_t actual_len = parsefloat(&f, ascii_str, ascii_str + max_len);
+
+  // Finally let the ASCII conversion function do the work.
+  char* ascii_end;
+  float_type result = strtod_fn(ascii_str, &ascii_end);
+  if (ascii_end != ascii_str + actual_len) abort();
+
+  if (end) *end = const_cast<wchar_t*>(str) + actual_len;
+
+  delete[] ascii_str;
+  return result;
+}
+
+float wcstof(const wchar_t* s, wchar_t** end) {
+  return wcstod<float>(s, end, strtof);
+}
+
+double wcstod(const wchar_t* s, wchar_t** end) {
+  return wcstod<double>(s, end, strtod);
+}
+
+long double wcstold(const wchar_t* s, wchar_t** end) {
+  return wcstod<long double>(s, end, strtold);
+}
diff --git a/libc/include/android/versioning.h b/libc/include/android/versioning.h
index 3686261..196c28e 100644
--- a/libc/include/android/versioning.h
+++ b/libc/include/android/versioning.h
@@ -27,4 +27,6 @@
 #define __INTRODUCED_IN_X86(api_level) __attribute__((annotate("introduced_in_x86=" #api_level)))
 #define __INTRODUCED_IN_MIPS(api_level) __attribute__((annotate("introduced_in_mips=" #api_level)))
 
+#define __VERSIONER_NO_GUARD __attribute__((annotate("versioner_no_guard")))
+
 #endif /* ANDROID_VERSIONING_H */
diff --git a/libc/include/bits/wctype.h b/libc/include/bits/wctype.h
index b98e500..c968d71 100644
--- a/libc/include/bits/wctype.h
+++ b/libc/include/bits/wctype.h
@@ -58,8 +58,8 @@
 int iswctype(wint_t, wctype_t);
 
 typedef const void* wctrans_t;
-wint_t towctrans(wint_t, wctrans_t) __INTRODUCED_IN_FUTURE;
-wctrans_t wctrans(const char*) __INTRODUCED_IN_FUTURE;
+wint_t towctrans(wint_t, wctrans_t) __INTRODUCED_IN_FUTURE __VERSIONER_NO_GUARD;
+wctrans_t wctrans(const char*) __INTRODUCED_IN_FUTURE __VERSIONER_NO_GUARD;
 
 __END_DECLS
 
diff --git a/libc/include/locale.h b/libc/include/locale.h
index a681a17..c1b6299 100644
--- a/libc/include/locale.h
+++ b/libc/include/locale.h
@@ -96,7 +96,7 @@
   char int_n_sign_posn;
 };
 
-struct lconv* localeconv(void) __INTRODUCED_IN(21);
+struct lconv* localeconv(void) __INTRODUCED_IN(21) __VERSIONER_NO_GUARD;
 
 locale_t duplocale(locale_t) __INTRODUCED_IN(21);
 void freelocale(locale_t) __INTRODUCED_IN(21);
diff --git a/libc/include/math.h b/libc/include/math.h
index 37aa2cc..4437b81 100644
--- a/libc/include/math.h
+++ b/libc/include/math.h
@@ -169,7 +169,7 @@
 double	fmin(double, double) __pure2;
 double	nearbyint(double);
 double	round(double);
-double scalbln(double, long) __INTRODUCED_IN_X86(18);
+double scalbln(double, long) __INTRODUCED_IN_X86(18) __VERSIONER_NO_GUARD;
 double scalbn(double, int);
 double	tgamma(double);
 double	trunc(double);
@@ -230,7 +230,7 @@
 float	remainderf(float, float);
 float	remquof(float, float, int *);
 float	rintf(float);
-float	scalblnf(float, long) __INTRODUCED_IN_X86(18);
+float	scalblnf(float, long) __INTRODUCED_IN_X86(18) __VERSIONER_NO_GUARD;
 float scalbnf(float, int);
 float	truncf(float);
 
@@ -259,11 +259,12 @@
 long double fabsl(long double) __pure2;
 long double fdiml(long double, long double);
 long double floorl(long double);
-long double fmal(long double, long double, long double) __INTRODUCED_IN(21);
+long double fmal(long double, long double, long double) __INTRODUCED_IN(21) __VERSIONER_NO_GUARD;
 long double fmaxl(long double, long double) __pure2;
 long double fminl(long double, long double) __pure2;
 long double fmodl(long double, long double) __INTRODUCED_IN(21);
-long double frexpl(long double value, int*) __INTRODUCED_IN(21); /* fundamentally !__pure2 */
+long double frexpl(long double value, int*)
+    __INTRODUCED_IN(21) __VERSIONER_NO_GUARD; /* fundamentally !__pure2 */
 long double hypotl(long double, long double) __INTRODUCED_IN(21);
 int ilogbl(long double) __pure2;
 long double ldexpl(long double, int);
@@ -280,16 +281,16 @@
 long double modfl(long double, long double*) __INTRODUCED_IN(21); /* fundamentally !__pure2 */
 long double nanl(const char*) __pure2 __INTRODUCED_IN(13);
 long double nearbyintl(long double) __INTRODUCED_IN(21);
-long double nextafterl(long double, long double) __INTRODUCED_IN(21);
-double nexttoward(double, long double) __INTRODUCED_IN(18);
+long double nextafterl(long double, long double) __INTRODUCED_IN(21) __VERSIONER_NO_GUARD;
+double nexttoward(double, long double) __INTRODUCED_IN(18) __VERSIONER_NO_GUARD;
 float nexttowardf(float, long double);
-long double nexttowardl(long double, long double) __INTRODUCED_IN(18);
+long double nexttowardl(long double, long double) __INTRODUCED_IN(18) __VERSIONER_NO_GUARD;
 long double powl(long double, long double) __INTRODUCED_IN(21);
 long double remainderl(long double, long double) __INTRODUCED_IN(21);
 long double remquol(long double, long double, int*) __INTRODUCED_IN(21);
 long double rintl(long double) __INTRODUCED_IN(21);
 long double roundl(long double);
-long double scalblnl(long double, long) __INTRODUCED_IN_X86(18);
+long double scalblnl(long double, long) __INTRODUCED_IN_X86(18) __VERSIONER_NO_GUARD;
 long double scalbnl(long double, int);
 long double sinhl(long double) __INTRODUCED_IN(21);
 long double sinl(long double) __INTRODUCED_IN(21);
diff --git a/libc/include/netinet/in.h b/libc/include/netinet/in.h
index 00b5cf9..3574976 100644
--- a/libc/include/netinet/in.h
+++ b/libc/include/netinet/in.h
@@ -47,8 +47,13 @@
 
 int bindresvport(int, struct sockaddr_in*);
 
+#if __ANDROID_API__ >= 24
 extern const struct in6_addr in6addr_any __INTRODUCED_IN(24);
 extern const struct in6_addr in6addr_loopback __INTRODUCED_IN(24);
+#else
+static const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
+static const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+#endif /* __ANDROID_API__ >= 24 */
 
 __END_DECLS
 
diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h
index 28cd4a8..6b0ec12 100644
--- a/libc/include/stdlib.h
+++ b/libc/include/stdlib.h
@@ -147,14 +147,23 @@
 const char* getprogname(void) __INTRODUCED_IN(21);
 void setprogname(const char*) __INTRODUCED_IN(21);
 
-int mblen(const char*, size_t) __INTRODUCED_IN_FUTURE;
+int mblen(const char*, size_t) __INTRODUCED_IN_FUTURE __VERSIONER_NO_GUARD;
 size_t mbstowcs(wchar_t*, const char*, size_t);
-int mbtowc(wchar_t*, const char*, size_t) __INTRODUCED_IN(21);
-int wctomb(char*, wchar_t) __INTRODUCED_IN(21);
+int mbtowc(wchar_t*, const char*, size_t) __INTRODUCED_IN(21) __VERSIONER_NO_GUARD;
+int wctomb(char*, wchar_t) __INTRODUCED_IN(21) __VERSIONER_NO_GUARD;
+
 size_t wcstombs(char*, const wchar_t*, size_t);
 
+#if __ANDROID_API__ >= 21
 size_t __ctype_get_mb_cur_max(void) __INTRODUCED_IN(21);
 #define MB_CUR_MAX __ctype_get_mb_cur_max()
+#else
+/*
+ * 4 is only true for UTF-8 locales, but that's what we default to. We'll need
+ * the NDK compatibility library to fix this properly.
+ */
+#define MB_CUR_MAX 4
+#endif
 
 #if defined(__BIONIC_FORTIFY)
 
diff --git a/libc/include/string.h b/libc/include/string.h
index 18b12bd..4a83c4e 100644
--- a/libc/include/string.h
+++ b/libc/include/string.h
@@ -239,9 +239,7 @@
 
     return __stpncpy_chk2(dst, src, n, bos_dst, bos_src);
 }
-#endif /* __ANDROID_API__ >= 21 */
 
-#if __ANDROID_API__ >= 17
 __BIONIC_FORTIFY_INLINE
 char* strncpy(char* _Nonnull __restrict dst, const char* _Nonnull __restrict src, size_t n) {
     size_t bos_dst = __bos(dst);
@@ -262,7 +260,9 @@
 
     return __strncpy_chk2(dst, src, n, bos_dst, bos_src);
 }
+#endif /* __ANDROID_API__ >= 21 */
 
+#if __ANDROID_API__ >= 17
 __BIONIC_FORTIFY_INLINE
 char* strcat(char* _Nonnull __restrict dst, const char* _Nonnull __restrict src) {
     return __builtin___strcat_chk(dst, src, __bos(dst));
diff --git a/libc/include/sys/select.h b/libc/include/sys/select.h
index 0f454b0..5a8a81d 100644
--- a/libc/include/sys/select.h
+++ b/libc/include/sys/select.h
@@ -61,7 +61,7 @@
 void __FD_SET_chk(int, fd_set*, size_t) __INTRODUCED_IN(21);
 int __FD_ISSET_chk(int, fd_set*, size_t) __INTRODUCED_IN(21);
 
-#if defined(__BIONIC_FORTIFY)
+#if defined(__BIONIC_FORTIFY) && __ANDROID_API__ >= 21
 #define FD_CLR(fd, set) __FD_CLR_chk(fd, set, __bos(set))
 #define FD_SET(fd, set) __FD_SET_chk(fd, set, __bos(set))
 #define FD_ISSET(fd, set) __FD_ISSET_chk(fd, set, __bos(set))
@@ -69,7 +69,7 @@
 #define FD_CLR(fd, set) (__FDS_BITS(set)[__FDELT(fd)] &= ~__FDMASK(fd))
 #define FD_SET(fd, set) (__FDS_BITS(set)[__FDELT(fd)] |= __FDMASK(fd))
 #define FD_ISSET(fd, set) ((__FDS_BITS(set)[__FDELT(fd)] & __FDMASK(fd)) != 0)
-#endif /* defined(__BIONIC_FORTIFY) */
+#endif /* defined(__BIONIC_FORTIFY) && __ANDROID_API >= 21 */
 
 int select(int, fd_set*, fd_set*, fd_set*, struct timeval*);
 int pselect(int, fd_set*, fd_set*, fd_set*, const struct timespec*, const sigset_t*);
diff --git a/libc/include/sys/socket.h b/libc/include/sys/socket.h
index 910fee1..03dc849 100644
--- a/libc/include/sys/socket.h
+++ b/libc/include/sys/socket.h
@@ -114,7 +114,22 @@
    ? (struct cmsghdr*) (msg)->msg_control : (struct cmsghdr*) NULL)
 #define CMSG_OK(mhdr, cmsg) ((cmsg)->cmsg_len >= sizeof(struct cmsghdr) &&   (cmsg)->cmsg_len <= (unsigned long)   ((mhdr)->msg_controllen -   ((char*)(cmsg) - (char*)(mhdr)->msg_control)))
 
+#if __ANDROID_API__ >= 21
 struct cmsghdr* __cmsg_nxthdr(struct msghdr*, struct cmsghdr*) __INTRODUCED_IN(21);
+#else
+/* TODO(danalbert): Move this into libandroid_support. */
+static inline struct cmsghdr* __cmsg_nxthdr(struct msghdr* msg, struct cmsghdr* cmsg) {
+  struct cmsghdr* ptr =
+      __BIONIC_CAST(reinterpret_cast, struct cmsghdr*,
+                    (__BIONIC_CAST(reinterpret_cast, char*, cmsg) + CMSG_ALIGN(cmsg->cmsg_len)));
+  size_t len = __BIONIC_CAST(reinterpret_cast, char*, ptr + 1) -
+               __BIONIC_CAST(reinterpret_cast, char*, msg->msg_control);
+  if (len > msg->msg_controllen) {
+    return NULL;
+  }
+  return ptr;
+}
+#endif /* __ANDROID_API__ >= 21 */
 
 #define SCM_RIGHTS 0x01
 #define SCM_CREDENTIALS 0x02
diff --git a/libc/include/unistd.h b/libc/include/unistd.h
index ac7d4c2..4e7d8ba 100644
--- a/libc/include/unistd.h
+++ b/libc/include/unistd.h
@@ -209,7 +209,7 @@
 #if __ANDROID_API__ >= 21
 int getpagesize(void) __INTRODUCED_IN(21);
 #else
-__inline__ int getpagesize(void) {
+static __inline__ int getpagesize(void) {
   return sysconf(_SC_PAGESIZE);
 }
 #endif
@@ -489,6 +489,7 @@
 }
 #endif /* __ANDROID_API__ >= 24 */
 
+#if __ANDROID_API__ >= 23
 __BIONIC_FORTIFY_INLINE
 ssize_t readlink(const char* path, char* buf, size_t size) {
     size_t bos = __bos(buf);
@@ -538,6 +539,7 @@
 
     return __readlinkat_chk(dirfd, path, buf, size, bos);
 }
+#endif /* __ANDROID_API__ >= 23 */
 
 #endif /* defined(__BIONIC_FORTIFY) */
 
diff --git a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
index b8cf7cf..49edaba 100644
--- a/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
+++ b/libc/malloc_debug/tests/malloc_debug_config_tests.cpp
@@ -122,7 +122,7 @@
   "6 malloc_debug     recorded. of frames to capture. The default value is 8000000.\n"
   "6 malloc_debug     If the allocation list fills up, all further allocations are not recorded.\n"
   "6 malloc_debug \n"
-  "6 malloc_debug   record_alloc_file[=FILE]\n"
+  "6 malloc_debug   record_allocs_file[=FILE]\n"
   "6 malloc_debug     This option only has meaning if the record_allocs options has been specified.\n"
   "6 malloc_debug     This is the name of the file to which recording information will be dumped.\n"
   "6 malloc_debug     The default is /data/local/tmp/record_allocs.txt.\n"
diff --git a/libc/upstream-openbsd/lib/libc/stdio/floatio.h b/libc/stdio/floatio.h
similarity index 100%
rename from libc/upstream-openbsd/lib/libc/stdio/floatio.h
rename to libc/stdio/floatio.h
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index 7be5a7c..575a428 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -147,11 +147,7 @@
 #define __SNPT 0
 #define __SOPT 0
 
-#if defined(__cplusplus)
-#define _EXT(fp) reinterpret_cast<__sfileext*>((fp)->_ext._base)
-#else
-#define _EXT(fp) ((struct __sfileext *)((fp)->_ext._base))
-#endif
+#define _EXT(fp) __BIONIC_CAST(reinterpret_cast, struct __sfileext*, (fp)->_ext._base)
 
 #define _UB(fp) _EXT(fp)->_ub
 #define _FLOCK(fp)  _EXT(fp)->_lock
@@ -171,7 +167,7 @@
 
 #define _FILEEXT_SETUP(f, fext) \
 do { \
-	(f)->_ext._base = (unsigned char *)(fext); \
+	(f)->_ext._base = __BIONIC_CAST(reinterpret_cast, unsigned char*, fext); \
 	_FILEEXT_INIT(f); \
 } while (0)
 
@@ -259,6 +255,9 @@
 extern void __sinit(void); // Not actually implemented.
 #define __sdidinit 1
 
+size_t parsefloat(FILE*, char*, char*);
+size_t wparsefloat(FILE*, wchar_t*, wchar_t*);
+
 __END_DECLS
 
 #endif
diff --git a/libc/stdio/parsefloat.c b/libc/stdio/parsefloat.c
new file mode 100644
index 0000000..e911da4
--- /dev/null
+++ b/libc/stdio/parsefloat.c
@@ -0,0 +1,336 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 <ctype.h>
+#include <stdlib.h>
+
+#include "local.h"
+#include "floatio.h"
+
+#define	BUF		513	/* Maximum length of numeric string. */
+
+size_t parsefloat(FILE *fp, char *buf, char *end) {
+	char *commit, *p;
+	int infnanpos = 0;
+	enum {
+		S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
+		S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
+	} state = S_START;
+	unsigned char c;
+	int gotmantdig = 0, ishex = 0;
+
+	/*
+	 * We set commit = p whenever the string we have read so far
+	 * constitutes a valid representation of a floating point
+	 * number by itself.  At some point, the parse will complete
+	 * or fail, and we will ungetc() back to the last commit point.
+	 * To ensure that the file offset gets updated properly, it is
+	 * always necessary to read at least one character that doesn't
+	 * match; thus, we can't short-circuit "infinity" or "nan(...)".
+	 */
+	commit = buf - 1;
+	for (p = buf; p < end; ) {
+		c = *fp->_p;
+reswitch:
+		switch (state) {
+		case S_START:
+			state = S_GOTSIGN;
+			if (c == '-' || c == '+')
+				break;
+			else
+				goto reswitch;
+		case S_GOTSIGN:
+			switch (c) {
+			case '0':
+				state = S_MAYBEHEX;
+				commit = p;
+				break;
+			case 'I':
+			case 'i':
+				state = S_INF;
+				break;
+			case 'N':
+			case 'n':
+				state = S_NAN;
+				break;
+			default:
+				state = S_DIGITS;
+				goto reswitch;
+			}
+			break;
+		case S_INF:
+			if (infnanpos > 6 ||
+			    (c != "nfinity"[infnanpos] &&
+			     c != "NFINITY"[infnanpos]))
+				goto parsedone;
+			if (infnanpos == 1 || infnanpos == 6)
+				commit = p;	/* inf or infinity */
+			infnanpos++;
+			break;
+		case S_NAN:
+			switch (infnanpos) {
+			case -1:	/* XXX kludge to deal with nan(...) */
+				goto parsedone;
+			case 0:
+				if (c != 'A' && c != 'a')
+					goto parsedone;
+				break;
+			case 1:
+				if (c != 'N' && c != 'n')
+					goto parsedone;
+				else
+					commit = p;
+				break;
+			case 2:
+				if (c != '(')
+					goto parsedone;
+				break;
+			default:
+				if (c == ')') {
+					commit = p;
+					infnanpos = -2;
+				} else if (!isalnum(c) && c != '_')
+					goto parsedone;
+				break;
+			}
+			infnanpos++;
+			break;
+		case S_MAYBEHEX:
+			state = S_DIGITS;
+			if (c == 'X' || c == 'x') {
+				ishex = 1;
+				break;
+			} else {	/* we saw a '0', but no 'x' */
+				gotmantdig = 1;
+				goto reswitch;
+			}
+		case S_DIGITS:
+			if ((ishex && isxdigit(c)) || isdigit(c))
+				gotmantdig = 1;
+			else {
+				state = S_FRAC;
+				if (c != '.')
+					goto reswitch;
+			}
+			if (gotmantdig)
+				commit = p;
+			break;
+		case S_FRAC:
+			if (((c == 'E' || c == 'e') && !ishex) ||
+			    ((c == 'P' || c == 'p') && ishex)) {
+				if (!gotmantdig)
+					goto parsedone;
+				else
+					state = S_EXP;
+			} else if ((ishex && isxdigit(c)) || isdigit(c)) {
+				commit = p;
+				gotmantdig = 1;
+			} else
+				goto parsedone;
+			break;
+		case S_EXP:
+			state = S_EXPDIGITS;
+			if (c == '-' || c == '+')
+				break;
+			else
+				goto reswitch;
+		case S_EXPDIGITS:
+			if (isdigit(c))
+				commit = p;
+			else
+				goto parsedone;
+			break;
+		default:
+			abort();
+		}
+		*p++ = c;
+		if (--fp->_r > 0)
+			fp->_p++;
+		else if (__srefill(fp))
+			break;	/* EOF */
+	}
+
+parsedone:
+	while (commit < --p)
+		(void)ungetc(*(unsigned char *)p, fp);
+	*++commit = '\0';
+	return commit - buf;
+}
+
+size_t wparsefloat(FILE *fp, wchar_t *buf, wchar_t *end) {
+	wchar_t *commit, *p;
+	int infnanpos = 0;
+	enum {
+		S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
+		S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
+	} state = S_START;
+	wint_t c;
+	int gotmantdig = 0, ishex = 0;
+
+	/*
+	 * We set commit = p whenever the string we have read so far
+	 * constitutes a valid representation of a floating point
+	 * number by itself.  At some point, the parse will complete
+	 * or fail, and we will ungetc() back to the last commit point.
+	 * To ensure that the file offset gets updated properly, it is
+	 * always necessary to read at least one character that doesn't
+	 * match; thus, we can't short-circuit "infinity" or "nan(...)".
+	 */
+	commit = buf - 1;
+	c = WEOF;
+	for (p = buf; p < end; ) {
+		if ((c = __fgetwc_unlock(fp)) == WEOF)
+			break;
+reswitch:
+		switch (state) {
+		case S_START:
+			state = S_GOTSIGN;
+			if (c == '-' || c == '+')
+				break;
+			else
+				goto reswitch;
+		case S_GOTSIGN:
+			switch (c) {
+			case '0':
+				state = S_MAYBEHEX;
+				commit = p;
+				break;
+			case 'I':
+			case 'i':
+				state = S_INF;
+				break;
+			case 'N':
+			case 'n':
+				state = S_NAN;
+				break;
+			default:
+				state = S_DIGITS;
+				goto reswitch;
+			}
+			break;
+		case S_INF:
+			if (infnanpos > 6 ||
+			    (c != (wint_t)"nfinity"[infnanpos] &&
+			     c != (wint_t)"NFINITY"[infnanpos]))
+				goto parsedone;
+			if (infnanpos == 1 || infnanpos == 6)
+				commit = p;	/* inf or infinity */
+			infnanpos++;
+			break;
+		case S_NAN:
+			switch (infnanpos) {
+			case -1:	/* XXX kludge to deal with nan(...) */
+				goto parsedone;
+			case 0:
+				if (c != 'A' && c != 'a')
+					goto parsedone;
+				break;
+			case 1:
+				if (c != 'N' && c != 'n')
+					goto parsedone;
+				else
+					commit = p;
+				break;
+			case 2:
+				if (c != '(')
+					goto parsedone;
+				break;
+			default:
+				if (c == ')') {
+					commit = p;
+					infnanpos = -2;
+				} else if (!iswalnum(c) && c != '_')
+					goto parsedone;
+				break;
+			}
+			infnanpos++;
+			break;
+		case S_MAYBEHEX:
+			state = S_DIGITS;
+			if (c == 'X' || c == 'x') {
+				ishex = 1;
+				break;
+			} else {	/* we saw a '0', but no 'x' */
+				gotmantdig = 1;
+				goto reswitch;
+			}
+		case S_DIGITS:
+			if ((ishex && iswxdigit(c)) || iswdigit(c))
+				gotmantdig = 1;
+			else {
+				state = S_FRAC;
+				if (c != L'.')
+					goto reswitch;
+			}
+			if (gotmantdig)
+				commit = p;
+			break;
+		case S_FRAC:
+			if (((c == 'E' || c == 'e') && !ishex) ||
+			    ((c == 'P' || c == 'p') && ishex)) {
+				if (!gotmantdig)
+					goto parsedone;
+				else
+					state = S_EXP;
+			} else if ((ishex && iswxdigit(c)) || iswdigit(c)) {
+				commit = p;
+				gotmantdig = 1;
+			} else
+				goto parsedone;
+			break;
+		case S_EXP:
+			state = S_EXPDIGITS;
+			if (c == '-' || c == '+')
+				break;
+			else
+				goto reswitch;
+		case S_EXPDIGITS:
+			if (iswdigit(c))
+				commit = p;
+			else
+				goto parsedone;
+			break;
+		default:
+			abort();
+		}
+		*p++ = c;
+		c = WEOF;
+	}
+
+parsedone:
+	if (c != WEOF)
+		ungetwc(c, fp);
+	while (commit < --p)
+		ungetwc(*p, fp);
+	*++commit = '\0';
+	return (int)(commit - buf);
+}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/vfscanf.c b/libc/stdio/vfscanf.c
similarity index 90%
rename from libc/upstream-openbsd/lib/libc/stdio/vfscanf.c
rename to libc/stdio/vfscanf.c
index abefe32..e47d0df 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/vfscanf.c
+++ b/libc/stdio/vfscanf.c
@@ -346,14 +346,14 @@
 					wcp = NULL;
 				n = 0;
 				while (width != 0) {
-					if (n == MB_CUR_MAX) {
+					if (n == (int)MB_CUR_MAX) {
 						fp->_flags |= __SERR;
 						goto input_failure;
 					}
 					buf[n++] = *fp->_p;
 					fp->_p++;
 					fp->_r--;
-					bzero(&mbs, sizeof(mbs));
+					memset(&mbs, 0, sizeof(mbs));
 					nconv = mbrtowc(wcp, buf, n, &mbs);
 					if (nconv == (size_t)-1) {
 						fp->_flags |= __SERR;
@@ -383,7 +383,7 @@
 			if (flags & SUPPRESS) {
 				size_t sum = 0;
 				for (;;) {
-					if ((n = fp->_r) < width) {
+					if ((n = fp->_r) < (int)width) {
 						sum += n;
 						width -= n;
 						fp->_p += n;
@@ -428,14 +428,14 @@
 				n = 0;
 				nchars = 0;
 				while (width != 0) {
-					if (n == MB_CUR_MAX) {
+					if (n == (int)MB_CUR_MAX) {
 						fp->_flags |= __SERR;
 						goto input_failure;
 					}
 					buf[n++] = *fp->_p;
 					fp->_p++;
 					fp->_r--;
-					bzero(&mbs, sizeof(mbs));
+					memset(&mbs, 0, sizeof(mbs));
 					nconv = mbrtowc(wcp, buf, n, &mbs);
 					if (nconv == (size_t)-1) {
 						fp->_flags |= __SERR;
@@ -532,14 +532,14 @@
 					wcp = &twc;
 				n = 0;
 				while (!isspace(*fp->_p) && width != 0) {
-					if (n == MB_CUR_MAX) {
+					if (n == (int)MB_CUR_MAX) {
 						fp->_flags |= __SERR;
 						goto input_failure;
 					}
 					buf[n++] = *fp->_p;
 					fp->_p++;
 					fp->_r--;
-					bzero(&mbs, sizeof(mbs));
+					memset(&mbs, 0, sizeof(mbs));
 					nconv = mbrtowc(wcp, buf, n, &mbs);
 					if (nconv == (size_t)-1) {
 						fp->_flags |= __SERR;
@@ -760,96 +760,25 @@
 #ifdef FLOATING_POINT
 		case CT_FLOAT:
 			/* scan a floating point number as if by strtod */
-#ifdef hardway
 			if (width == 0 || width > sizeof(buf) - 1)
 				width = sizeof(buf) - 1;
-#else
-			/* size_t is unsigned, hence this optimisation */
-			if (--width > sizeof(buf) - 2)
-				width = sizeof(buf) - 2;
-			width++;
-#endif
-			flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
-			for (p = buf; width; width--) {
-				c = *fp->_p;
-				/*
-				 * This code mimicks the integer conversion
-				 * code, but is much simpler.
-				 */
-				switch (c) {
-
-				case '0': case '1': case '2': case '3':
-				case '4': case '5': case '6': case '7':
-				case '8': case '9':
-					flags &= ~(SIGNOK | NDIGITS);
-					goto fok;
-
-				case '+': case '-':
-					if (flags & SIGNOK) {
-						flags &= ~SIGNOK;
-						goto fok;
-					}
-					break;
-				case '.':
-					if (flags & DPTOK) {
-						flags &= ~(SIGNOK | DPTOK);
-						goto fok;
-					}
-					break;
-				case 'e': case 'E':
-					/* no exponent without some digits */
-					if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
-						flags =
-						    (flags & ~(EXPOK|DPTOK)) |
-						    SIGNOK | NDIGITS;
-						goto fok;
-					}
-					break;
-				}
-				break;
-		fok:
-				*p++ = c;
-				if (--fp->_r > 0)
-					fp->_p++;
-				else if (__srefill(fp))
-					break;	/* EOF */
-			}
-			/*
-			 * If no digits, might be missing exponent digits
-			 * (just give back the exponent) or might be missing
-			 * regular digits, but had sign and/or decimal point.
-			 */
-			if (flags & NDIGITS) {
-				if (flags & EXPOK) {
-					/* no digits at all */
-					while (p > buf)
-						ungetc(*(u_char *)--p, fp);
-					goto match_failure;
-				}
-				/* just a bad exponent (e and maybe sign) */
-				c = *(u_char *)--p;
-				if (c != 'e' && c != 'E') {
-					(void) ungetc(c, fp);/* sign */
-					c = *(u_char *)--p;
-				}
-				(void) ungetc(c, fp);
-			}
+			if ((width = parsefloat(fp, buf, buf + width)) == 0)
+				goto match_failure;
 			if ((flags & SUPPRESS) == 0) {
-				*p = '\0';
 				if (flags & LONGDBL) {
-					long double res = strtold(buf,
-					    (char **)NULL);
+					long double res = strtold(buf, &p);
 					*va_arg(ap, long double *) = res;
 				} else if (flags & LONG) {
-					double res = strtod(buf, (char **)NULL);
+					double res = strtod(buf, &p);
 					*va_arg(ap, double *) = res;
 				} else {
-					float res = strtof(buf, (char **)NULL);
+					float res = strtof(buf, &p);
 					*va_arg(ap, float *) = res;
 				}
+				if ((size_t)(p - buf) != width) abort();
 				nassigned++;
 			}
-			nread += p - buf;
+			nread += width;
 			break;
 #endif /* FLOATING_POINT */
 		}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/vfwscanf.c b/libc/stdio/vfwscanf.c
similarity index 89%
rename from libc/upstream-openbsd/lib/libc/stdio/vfwscanf.c
rename to libc/stdio/vfwscanf.c
index cbb36be..0a7bfa9 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/vfwscanf.c
+++ b/libc/stdio/vfwscanf.c
@@ -95,6 +95,9 @@
 	(cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) : \
 	(wmemchr(ccls, (_c), ccle - ccls) != NULL))
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
 /*
  * vfwscanf
  */
@@ -120,9 +123,6 @@
 	size_t nconv;		/* number of bytes in mb. conversion */
 	char mbbuf[MB_LEN_MAX];	/* temporary mb. character buffer */
  	mbstate_t mbs;
-#ifdef FLOATING_POINT
-	wchar_t decimal_point = 0;
-#endif
 
 	/* `basefix' is used to avoid `if' tests in the integer scanner */
 	static short basefix[17] =
@@ -371,7 +371,7 @@
 				if (!(flags & SUPPRESS))
 					mbp = va_arg(ap, char *);
 				n = 0;
-				bzero(&mbs, sizeof(mbs));
+				memset(&mbs, 0, sizeof(mbs));
 				while (width != 0 &&
 				    (wi = __fgetwc_unlock(fp)) != WEOF) {
 					if (width >= MB_CUR_MAX &&
@@ -436,7 +436,7 @@
 				if (!(flags & SUPPRESS))
 					mbp = va_arg(ap, char *);
 				n = 0;
-				bzero(&mbs, sizeof(mbs));
+				memset(&mbs, 0, sizeof(mbs));
 				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
 				    width != 0 && INCCL(wi)) {
 					if (width >= MB_CUR_MAX &&
@@ -497,7 +497,7 @@
 			} else {
 				if (!(flags & SUPPRESS))
 					mbp = va_arg(ap, char *);
-				bzero(&mbs, sizeof(mbs));
+				memset(&mbs, 0, sizeof(mbs));
 				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
 				    width != 0 &&
 				    !iswspace(wi)) {
@@ -686,95 +686,23 @@
 			if (width == 0 || width > sizeof(buf) /
 			    sizeof(*buf) - 1)
 				width = sizeof(buf) / sizeof(*buf) - 1;
-			flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
-			for (p = buf; width; width--) {
-				c = __fgetwc_unlock(fp);
-				/*
-				 * This code mimicks the integer conversion
-				 * code, but is much simpler.
-				 */
-				switch (c) {
-
-				case '0': case '1': case '2': case '3':
-				case '4': case '5': case '6': case '7':
-				case '8': case '9':
-					flags &= ~(SIGNOK | NDIGITS);
-					goto fok;
-
-				case '+': case '-':
-					if (flags & SIGNOK) {
-						flags &= ~SIGNOK;
-						goto fok;
-					}
-					break;
-				case 'e': case 'E':
-					/* no exponent without some digits */
-					if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
-						flags =
-						    (flags & ~(EXPOK|DPTOK)) |
-						    SIGNOK | NDIGITS;
-						goto fok;
-					}
-					break;
-				default:
-					if (decimal_point == 0) {
-						bzero(&mbs, sizeof(mbs));
-						nconv = mbrtowc(&decimal_point,
-						    localeconv()->decimal_point,
-					    	    MB_CUR_MAX, &mbs);
-						if (nconv == 0 ||
-						    nconv == (size_t)-1 ||
-						    nconv == (size_t)-2)
-							decimal_point = '.';
-					}
-					if (c == decimal_point &&
-					    (flags & DPTOK)) {
-						flags &= ~(SIGNOK | DPTOK);
-						goto fok;
-					}
-					break;
-				}
-				if (c != WEOF)
-					__ungetwc(c, fp);
-				break;
-		fok:
-				*p++ = c;
-			}
-			/*
-			 * If no digits, might be missing exponent digits
-			 * (just give back the exponent) or might be missing
-			 * regular digits, but had sign and/or decimal point.
-			 */
-			if (flags & NDIGITS) {
-				if (flags & EXPOK) {
-					/* no digits at all */
-					while (p > buf)
-						__ungetwc(*--p, fp);
-					goto match_failure;
-				}
-				/* just a bad exponent (e and maybe sign) */
-				c = *--p;
-				if (c != 'e' && c != 'E') {
-					__ungetwc(c, fp);/* sign */
-					c = *--p;
-				}
-				__ungetwc(c, fp);
-			}
+			if ((width = wparsefloat(fp, buf, buf + width)) == 0)
+				goto match_failure;
 			if ((flags & SUPPRESS) == 0) {
-				*p = 0;
 				if (flags & LONGDBL) {
-					long double res = wcstold(buf, NULL);
+					long double res = wcstold(buf, &p);
 					*va_arg(ap, long double *) = res;
 				} else if (flags & LONG) {
-					double res = wcstod(buf, NULL);
+					double res = wcstod(buf, &p);
 					*va_arg(ap, double *) = res;
 				} else {
-					float res = wcstof(buf, NULL);
+					float res = wcstof(buf, &p);
 					*va_arg(ap, float *) = res;
 				}
+				if (p - buf != (ptrdiff_t)width) abort();
 				nassigned++;
 			}
-			nread += p - buf;
+			nread += width;
 			nconversions++;
 			break;
 #endif /* FLOATING_POINT */
@@ -785,6 +713,7 @@
 match_failure:
 	return (nassigned);
 }
+#pragma GCC diagnostic pop
 
 int
 vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, __va_list ap)
diff --git a/libc/upstream-openbsd/android/include/gd_qnan.h b/libc/upstream-openbsd/android/include/gd_qnan.h
index e8e907b..e5bf973 100644
--- a/libc/upstream-openbsd/android/include/gd_qnan.h
+++ b/libc/upstream-openbsd/android/include/gd_qnan.h
@@ -14,35 +14,20 @@
  * limitations under the License.
  */
 
-#if __arm__
+//
+// The values in this file came from reading the bits of <math.h>'s NAN back from a union.
+//
 
-#define f_QNAN  0xffffffff
-
-#define d_QNAN0 0xffffffff
-#define d_QNAN1 0xffffffff
-
-#elif __mips__
-
-#define f_QNAN  0x7fbfffff
-
-#define d_QNAN0 0x7ff7ffff
-#define d_QNAN1 0xffffffff
-
-#else
-
-#define f_QNAN  0xffc00000
+#define f_QNAN 0x7fc00000
 
 #define d_QNAN0 0x00000000
-#define d_QNAN1 0xfff80000
+#define d_QNAN1 0x7ff80000
 
-#endif
-
-/* long double. */
 #if __LP64__
-#define ld_QNAN0 0x7fff8000
+#define ld_QNAN0 0x00000000
 #define ld_QNAN1 0x00000000
 #define ld_QNAN2 0x00000000
-#define ld_QNAN3 0x00000000
+#define ld_QNAN3 0x7fff8000
 #else
-/* sizeof(long double) == sizeof(double), so we shouldn't be trying to use these constants. */
+// LP32 sizeof(long double) == sizeof(double), so LP32 shouldn't try to use these constants.
 #endif
diff --git a/libc/upstream-openbsd/lib/libc/locale/_wcstod.h b/libc/upstream-openbsd/lib/libc/locale/_wcstod.h
deleted file mode 100644
index ae993ad..0000000
--- a/libc/upstream-openbsd/lib/libc/locale/_wcstod.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/*	$OpenBSD: _wcstod.h,v 1.2 2013/06/02 15:22:20 matthew Exp $	*/
-/* $NetBSD: wcstod.c,v 1.4 2001/10/28 12:08:43 yamt Exp $ */
-
-/*-
- * Copyright (c)1999, 2000, 2001 Citrus 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:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. 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 AUTHOR 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 AUTHOR 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.
- *
- *	$Citrus: xpg4dl/FreeBSD/lib/libc/locale/wcstod.c,v 1.2 2001/09/27 16:23:57 yamt Exp $
- */
-
-/*
- * function template for wcstof, wcstod and wcstold.
- *
- * parameters:
- *	FUNCNAME : function name
- *      float_type : return type
- *      STRTOD_FUNC : conversion function
- */
-
-float_type
-FUNCNAME(const wchar_t *nptr, wchar_t **endptr)
-{
-	const wchar_t *src;
-	size_t size;
-	const wchar_t *start;
-	const wchar_t *aftersign;
-
-	/*
-	 * check length of string and call strtod
-	 */
-	src = nptr;
-
-	/* skip space first */
-	while (iswspace(*src)) {
-		src++;
-	}
-
-	/* get length of string */
-	start = src;
-	if (*src && wcschr(L"+-", *src))
-		src++;
-	aftersign = src;
-	if (wcsncasecmp(src, L"inf", 3) == 0) {
-		src += 3;
-		if (wcsncasecmp(src, L"inity", 5) == 0)
-			src += 5;
-		goto match;
-	}
-	if (wcsncasecmp(src, L"nan", 3) == 0) {
-		src += 3;
-		if (*src == L'(') {
-			size = 1;
-			while (src[size] != L'\0' && src[size] != L')')
-				size++;
-			if (src[size] == L')')
-				src += size + 1;
-		}
-		goto match;
-	}
-	size = wcsspn(src, L"0123456789");
-	src += size;
-	if (*src == L'.') {/* XXX use localeconv */
-		src++;
-		size = wcsspn(src, L"0123456789");
-		src += size;
-	}
-	if (*src && wcschr(L"Ee", *src)) {
-		src++;
-		if (*src && wcschr(L"+-", *src))
-			src++;
-		size = wcsspn(src, L"0123456789");
-		src += size;
-	}
-match:
-	size = src - start;
-
-	/*
-	 * convert to a char-string and pass it to strtod.
-	 */
-	if (src > aftersign) {
-		mbstate_t st;
-		char *buf;
-		char *end;
-		const wchar_t *s;
-		size_t size_converted;
-		float_type result;
-		size_t bufsize;
-
-		s = start;
-		memset(&st, 0, sizeof(st));
-		bufsize = wcsnrtombs(NULL, &s, size, 0, &st);
-
-		buf = malloc(bufsize + 1);
-		if (!buf) {
-			errno = ENOMEM; /* XXX */
-			goto fail;
-		}
-
-		s = start;
-		memset(&st, 0, sizeof(st));
-		size_converted = wcsnrtombs(buf, &s, size, bufsize, &st);
-		if (size_converted != bufsize) {
-			/* XXX should not happen */
-			free(buf);
-			errno = EILSEQ;
-			goto fail;
-		}
-
-		buf[bufsize] = 0;
-		result = STRTOD_FUNC(buf, &end);
-
-		if (endptr) {
-			const char *s = buf;
-			memset(&st, 0, sizeof(st));
-			size = mbsnrtowcs(NULL, &s, end - buf, 0, &st);
-
-			/* LINTED bad interface */
-			*endptr = (wchar_t*)start + size;
-		}
-
-		free(buf);
-
-		return result;
-	}
-
-fail:
-	if (endptr)
-		/* LINTED bad interface */
-		*endptr = (wchar_t*)nptr;
-
-	return 0;
-}
diff --git a/libc/upstream-openbsd/lib/libc/locale/wcstod.c b/libc/upstream-openbsd/lib/libc/locale/wcstod.c
deleted file mode 100644
index 957d0a1..0000000
--- a/libc/upstream-openbsd/lib/libc/locale/wcstod.c
+++ /dev/null
@@ -1,13 +0,0 @@
-/*	$OpenBSD: wcstod.c,v 1.3 2009/01/13 18:18:31 kettenis Exp $	*/
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#define FUNCNAME	wcstod
-typedef double		float_type;
-#define STRTOD_FUNC	strtod
-
-#include "_wcstod.h"
diff --git a/libc/upstream-openbsd/lib/libc/locale/wcstof.c b/libc/upstream-openbsd/lib/libc/locale/wcstof.c
deleted file mode 100644
index 40d76c7..0000000
--- a/libc/upstream-openbsd/lib/libc/locale/wcstof.c
+++ /dev/null
@@ -1,13 +0,0 @@
-/*	$OpenBSD: wcstof.c,v 1.1 2009/01/13 18:18:31 kettenis Exp $	*/
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#define FUNCNAME	wcstof
-typedef float		float_type;
-#define STRTOD_FUNC	strtof
-
-#include "_wcstod.h"
diff --git a/libc/upstream-openbsd/lib/libc/locale/wcstold.c b/libc/upstream-openbsd/lib/libc/locale/wcstold.c
deleted file mode 100644
index a642542..0000000
--- a/libc/upstream-openbsd/lib/libc/locale/wcstold.c
+++ /dev/null
@@ -1,13 +0,0 @@
-/*	$OpenBSD: wcstold.c,v 1.1 2009/01/13 18:18:31 kettenis Exp $	*/
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#define FUNCNAME	wcstold
-typedef long double	float_type;
-#define STRTOD_FUNC	strtold
-
-#include "_wcstod.h"
diff --git a/libm/Android.bp b/libm/Android.bp
index 23b9d5e..64d281f 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -531,3 +531,9 @@
     },
     stl: "none",
 }
+
+ndk_library {
+    name: "libm.ndk",
+    symbol_file: "libm.map.txt",
+    first_version: "9",
+}
diff --git a/libm/libm.arm.map b/libm/libm.arm.map
index a61dc2e..3052f03 100644
--- a/libm/libm.arm.map
+++ b/libm/libm.arm.map
@@ -1,7 +1,7 @@
 # Generated by genversion-scripts.py. Do not edit.
 LIBC {
   global:
-    __fe_dfl_env;
+    __fe_dfl_env; # var
     __signbit;
     __signbitf;
     __signbitl;
@@ -9,59 +9,59 @@
     acosf;
     acosh;
     acoshf;
-    acoshl;
-    acosl;
+    acoshl; # introduced=21
+    acosl; # introduced=21
     asin;
     asinf;
     asinh;
     asinhf;
-    asinhl;
-    asinl;
+    asinhl; # introduced=21
+    asinl; # introduced=21
     atan;
     atan2;
     atan2f;
-    atan2l;
+    atan2l; # introduced=21
     atanf;
     atanh;
     atanhf;
-    atanhl;
-    atanl;
-    cabs;
-    cabsf;
-    cabsl;
-    cacos;
-    cacosf;
-    cacosh;
-    cacoshf;
-    carg;
-    cargf;
-    cargl;
-    casin;
-    casinf;
-    casinh;
-    casinhf;
-    catan;
-    catanf;
-    catanh;
-    catanhf;
+    atanhl; # introduced=21
+    atanl; # introduced=21
+    cabs; # introduced=23
+    cabsf; # introduced=23
+    cabsl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    cacos; # introduced=23
+    cacosf; # introduced=23
+    cacosh; # introduced=23
+    cacoshf; # introduced=23
+    carg; # introduced=23
+    cargf; # introduced=23
+    cargl; # introduced=23
+    casin; # introduced=23
+    casinf; # introduced=23
+    casinh; # introduced=23
+    casinhf; # introduced=23
+    catan; # introduced=23
+    catanf; # introduced=23
+    catanh; # introduced=23
+    catanhf; # introduced=23
     cbrt;
     cbrtf;
-    cbrtl;
-    ccos;
-    ccosf;
-    ccosh;
-    ccoshf;
+    cbrtl; # introduced=21
+    ccos; # introduced=23
+    ccosf; # introduced=23
+    ccosh; # introduced=23
+    ccoshf; # introduced=23
     ceil;
     ceilf;
     ceill;
-    cexp;
-    cexpf;
-    cimag;
-    cimagf;
-    cimagl;
-    conj;
-    conjf;
-    conjl;
+    cexp; # introduced=23
+    cexpf; # introduced=23
+    cimag; # introduced=23
+    cimagf; # introduced=23
+    cimagl; # introduced=23
+    conj; # introduced=23
+    conjf; # introduced=23
+    conjl; # introduced=23
     copysign;
     copysignf;
     copysignl;
@@ -69,62 +69,62 @@
     cosf;
     cosh;
     coshf;
-    coshl;
-    cosl;
-    cproj;
-    cprojf;
-    cprojl;
-    creal;
-    crealf;
-    creall;
-    csin;
-    csinf;
-    csinh;
-    csinhf;
-    csqrt;
-    csqrtf;
-    csqrtl;
-    ctan;
-    ctanf;
-    ctanh;
-    ctanhf;
+    coshl; # introduced=21
+    cosl; # introduced=21
+    cproj; # introduced=23
+    cprojf; # introduced=23
+    cprojl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    creal; # introduced=23
+    crealf; # introduced=23
+    creall; # introduced=23
+    csin; # introduced=23
+    csinf; # introduced=23
+    csinh; # introduced=23
+    csinhf; # introduced=23
+    csqrt; # introduced=23
+    csqrtf; # introduced=23
+    csqrtl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    ctan; # introduced=23
+    ctanf; # introduced=23
+    ctanh; # introduced=23
+    ctanhf; # introduced=23
     drem;
     dremf;
     erf;
     erfc;
     erfcf;
-    erfcl;
+    erfcl; # introduced=21
     erff;
-    erfl;
+    erfl; # introduced=21
     exp;
     exp2;
     exp2f;
-    exp2l;
+    exp2l; # introduced=21
     expf;
-    expl;
+    expl; # introduced=21
     expm1;
     expm1f;
-    expm1l;
+    expm1l; # introduced=21
     fabs;
     fabsf;
     fabsl;
     fdim;
     fdimf;
     fdiml;
-    feclearexcept;
-    fedisableexcept;
-    feenableexcept;
-    fegetenv;
-    fegetexcept;
-    fegetexceptflag;
-    fegetround;
-    feholdexcept;
-    feraiseexcept;
-    fesetenv;
-    fesetexceptflag;
-    fesetround;
-    fetestexcept;
-    feupdateenv;
+    feclearexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fedisableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feenableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feholdexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feraiseexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fetestexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feupdateenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
     finite;
     finitef;
     floor;
@@ -132,7 +132,7 @@
     floorl;
     fma;
     fmaf;
-    fmal;
+    fmal; # introduced=21
     fmax;
     fmaxf;
     fmaxl;
@@ -141,17 +141,17 @@
     fminl;
     fmod;
     fmodf;
-    fmodl;
+    fmodl; # introduced=21
     frexp;
     frexpf;
-    frexpl;
+    frexpl; # introduced=21
     gamma;
     gamma_r;
     gammaf;
     gammaf_r;
     hypot;
     hypotf;
-    hypotl;
+    hypotl; # introduced=21
     ilogb;
     ilogbf;
     ilogbl;
@@ -167,77 +167,77 @@
     lgamma_r;
     lgammaf;
     lgammaf_r;
-    lgammal;
-    lgammal_r;
+    lgammal; # introduced=21
+    lgammal_r; # introduced=23
     llrint;
     llrintf;
-    llrintl;
+    llrintl; # introduced=21
     llround;
     llroundf;
     llroundl;
     log;
     log10;
     log10f;
-    log10l;
+    log10l; # introduced=21
     log1p;
     log1pf;
-    log1pl;
-    log2;
-    log2f;
-    log2l;
+    log1pl; # introduced=21
+    log2; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2f; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2l; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logb;
     logbf;
-    logbl;
+    logbl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logf;
-    logl;
+    logl; # introduced=21
     lrint;
     lrintf;
-    lrintl;
+    lrintl; # introduced=21
     lround;
     lroundf;
     lroundl;
     modf;
     modff;
-    modfl;
-    nan;
-    nanf;
-    nanl;
+    modfl; # introduced=21
+    nan; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanl; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=13 introduced-x86_64=21
     nearbyint;
     nearbyintf;
-    nearbyintl;
+    nearbyintl; # introduced=21
     nextafter;
     nextafterf;
-    nextafterl;
-    nexttoward;
+    nextafterl; # introduced=21
+    nexttoward; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     nexttowardf;
-    nexttowardl;
+    nexttowardl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     pow;
     powf;
-    powl;
+    powl; # introduced=21
     remainder;
     remainderf;
-    remainderl;
+    remainderl; # introduced=21
     remquo;
     remquof;
-    remquol;
+    remquol; # introduced=21
     rint;
     rintf;
-    rintl;
+    rintl; # introduced=21
     round;
     roundf;
     roundl;
     scalb;
     scalbf;
-    scalbln;
-    scalblnf;
-    scalblnl;
+    scalbln; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnf; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnl; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     scalbn;
     scalbnf;
     scalbnl;
-    signgam;
+    signgam; # var
     significand;
     significandf;
-    significandl;
+    significandl; # introduced=21
     sin;
     sincos;
     sincosf;
@@ -245,20 +245,20 @@
     sinf;
     sinh;
     sinhf;
-    sinhl;
-    sinl;
+    sinhl; # introduced=21
+    sinl; # introduced=21
     sqrt;
     sqrtf;
-    sqrtl;
+    sqrtl; # introduced=21
     tan;
     tanf;
     tanh;
     tanhf;
-    tanhl;
-    tanl;
+    tanhl; # introduced=21
+    tanl; # introduced=21
     tgamma;
-    tgammaf;
-    tgammal;
+    tgammaf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    tgammal; # introduced=21
     trunc;
     truncf;
     truncl;
diff --git a/libm/libm.arm64.map b/libm/libm.arm64.map
index 11032ca..3e259dd 100644
--- a/libm/libm.arm64.map
+++ b/libm/libm.arm64.map
@@ -1,7 +1,7 @@
 # Generated by genversion-scripts.py. Do not edit.
 LIBC {
   global:
-    __fe_dfl_env;
+    __fe_dfl_env; # var
     __signbit;
     __signbitf;
     __signbitl;
@@ -9,59 +9,59 @@
     acosf;
     acosh;
     acoshf;
-    acoshl;
-    acosl;
+    acoshl; # introduced=21
+    acosl; # introduced=21
     asin;
     asinf;
     asinh;
     asinhf;
-    asinhl;
-    asinl;
+    asinhl; # introduced=21
+    asinl; # introduced=21
     atan;
     atan2;
     atan2f;
-    atan2l;
+    atan2l; # introduced=21
     atanf;
     atanh;
     atanhf;
-    atanhl;
-    atanl;
-    cabs;
-    cabsf;
-    cabsl;
-    cacos;
-    cacosf;
-    cacosh;
-    cacoshf;
-    carg;
-    cargf;
-    cargl;
-    casin;
-    casinf;
-    casinh;
-    casinhf;
-    catan;
-    catanf;
-    catanh;
-    catanhf;
+    atanhl; # introduced=21
+    atanl; # introduced=21
+    cabs; # introduced=23
+    cabsf; # introduced=23
+    cabsl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    cacos; # introduced=23
+    cacosf; # introduced=23
+    cacosh; # introduced=23
+    cacoshf; # introduced=23
+    carg; # introduced=23
+    cargf; # introduced=23
+    cargl; # introduced=23
+    casin; # introduced=23
+    casinf; # introduced=23
+    casinh; # introduced=23
+    casinhf; # introduced=23
+    catan; # introduced=23
+    catanf; # introduced=23
+    catanh; # introduced=23
+    catanhf; # introduced=23
     cbrt;
     cbrtf;
-    cbrtl;
-    ccos;
-    ccosf;
-    ccosh;
-    ccoshf;
+    cbrtl; # introduced=21
+    ccos; # introduced=23
+    ccosf; # introduced=23
+    ccosh; # introduced=23
+    ccoshf; # introduced=23
     ceil;
     ceilf;
     ceill;
-    cexp;
-    cexpf;
-    cimag;
-    cimagf;
-    cimagl;
-    conj;
-    conjf;
-    conjl;
+    cexp; # introduced=23
+    cexpf; # introduced=23
+    cimag; # introduced=23
+    cimagf; # introduced=23
+    cimagl; # introduced=23
+    conj; # introduced=23
+    conjf; # introduced=23
+    conjl; # introduced=23
     copysign;
     copysignf;
     copysignl;
@@ -69,62 +69,62 @@
     cosf;
     cosh;
     coshf;
-    coshl;
-    cosl;
-    cproj;
-    cprojf;
-    cprojl;
-    creal;
-    crealf;
-    creall;
-    csin;
-    csinf;
-    csinh;
-    csinhf;
-    csqrt;
-    csqrtf;
-    csqrtl;
-    ctan;
-    ctanf;
-    ctanh;
-    ctanhf;
+    coshl; # introduced=21
+    cosl; # introduced=21
+    cproj; # introduced=23
+    cprojf; # introduced=23
+    cprojl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    creal; # introduced=23
+    crealf; # introduced=23
+    creall; # introduced=23
+    csin; # introduced=23
+    csinf; # introduced=23
+    csinh; # introduced=23
+    csinhf; # introduced=23
+    csqrt; # introduced=23
+    csqrtf; # introduced=23
+    csqrtl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    ctan; # introduced=23
+    ctanf; # introduced=23
+    ctanh; # introduced=23
+    ctanhf; # introduced=23
     drem;
     dremf;
     erf;
     erfc;
     erfcf;
-    erfcl;
+    erfcl; # introduced=21
     erff;
-    erfl;
+    erfl; # introduced=21
     exp;
     exp2;
     exp2f;
-    exp2l;
+    exp2l; # introduced=21
     expf;
-    expl;
+    expl; # introduced=21
     expm1;
     expm1f;
-    expm1l;
+    expm1l; # introduced=21
     fabs;
     fabsf;
     fabsl;
     fdim;
     fdimf;
     fdiml;
-    feclearexcept;
-    fedisableexcept;
-    feenableexcept;
-    fegetenv;
-    fegetexcept;
-    fegetexceptflag;
-    fegetround;
-    feholdexcept;
-    feraiseexcept;
-    fesetenv;
-    fesetexceptflag;
-    fesetround;
-    fetestexcept;
-    feupdateenv;
+    feclearexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fedisableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feenableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feholdexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feraiseexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fetestexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feupdateenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
     finite;
     finitef;
     floor;
@@ -132,7 +132,7 @@
     floorl;
     fma;
     fmaf;
-    fmal;
+    fmal; # introduced=21
     fmax;
     fmaxf;
     fmaxl;
@@ -141,17 +141,17 @@
     fminl;
     fmod;
     fmodf;
-    fmodl;
+    fmodl; # introduced=21
     frexp;
     frexpf;
-    frexpl;
+    frexpl; # introduced=21
     gamma;
     gamma_r;
     gammaf;
     gammaf_r;
     hypot;
     hypotf;
-    hypotl;
+    hypotl; # introduced=21
     ilogb;
     ilogbf;
     ilogbl;
@@ -167,77 +167,77 @@
     lgamma_r;
     lgammaf;
     lgammaf_r;
-    lgammal;
-    lgammal_r;
+    lgammal; # introduced=21
+    lgammal_r; # introduced=23
     llrint;
     llrintf;
-    llrintl;
+    llrintl; # introduced=21
     llround;
     llroundf;
     llroundl;
     log;
     log10;
     log10f;
-    log10l;
+    log10l; # introduced=21
     log1p;
     log1pf;
-    log1pl;
-    log2;
-    log2f;
-    log2l;
+    log1pl; # introduced=21
+    log2; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2f; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2l; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logb;
     logbf;
-    logbl;
+    logbl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logf;
-    logl;
+    logl; # introduced=21
     lrint;
     lrintf;
-    lrintl;
+    lrintl; # introduced=21
     lround;
     lroundf;
     lroundl;
     modf;
     modff;
-    modfl;
-    nan;
-    nanf;
-    nanl;
+    modfl; # introduced=21
+    nan; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanl; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=13 introduced-x86_64=21
     nearbyint;
     nearbyintf;
-    nearbyintl;
+    nearbyintl; # introduced=21
     nextafter;
     nextafterf;
-    nextafterl;
-    nexttoward;
+    nextafterl; # introduced=21
+    nexttoward; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     nexttowardf;
-    nexttowardl;
+    nexttowardl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     pow;
     powf;
-    powl;
+    powl; # introduced=21
     remainder;
     remainderf;
-    remainderl;
+    remainderl; # introduced=21
     remquo;
     remquof;
-    remquol;
+    remquol; # introduced=21
     rint;
     rintf;
-    rintl;
+    rintl; # introduced=21
     round;
     roundf;
     roundl;
     scalb;
     scalbf;
-    scalbln;
-    scalblnf;
-    scalblnl;
+    scalbln; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnf; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnl; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     scalbn;
     scalbnf;
     scalbnl;
-    signgam;
+    signgam; # var
     significand;
     significandf;
-    significandl;
+    significandl; # introduced=21
     sin;
     sincos;
     sincosf;
@@ -245,20 +245,20 @@
     sinf;
     sinh;
     sinhf;
-    sinhl;
-    sinl;
+    sinhl; # introduced=21
+    sinl; # introduced=21
     sqrt;
     sqrtf;
-    sqrtl;
+    sqrtl; # introduced=21
     tan;
     tanf;
     tanh;
     tanhf;
-    tanhl;
-    tanl;
+    tanhl; # introduced=21
+    tanl; # introduced=21
     tgamma;
-    tgammaf;
-    tgammal;
+    tgammaf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    tgammal; # introduced=21
     trunc;
     truncf;
     truncl;
diff --git a/libm/libm.map.txt b/libm/libm.map.txt
index 66dfd19..c77e1d1 100644
--- a/libm/libm.map.txt
+++ b/libm/libm.map.txt
@@ -1,6 +1,6 @@
 LIBC {
   global:
-    __fe_dfl_env;
+    __fe_dfl_env; # var
     __signbit;
     __signbitf;
     __signbitl;
@@ -8,59 +8,59 @@
     acosf;
     acosh;
     acoshf;
-    acoshl;
-    acosl;
+    acoshl; # introduced=21
+    acosl; # introduced=21
     asin;
     asinf;
     asinh;
     asinhf;
-    asinhl;
-    asinl;
+    asinhl; # introduced=21
+    asinl; # introduced=21
     atan;
     atan2;
     atan2f;
-    atan2l;
+    atan2l; # introduced=21
     atanf;
     atanh;
     atanhf;
-    atanhl;
-    atanl;
-    cabs;
-    cabsf;
-    cabsl;
-    cacos;
-    cacosf;
-    cacosh;
-    cacoshf;
-    carg;
-    cargf;
-    cargl;
-    casin;
-    casinf;
-    casinh;
-    casinhf;
-    catan;
-    catanf;
-    catanh;
-    catanhf;
+    atanhl; # introduced=21
+    atanl; # introduced=21
+    cabs; # introduced=23
+    cabsf; # introduced=23
+    cabsl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    cacos; # introduced=23
+    cacosf; # introduced=23
+    cacosh; # introduced=23
+    cacoshf; # introduced=23
+    carg; # introduced=23
+    cargf; # introduced=23
+    cargl; # introduced=23
+    casin; # introduced=23
+    casinf; # introduced=23
+    casinh; # introduced=23
+    casinhf; # introduced=23
+    catan; # introduced=23
+    catanf; # introduced=23
+    catanh; # introduced=23
+    catanhf; # introduced=23
     cbrt;
     cbrtf;
-    cbrtl;
-    ccos;
-    ccosf;
-    ccosh;
-    ccoshf;
+    cbrtl; # introduced=21
+    ccos; # introduced=23
+    ccosf; # introduced=23
+    ccosh; # introduced=23
+    ccoshf; # introduced=23
     ceil;
     ceilf;
     ceill;
-    cexp;
-    cexpf;
-    cimag;
-    cimagf;
-    cimagl;
-    conj;
-    conjf;
-    conjl;
+    cexp; # introduced=23
+    cexpf; # introduced=23
+    cimag; # introduced=23
+    cimagf; # introduced=23
+    cimagl; # introduced=23
+    conj; # introduced=23
+    conjf; # introduced=23
+    conjl; # introduced=23
     copysign;
     copysignf;
     copysignl;
@@ -68,62 +68,62 @@
     cosf;
     cosh;
     coshf;
-    coshl;
-    cosl;
-    cproj;
-    cprojf;
-    cprojl;
-    creal;
-    crealf;
-    creall;
-    csin;
-    csinf;
-    csinh;
-    csinhf;
-    csqrt;
-    csqrtf;
-    csqrtl;
-    ctan;
-    ctanf;
-    ctanh;
-    ctanhf;
+    coshl; # introduced=21
+    cosl; # introduced=21
+    cproj; # introduced=23
+    cprojf; # introduced=23
+    cprojl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    creal; # introduced=23
+    crealf; # introduced=23
+    creall; # introduced=23
+    csin; # introduced=23
+    csinf; # introduced=23
+    csinh; # introduced=23
+    csinhf; # introduced=23
+    csqrt; # introduced=23
+    csqrtf; # introduced=23
+    csqrtl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    ctan; # introduced=23
+    ctanf; # introduced=23
+    ctanh; # introduced=23
+    ctanhf; # introduced=23
     drem;
     dremf;
     erf;
     erfc;
     erfcf;
-    erfcl;
+    erfcl; # introduced=21
     erff;
-    erfl;
+    erfl; # introduced=21
     exp;
     exp2;
     exp2f;
-    exp2l;
+    exp2l; # introduced=21
     expf;
-    expl;
+    expl; # introduced=21
     expm1;
     expm1f;
-    expm1l;
+    expm1l; # introduced=21
     fabs;
     fabsf;
     fabsl;
     fdim;
     fdimf;
     fdiml;
-    feclearexcept;
-    fedisableexcept;
-    feenableexcept;
-    fegetenv;
-    fegetexcept;
-    fegetexceptflag;
-    fegetround;
-    feholdexcept;
-    feraiseexcept;
-    fesetenv;
-    fesetexceptflag;
-    fesetround;
-    fetestexcept;
-    feupdateenv;
+    feclearexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fedisableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feenableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feholdexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feraiseexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fetestexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feupdateenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
     finite;
     finitef;
     floor;
@@ -131,7 +131,7 @@
     floorl;
     fma;
     fmaf;
-    fmal;
+    fmal; # introduced=21
     fmax;
     fmaxf;
     fmaxl;
@@ -140,17 +140,17 @@
     fminl;
     fmod;
     fmodf;
-    fmodl;
+    fmodl; # introduced=21
     frexp;
     frexpf;
-    frexpl;
+    frexpl; # introduced=21
     gamma;
     gamma_r;
     gammaf;
     gammaf_r;
     hypot;
     hypotf;
-    hypotl;
+    hypotl; # introduced=21
     ilogb;
     ilogbf;
     ilogbl;
@@ -166,77 +166,77 @@
     lgamma_r;
     lgammaf;
     lgammaf_r;
-    lgammal;
-    lgammal_r;
+    lgammal; # introduced=21
+    lgammal_r; # introduced=23
     llrint;
     llrintf;
-    llrintl;
+    llrintl; # introduced=21
     llround;
     llroundf;
     llroundl;
     log;
     log10;
     log10f;
-    log10l;
+    log10l; # introduced=21
     log1p;
     log1pf;
-    log1pl;
-    log2;
-    log2f;
-    log2l;
+    log1pl; # introduced=21
+    log2; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2f; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2l; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logb;
     logbf;
-    logbl;
+    logbl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logf;
-    logl;
+    logl; # introduced=21
     lrint;
     lrintf;
-    lrintl;
+    lrintl; # introduced=21
     lround;
     lroundf;
     lroundl;
     modf;
     modff;
-    modfl;
-    nan;
-    nanf;
-    nanl;
+    modfl; # introduced=21
+    nan; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanl; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=13 introduced-x86_64=21
     nearbyint;
     nearbyintf;
-    nearbyintl;
+    nearbyintl; # introduced=21
     nextafter;
     nextafterf;
-    nextafterl;
-    nexttoward;
+    nextafterl; # introduced=21
+    nexttoward; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     nexttowardf;
-    nexttowardl;
+    nexttowardl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     pow;
     powf;
-    powl;
+    powl; # introduced=21
     remainder;
     remainderf;
-    remainderl;
+    remainderl; # introduced=21
     remquo;
     remquof;
-    remquol;
+    remquol; # introduced=21
     rint;
     rintf;
-    rintl;
+    rintl; # introduced=21
     round;
     roundf;
     roundl;
     scalb;
     scalbf;
-    scalbln;
-    scalblnf;
-    scalblnl;
+    scalbln; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnf; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnl; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     scalbn;
     scalbnf;
     scalbnl;
-    signgam;
+    signgam; # var
     significand;
     significandf;
-    significandl;
+    significandl; # introduced=21
     sin;
     sincos;
     sincosf;
@@ -244,20 +244,20 @@
     sinf;
     sinh;
     sinhf;
-    sinhl;
-    sinl;
+    sinhl; # introduced=21
+    sinl; # introduced=21
     sqrt;
     sqrtf;
-    sqrtl;
+    sqrtl; # introduced=21
     tan;
     tanf;
     tanh;
     tanhf;
-    tanhl;
-    tanl;
+    tanhl; # introduced=21
+    tanl; # introduced=21
     tgamma;
-    tgammaf;
-    tgammal;
+    tgammaf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    tgammal; # introduced=21
     trunc;
     truncf;
     truncl;
diff --git a/libm/libm.mips.map b/libm/libm.mips.map
index 8353627..2f01cfa 100644
--- a/libm/libm.mips.map
+++ b/libm/libm.mips.map
@@ -1,7 +1,7 @@
 # Generated by genversion-scripts.py. Do not edit.
 LIBC {
   global:
-    __fe_dfl_env;
+    __fe_dfl_env; # var
     __signbit;
     __signbitf;
     __signbitl;
@@ -9,59 +9,59 @@
     acosf;
     acosh;
     acoshf;
-    acoshl;
-    acosl;
+    acoshl; # introduced=21
+    acosl; # introduced=21
     asin;
     asinf;
     asinh;
     asinhf;
-    asinhl;
-    asinl;
+    asinhl; # introduced=21
+    asinl; # introduced=21
     atan;
     atan2;
     atan2f;
-    atan2l;
+    atan2l; # introduced=21
     atanf;
     atanh;
     atanhf;
-    atanhl;
-    atanl;
-    cabs;
-    cabsf;
-    cabsl;
-    cacos;
-    cacosf;
-    cacosh;
-    cacoshf;
-    carg;
-    cargf;
-    cargl;
-    casin;
-    casinf;
-    casinh;
-    casinhf;
-    catan;
-    catanf;
-    catanh;
-    catanhf;
+    atanhl; # introduced=21
+    atanl; # introduced=21
+    cabs; # introduced=23
+    cabsf; # introduced=23
+    cabsl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    cacos; # introduced=23
+    cacosf; # introduced=23
+    cacosh; # introduced=23
+    cacoshf; # introduced=23
+    carg; # introduced=23
+    cargf; # introduced=23
+    cargl; # introduced=23
+    casin; # introduced=23
+    casinf; # introduced=23
+    casinh; # introduced=23
+    casinhf; # introduced=23
+    catan; # introduced=23
+    catanf; # introduced=23
+    catanh; # introduced=23
+    catanhf; # introduced=23
     cbrt;
     cbrtf;
-    cbrtl;
-    ccos;
-    ccosf;
-    ccosh;
-    ccoshf;
+    cbrtl; # introduced=21
+    ccos; # introduced=23
+    ccosf; # introduced=23
+    ccosh; # introduced=23
+    ccoshf; # introduced=23
     ceil;
     ceilf;
     ceill;
-    cexp;
-    cexpf;
-    cimag;
-    cimagf;
-    cimagl;
-    conj;
-    conjf;
-    conjl;
+    cexp; # introduced=23
+    cexpf; # introduced=23
+    cimag; # introduced=23
+    cimagf; # introduced=23
+    cimagl; # introduced=23
+    conj; # introduced=23
+    conjf; # introduced=23
+    conjl; # introduced=23
     copysign;
     copysignf;
     copysignl;
@@ -69,62 +69,62 @@
     cosf;
     cosh;
     coshf;
-    coshl;
-    cosl;
-    cproj;
-    cprojf;
-    cprojl;
-    creal;
-    crealf;
-    creall;
-    csin;
-    csinf;
-    csinh;
-    csinhf;
-    csqrt;
-    csqrtf;
-    csqrtl;
-    ctan;
-    ctanf;
-    ctanh;
-    ctanhf;
+    coshl; # introduced=21
+    cosl; # introduced=21
+    cproj; # introduced=23
+    cprojf; # introduced=23
+    cprojl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    creal; # introduced=23
+    crealf; # introduced=23
+    creall; # introduced=23
+    csin; # introduced=23
+    csinf; # introduced=23
+    csinh; # introduced=23
+    csinhf; # introduced=23
+    csqrt; # introduced=23
+    csqrtf; # introduced=23
+    csqrtl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    ctan; # introduced=23
+    ctanf; # introduced=23
+    ctanh; # introduced=23
+    ctanhf; # introduced=23
     drem;
     dremf;
     erf;
     erfc;
     erfcf;
-    erfcl;
+    erfcl; # introduced=21
     erff;
-    erfl;
+    erfl; # introduced=21
     exp;
     exp2;
     exp2f;
-    exp2l;
+    exp2l; # introduced=21
     expf;
-    expl;
+    expl; # introduced=21
     expm1;
     expm1f;
-    expm1l;
+    expm1l; # introduced=21
     fabs;
     fabsf;
     fabsl;
     fdim;
     fdimf;
     fdiml;
-    feclearexcept;
-    fedisableexcept;
-    feenableexcept;
-    fegetenv;
-    fegetexcept;
-    fegetexceptflag;
-    fegetround;
-    feholdexcept;
-    feraiseexcept;
-    fesetenv;
-    fesetexceptflag;
-    fesetround;
-    fetestexcept;
-    feupdateenv;
+    feclearexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fedisableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feenableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feholdexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feraiseexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fetestexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feupdateenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
     finite;
     finitef;
     floor;
@@ -132,7 +132,7 @@
     floorl;
     fma;
     fmaf;
-    fmal;
+    fmal; # introduced=21
     fmax;
     fmaxf;
     fmaxl;
@@ -141,17 +141,17 @@
     fminl;
     fmod;
     fmodf;
-    fmodl;
+    fmodl; # introduced=21
     frexp;
     frexpf;
-    frexpl;
+    frexpl; # introduced=21
     gamma;
     gamma_r;
     gammaf;
     gammaf_r;
     hypot;
     hypotf;
-    hypotl;
+    hypotl; # introduced=21
     ilogb;
     ilogbf;
     ilogbl;
@@ -167,77 +167,77 @@
     lgamma_r;
     lgammaf;
     lgammaf_r;
-    lgammal;
-    lgammal_r;
+    lgammal; # introduced=21
+    lgammal_r; # introduced=23
     llrint;
     llrintf;
-    llrintl;
+    llrintl; # introduced=21
     llround;
     llroundf;
     llroundl;
     log;
     log10;
     log10f;
-    log10l;
+    log10l; # introduced=21
     log1p;
     log1pf;
-    log1pl;
-    log2;
-    log2f;
-    log2l;
+    log1pl; # introduced=21
+    log2; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2f; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2l; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logb;
     logbf;
-    logbl;
+    logbl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logf;
-    logl;
+    logl; # introduced=21
     lrint;
     lrintf;
-    lrintl;
+    lrintl; # introduced=21
     lround;
     lroundf;
     lroundl;
     modf;
     modff;
-    modfl;
-    nan;
-    nanf;
-    nanl;
+    modfl; # introduced=21
+    nan; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanl; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=13 introduced-x86_64=21
     nearbyint;
     nearbyintf;
-    nearbyintl;
+    nearbyintl; # introduced=21
     nextafter;
     nextafterf;
-    nextafterl;
-    nexttoward;
+    nextafterl; # introduced=21
+    nexttoward; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     nexttowardf;
-    nexttowardl;
+    nexttowardl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     pow;
     powf;
-    powl;
+    powl; # introduced=21
     remainder;
     remainderf;
-    remainderl;
+    remainderl; # introduced=21
     remquo;
     remquof;
-    remquol;
+    remquol; # introduced=21
     rint;
     rintf;
-    rintl;
+    rintl; # introduced=21
     round;
     roundf;
     roundl;
     scalb;
     scalbf;
-    scalbln;
-    scalblnf;
-    scalblnl;
+    scalbln; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnf; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnl; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     scalbn;
     scalbnf;
     scalbnl;
-    signgam;
+    signgam; # var
     significand;
     significandf;
-    significandl;
+    significandl; # introduced=21
     sin;
     sincos;
     sincosf;
@@ -245,20 +245,20 @@
     sinf;
     sinh;
     sinhf;
-    sinhl;
-    sinl;
+    sinhl; # introduced=21
+    sinl; # introduced=21
     sqrt;
     sqrtf;
-    sqrtl;
+    sqrtl; # introduced=21
     tan;
     tanf;
     tanh;
     tanhf;
-    tanhl;
-    tanl;
+    tanhl; # introduced=21
+    tanl; # introduced=21
     tgamma;
-    tgammaf;
-    tgammal;
+    tgammaf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    tgammal; # introduced=21
     trunc;
     truncf;
     truncl;
diff --git a/libm/libm.mips64.map b/libm/libm.mips64.map
index 11032ca..3e259dd 100644
--- a/libm/libm.mips64.map
+++ b/libm/libm.mips64.map
@@ -1,7 +1,7 @@
 # Generated by genversion-scripts.py. Do not edit.
 LIBC {
   global:
-    __fe_dfl_env;
+    __fe_dfl_env; # var
     __signbit;
     __signbitf;
     __signbitl;
@@ -9,59 +9,59 @@
     acosf;
     acosh;
     acoshf;
-    acoshl;
-    acosl;
+    acoshl; # introduced=21
+    acosl; # introduced=21
     asin;
     asinf;
     asinh;
     asinhf;
-    asinhl;
-    asinl;
+    asinhl; # introduced=21
+    asinl; # introduced=21
     atan;
     atan2;
     atan2f;
-    atan2l;
+    atan2l; # introduced=21
     atanf;
     atanh;
     atanhf;
-    atanhl;
-    atanl;
-    cabs;
-    cabsf;
-    cabsl;
-    cacos;
-    cacosf;
-    cacosh;
-    cacoshf;
-    carg;
-    cargf;
-    cargl;
-    casin;
-    casinf;
-    casinh;
-    casinhf;
-    catan;
-    catanf;
-    catanh;
-    catanhf;
+    atanhl; # introduced=21
+    atanl; # introduced=21
+    cabs; # introduced=23
+    cabsf; # introduced=23
+    cabsl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    cacos; # introduced=23
+    cacosf; # introduced=23
+    cacosh; # introduced=23
+    cacoshf; # introduced=23
+    carg; # introduced=23
+    cargf; # introduced=23
+    cargl; # introduced=23
+    casin; # introduced=23
+    casinf; # introduced=23
+    casinh; # introduced=23
+    casinhf; # introduced=23
+    catan; # introduced=23
+    catanf; # introduced=23
+    catanh; # introduced=23
+    catanhf; # introduced=23
     cbrt;
     cbrtf;
-    cbrtl;
-    ccos;
-    ccosf;
-    ccosh;
-    ccoshf;
+    cbrtl; # introduced=21
+    ccos; # introduced=23
+    ccosf; # introduced=23
+    ccosh; # introduced=23
+    ccoshf; # introduced=23
     ceil;
     ceilf;
     ceill;
-    cexp;
-    cexpf;
-    cimag;
-    cimagf;
-    cimagl;
-    conj;
-    conjf;
-    conjl;
+    cexp; # introduced=23
+    cexpf; # introduced=23
+    cimag; # introduced=23
+    cimagf; # introduced=23
+    cimagl; # introduced=23
+    conj; # introduced=23
+    conjf; # introduced=23
+    conjl; # introduced=23
     copysign;
     copysignf;
     copysignl;
@@ -69,62 +69,62 @@
     cosf;
     cosh;
     coshf;
-    coshl;
-    cosl;
-    cproj;
-    cprojf;
-    cprojl;
-    creal;
-    crealf;
-    creall;
-    csin;
-    csinf;
-    csinh;
-    csinhf;
-    csqrt;
-    csqrtf;
-    csqrtl;
-    ctan;
-    ctanf;
-    ctanh;
-    ctanhf;
+    coshl; # introduced=21
+    cosl; # introduced=21
+    cproj; # introduced=23
+    cprojf; # introduced=23
+    cprojl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    creal; # introduced=23
+    crealf; # introduced=23
+    creall; # introduced=23
+    csin; # introduced=23
+    csinf; # introduced=23
+    csinh; # introduced=23
+    csinhf; # introduced=23
+    csqrt; # introduced=23
+    csqrtf; # introduced=23
+    csqrtl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    ctan; # introduced=23
+    ctanf; # introduced=23
+    ctanh; # introduced=23
+    ctanhf; # introduced=23
     drem;
     dremf;
     erf;
     erfc;
     erfcf;
-    erfcl;
+    erfcl; # introduced=21
     erff;
-    erfl;
+    erfl; # introduced=21
     exp;
     exp2;
     exp2f;
-    exp2l;
+    exp2l; # introduced=21
     expf;
-    expl;
+    expl; # introduced=21
     expm1;
     expm1f;
-    expm1l;
+    expm1l; # introduced=21
     fabs;
     fabsf;
     fabsl;
     fdim;
     fdimf;
     fdiml;
-    feclearexcept;
-    fedisableexcept;
-    feenableexcept;
-    fegetenv;
-    fegetexcept;
-    fegetexceptflag;
-    fegetround;
-    feholdexcept;
-    feraiseexcept;
-    fesetenv;
-    fesetexceptflag;
-    fesetround;
-    fetestexcept;
-    feupdateenv;
+    feclearexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fedisableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feenableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feholdexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feraiseexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fetestexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feupdateenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
     finite;
     finitef;
     floor;
@@ -132,7 +132,7 @@
     floorl;
     fma;
     fmaf;
-    fmal;
+    fmal; # introduced=21
     fmax;
     fmaxf;
     fmaxl;
@@ -141,17 +141,17 @@
     fminl;
     fmod;
     fmodf;
-    fmodl;
+    fmodl; # introduced=21
     frexp;
     frexpf;
-    frexpl;
+    frexpl; # introduced=21
     gamma;
     gamma_r;
     gammaf;
     gammaf_r;
     hypot;
     hypotf;
-    hypotl;
+    hypotl; # introduced=21
     ilogb;
     ilogbf;
     ilogbl;
@@ -167,77 +167,77 @@
     lgamma_r;
     lgammaf;
     lgammaf_r;
-    lgammal;
-    lgammal_r;
+    lgammal; # introduced=21
+    lgammal_r; # introduced=23
     llrint;
     llrintf;
-    llrintl;
+    llrintl; # introduced=21
     llround;
     llroundf;
     llroundl;
     log;
     log10;
     log10f;
-    log10l;
+    log10l; # introduced=21
     log1p;
     log1pf;
-    log1pl;
-    log2;
-    log2f;
-    log2l;
+    log1pl; # introduced=21
+    log2; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2f; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2l; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logb;
     logbf;
-    logbl;
+    logbl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logf;
-    logl;
+    logl; # introduced=21
     lrint;
     lrintf;
-    lrintl;
+    lrintl; # introduced=21
     lround;
     lroundf;
     lroundl;
     modf;
     modff;
-    modfl;
-    nan;
-    nanf;
-    nanl;
+    modfl; # introduced=21
+    nan; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanl; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=13 introduced-x86_64=21
     nearbyint;
     nearbyintf;
-    nearbyintl;
+    nearbyintl; # introduced=21
     nextafter;
     nextafterf;
-    nextafterl;
-    nexttoward;
+    nextafterl; # introduced=21
+    nexttoward; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     nexttowardf;
-    nexttowardl;
+    nexttowardl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     pow;
     powf;
-    powl;
+    powl; # introduced=21
     remainder;
     remainderf;
-    remainderl;
+    remainderl; # introduced=21
     remquo;
     remquof;
-    remquol;
+    remquol; # introduced=21
     rint;
     rintf;
-    rintl;
+    rintl; # introduced=21
     round;
     roundf;
     roundl;
     scalb;
     scalbf;
-    scalbln;
-    scalblnf;
-    scalblnl;
+    scalbln; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnf; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnl; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     scalbn;
     scalbnf;
     scalbnl;
-    signgam;
+    signgam; # var
     significand;
     significandf;
-    significandl;
+    significandl; # introduced=21
     sin;
     sincos;
     sincosf;
@@ -245,20 +245,20 @@
     sinf;
     sinh;
     sinhf;
-    sinhl;
-    sinl;
+    sinhl; # introduced=21
+    sinl; # introduced=21
     sqrt;
     sqrtf;
-    sqrtl;
+    sqrtl; # introduced=21
     tan;
     tanf;
     tanh;
     tanhf;
-    tanhl;
-    tanl;
+    tanhl; # introduced=21
+    tanl; # introduced=21
     tgamma;
-    tgammaf;
-    tgammal;
+    tgammaf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    tgammal; # introduced=21
     trunc;
     truncf;
     truncl;
diff --git a/libm/libm.x86.map b/libm/libm.x86.map
index 11032ca..3e259dd 100644
--- a/libm/libm.x86.map
+++ b/libm/libm.x86.map
@@ -1,7 +1,7 @@
 # Generated by genversion-scripts.py. Do not edit.
 LIBC {
   global:
-    __fe_dfl_env;
+    __fe_dfl_env; # var
     __signbit;
     __signbitf;
     __signbitl;
@@ -9,59 +9,59 @@
     acosf;
     acosh;
     acoshf;
-    acoshl;
-    acosl;
+    acoshl; # introduced=21
+    acosl; # introduced=21
     asin;
     asinf;
     asinh;
     asinhf;
-    asinhl;
-    asinl;
+    asinhl; # introduced=21
+    asinl; # introduced=21
     atan;
     atan2;
     atan2f;
-    atan2l;
+    atan2l; # introduced=21
     atanf;
     atanh;
     atanhf;
-    atanhl;
-    atanl;
-    cabs;
-    cabsf;
-    cabsl;
-    cacos;
-    cacosf;
-    cacosh;
-    cacoshf;
-    carg;
-    cargf;
-    cargl;
-    casin;
-    casinf;
-    casinh;
-    casinhf;
-    catan;
-    catanf;
-    catanh;
-    catanhf;
+    atanhl; # introduced=21
+    atanl; # introduced=21
+    cabs; # introduced=23
+    cabsf; # introduced=23
+    cabsl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    cacos; # introduced=23
+    cacosf; # introduced=23
+    cacosh; # introduced=23
+    cacoshf; # introduced=23
+    carg; # introduced=23
+    cargf; # introduced=23
+    cargl; # introduced=23
+    casin; # introduced=23
+    casinf; # introduced=23
+    casinh; # introduced=23
+    casinhf; # introduced=23
+    catan; # introduced=23
+    catanf; # introduced=23
+    catanh; # introduced=23
+    catanhf; # introduced=23
     cbrt;
     cbrtf;
-    cbrtl;
-    ccos;
-    ccosf;
-    ccosh;
-    ccoshf;
+    cbrtl; # introduced=21
+    ccos; # introduced=23
+    ccosf; # introduced=23
+    ccosh; # introduced=23
+    ccoshf; # introduced=23
     ceil;
     ceilf;
     ceill;
-    cexp;
-    cexpf;
-    cimag;
-    cimagf;
-    cimagl;
-    conj;
-    conjf;
-    conjl;
+    cexp; # introduced=23
+    cexpf; # introduced=23
+    cimag; # introduced=23
+    cimagf; # introduced=23
+    cimagl; # introduced=23
+    conj; # introduced=23
+    conjf; # introduced=23
+    conjl; # introduced=23
     copysign;
     copysignf;
     copysignl;
@@ -69,62 +69,62 @@
     cosf;
     cosh;
     coshf;
-    coshl;
-    cosl;
-    cproj;
-    cprojf;
-    cprojl;
-    creal;
-    crealf;
-    creall;
-    csin;
-    csinf;
-    csinh;
-    csinhf;
-    csqrt;
-    csqrtf;
-    csqrtl;
-    ctan;
-    ctanf;
-    ctanh;
-    ctanhf;
+    coshl; # introduced=21
+    cosl; # introduced=21
+    cproj; # introduced=23
+    cprojf; # introduced=23
+    cprojl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    creal; # introduced=23
+    crealf; # introduced=23
+    creall; # introduced=23
+    csin; # introduced=23
+    csinf; # introduced=23
+    csinh; # introduced=23
+    csinhf; # introduced=23
+    csqrt; # introduced=23
+    csqrtf; # introduced=23
+    csqrtl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    ctan; # introduced=23
+    ctanf; # introduced=23
+    ctanh; # introduced=23
+    ctanhf; # introduced=23
     drem;
     dremf;
     erf;
     erfc;
     erfcf;
-    erfcl;
+    erfcl; # introduced=21
     erff;
-    erfl;
+    erfl; # introduced=21
     exp;
     exp2;
     exp2f;
-    exp2l;
+    exp2l; # introduced=21
     expf;
-    expl;
+    expl; # introduced=21
     expm1;
     expm1f;
-    expm1l;
+    expm1l; # introduced=21
     fabs;
     fabsf;
     fabsl;
     fdim;
     fdimf;
     fdiml;
-    feclearexcept;
-    fedisableexcept;
-    feenableexcept;
-    fegetenv;
-    fegetexcept;
-    fegetexceptflag;
-    fegetround;
-    feholdexcept;
-    feraiseexcept;
-    fesetenv;
-    fesetexceptflag;
-    fesetround;
-    fetestexcept;
-    feupdateenv;
+    feclearexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fedisableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feenableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feholdexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feraiseexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fetestexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feupdateenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
     finite;
     finitef;
     floor;
@@ -132,7 +132,7 @@
     floorl;
     fma;
     fmaf;
-    fmal;
+    fmal; # introduced=21
     fmax;
     fmaxf;
     fmaxl;
@@ -141,17 +141,17 @@
     fminl;
     fmod;
     fmodf;
-    fmodl;
+    fmodl; # introduced=21
     frexp;
     frexpf;
-    frexpl;
+    frexpl; # introduced=21
     gamma;
     gamma_r;
     gammaf;
     gammaf_r;
     hypot;
     hypotf;
-    hypotl;
+    hypotl; # introduced=21
     ilogb;
     ilogbf;
     ilogbl;
@@ -167,77 +167,77 @@
     lgamma_r;
     lgammaf;
     lgammaf_r;
-    lgammal;
-    lgammal_r;
+    lgammal; # introduced=21
+    lgammal_r; # introduced=23
     llrint;
     llrintf;
-    llrintl;
+    llrintl; # introduced=21
     llround;
     llroundf;
     llroundl;
     log;
     log10;
     log10f;
-    log10l;
+    log10l; # introduced=21
     log1p;
     log1pf;
-    log1pl;
-    log2;
-    log2f;
-    log2l;
+    log1pl; # introduced=21
+    log2; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2f; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2l; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logb;
     logbf;
-    logbl;
+    logbl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logf;
-    logl;
+    logl; # introduced=21
     lrint;
     lrintf;
-    lrintl;
+    lrintl; # introduced=21
     lround;
     lroundf;
     lroundl;
     modf;
     modff;
-    modfl;
-    nan;
-    nanf;
-    nanl;
+    modfl; # introduced=21
+    nan; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanl; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=13 introduced-x86_64=21
     nearbyint;
     nearbyintf;
-    nearbyintl;
+    nearbyintl; # introduced=21
     nextafter;
     nextafterf;
-    nextafterl;
-    nexttoward;
+    nextafterl; # introduced=21
+    nexttoward; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     nexttowardf;
-    nexttowardl;
+    nexttowardl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     pow;
     powf;
-    powl;
+    powl; # introduced=21
     remainder;
     remainderf;
-    remainderl;
+    remainderl; # introduced=21
     remquo;
     remquof;
-    remquol;
+    remquol; # introduced=21
     rint;
     rintf;
-    rintl;
+    rintl; # introduced=21
     round;
     roundf;
     roundl;
     scalb;
     scalbf;
-    scalbln;
-    scalblnf;
-    scalblnl;
+    scalbln; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnf; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnl; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     scalbn;
     scalbnf;
     scalbnl;
-    signgam;
+    signgam; # var
     significand;
     significandf;
-    significandl;
+    significandl; # introduced=21
     sin;
     sincos;
     sincosf;
@@ -245,20 +245,20 @@
     sinf;
     sinh;
     sinhf;
-    sinhl;
-    sinl;
+    sinhl; # introduced=21
+    sinl; # introduced=21
     sqrt;
     sqrtf;
-    sqrtl;
+    sqrtl; # introduced=21
     tan;
     tanf;
     tanh;
     tanhf;
-    tanhl;
-    tanl;
+    tanhl; # introduced=21
+    tanl; # introduced=21
     tgamma;
-    tgammaf;
-    tgammal;
+    tgammaf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    tgammal; # introduced=21
     trunc;
     truncf;
     truncl;
diff --git a/libm/libm.x86_64.map b/libm/libm.x86_64.map
index 11032ca..3e259dd 100644
--- a/libm/libm.x86_64.map
+++ b/libm/libm.x86_64.map
@@ -1,7 +1,7 @@
 # Generated by genversion-scripts.py. Do not edit.
 LIBC {
   global:
-    __fe_dfl_env;
+    __fe_dfl_env; # var
     __signbit;
     __signbitf;
     __signbitl;
@@ -9,59 +9,59 @@
     acosf;
     acosh;
     acoshf;
-    acoshl;
-    acosl;
+    acoshl; # introduced=21
+    acosl; # introduced=21
     asin;
     asinf;
     asinh;
     asinhf;
-    asinhl;
-    asinl;
+    asinhl; # introduced=21
+    asinl; # introduced=21
     atan;
     atan2;
     atan2f;
-    atan2l;
+    atan2l; # introduced=21
     atanf;
     atanh;
     atanhf;
-    atanhl;
-    atanl;
-    cabs;
-    cabsf;
-    cabsl;
-    cacos;
-    cacosf;
-    cacosh;
-    cacoshf;
-    carg;
-    cargf;
-    cargl;
-    casin;
-    casinf;
-    casinh;
-    casinhf;
-    catan;
-    catanf;
-    catanh;
-    catanhf;
+    atanhl; # introduced=21
+    atanl; # introduced=21
+    cabs; # introduced=23
+    cabsf; # introduced=23
+    cabsl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    cacos; # introduced=23
+    cacosf; # introduced=23
+    cacosh; # introduced=23
+    cacoshf; # introduced=23
+    carg; # introduced=23
+    cargf; # introduced=23
+    cargl; # introduced=23
+    casin; # introduced=23
+    casinf; # introduced=23
+    casinh; # introduced=23
+    casinhf; # introduced=23
+    catan; # introduced=23
+    catanf; # introduced=23
+    catanh; # introduced=23
+    catanhf; # introduced=23
     cbrt;
     cbrtf;
-    cbrtl;
-    ccos;
-    ccosf;
-    ccosh;
-    ccoshf;
+    cbrtl; # introduced=21
+    ccos; # introduced=23
+    ccosf; # introduced=23
+    ccosh; # introduced=23
+    ccoshf; # introduced=23
     ceil;
     ceilf;
     ceill;
-    cexp;
-    cexpf;
-    cimag;
-    cimagf;
-    cimagl;
-    conj;
-    conjf;
-    conjl;
+    cexp; # introduced=23
+    cexpf; # introduced=23
+    cimag; # introduced=23
+    cimagf; # introduced=23
+    cimagl; # introduced=23
+    conj; # introduced=23
+    conjf; # introduced=23
+    conjl; # introduced=23
     copysign;
     copysignf;
     copysignl;
@@ -69,62 +69,62 @@
     cosf;
     cosh;
     coshf;
-    coshl;
-    cosl;
-    cproj;
-    cprojf;
-    cprojl;
-    creal;
-    crealf;
-    creall;
-    csin;
-    csinf;
-    csinh;
-    csinhf;
-    csqrt;
-    csqrtf;
-    csqrtl;
-    ctan;
-    ctanf;
-    ctanh;
-    ctanhf;
+    coshl; # introduced=21
+    cosl; # introduced=21
+    cproj; # introduced=23
+    cprojf; # introduced=23
+    cprojl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    creal; # introduced=23
+    crealf; # introduced=23
+    creall; # introduced=23
+    csin; # introduced=23
+    csinf; # introduced=23
+    csinh; # introduced=23
+    csinhf; # introduced=23
+    csqrt; # introduced=23
+    csqrtf; # introduced=23
+    csqrtl; # introduced-arm=21 introduced-arm64=23 introduced-mips=21 introduced-mips64=23 introduced-x86=21 introduced-x86_64=23
+    ctan; # introduced=23
+    ctanf; # introduced=23
+    ctanh; # introduced=23
+    ctanhf; # introduced=23
     drem;
     dremf;
     erf;
     erfc;
     erfcf;
-    erfcl;
+    erfcl; # introduced=21
     erff;
-    erfl;
+    erfl; # introduced=21
     exp;
     exp2;
     exp2f;
-    exp2l;
+    exp2l; # introduced=21
     expf;
-    expl;
+    expl; # introduced=21
     expm1;
     expm1f;
-    expm1l;
+    expm1l; # introduced=21
     fabs;
     fabsf;
     fabsl;
     fdim;
     fdimf;
     fdiml;
-    feclearexcept;
-    fedisableexcept;
-    feenableexcept;
-    fegetenv;
-    fegetexcept;
-    fegetexceptflag;
-    fegetround;
-    feholdexcept;
-    feraiseexcept;
-    fesetenv;
-    fesetexceptflag;
-    fesetround;
-    fetestexcept;
-    feupdateenv;
+    feclearexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fedisableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feenableexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fegetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feholdexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feraiseexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetexceptflag; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fesetround; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    fetestexcept; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    feupdateenv; # introduced-arm=21 introduced-arm64=21 introduced-mips=21 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
     finite;
     finitef;
     floor;
@@ -132,7 +132,7 @@
     floorl;
     fma;
     fmaf;
-    fmal;
+    fmal; # introduced=21
     fmax;
     fmaxf;
     fmaxl;
@@ -141,17 +141,17 @@
     fminl;
     fmod;
     fmodf;
-    fmodl;
+    fmodl; # introduced=21
     frexp;
     frexpf;
-    frexpl;
+    frexpl; # introduced=21
     gamma;
     gamma_r;
     gammaf;
     gammaf_r;
     hypot;
     hypotf;
-    hypotl;
+    hypotl; # introduced=21
     ilogb;
     ilogbf;
     ilogbl;
@@ -167,77 +167,77 @@
     lgamma_r;
     lgammaf;
     lgammaf_r;
-    lgammal;
-    lgammal_r;
+    lgammal; # introduced=21
+    lgammal_r; # introduced=23
     llrint;
     llrintf;
-    llrintl;
+    llrintl; # introduced=21
     llround;
     llroundf;
     llroundl;
     log;
     log10;
     log10f;
-    log10l;
+    log10l; # introduced=21
     log1p;
     log1pf;
-    log1pl;
-    log2;
-    log2f;
-    log2l;
+    log1pl; # introduced=21
+    log2; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2f; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    log2l; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logb;
     logbf;
-    logbl;
+    logbl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     logf;
-    logl;
+    logl; # introduced=21
     lrint;
     lrintf;
-    lrintl;
+    lrintl; # introduced=21
     lround;
     lroundf;
     lroundl;
     modf;
     modff;
-    modfl;
-    nan;
-    nanf;
-    nanl;
+    modfl; # introduced=21
+    nan; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    nanl; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=13 introduced-x86_64=21
     nearbyint;
     nearbyintf;
-    nearbyintl;
+    nearbyintl; # introduced=21
     nextafter;
     nextafterf;
-    nextafterl;
-    nexttoward;
+    nextafterl; # introduced=21
+    nexttoward; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     nexttowardf;
-    nexttowardl;
+    nexttowardl; # introduced-arm=18 introduced-arm64=21 introduced-mips=18 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     pow;
     powf;
-    powl;
+    powl; # introduced=21
     remainder;
     remainderf;
-    remainderl;
+    remainderl; # introduced=21
     remquo;
     remquof;
-    remquol;
+    remquol; # introduced=21
     rint;
     rintf;
-    rintl;
+    rintl; # introduced=21
     round;
     roundf;
     roundl;
     scalb;
     scalbf;
-    scalbln;
-    scalblnf;
-    scalblnl;
+    scalbln; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnf; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
+    scalblnl; # introduced-arm=9 introduced-arm64=21 introduced-mips=9 introduced-mips64=21 introduced-x86=18 introduced-x86_64=21
     scalbn;
     scalbnf;
     scalbnl;
-    signgam;
+    signgam; # var
     significand;
     significandf;
-    significandl;
+    significandl; # introduced=21
     sin;
     sincos;
     sincosf;
@@ -245,20 +245,20 @@
     sinf;
     sinh;
     sinhf;
-    sinhl;
-    sinl;
+    sinhl; # introduced=21
+    sinl; # introduced=21
     sqrt;
     sqrtf;
-    sqrtl;
+    sqrtl; # introduced=21
     tan;
     tanf;
     tanh;
     tanhf;
-    tanhl;
-    tanl;
+    tanhl; # introduced=21
+    tanl; # introduced=21
     tgamma;
-    tgammaf;
-    tgammal;
+    tgammaf; # introduced-arm=13 introduced-arm64=21 introduced-mips=13 introduced-mips64=21 introduced-x86=9 introduced-x86_64=21
+    tgammal; # introduced=21
     trunc;
     truncf;
     truncl;
diff --git a/linker/Android.bp b/linker/Android.bp
index 39f1da9..4d770ac 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -22,6 +22,7 @@
         "linker_gdb_support.cpp",
         "linker_globals.cpp",
         "linker_libc_support.c",
+        "linker_main.cpp",
         "linker_namespaces.cpp",
         "linker_logger.cpp",
         "linker_mapped_file_fragment.cpp",
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 033aa8f..fab64bc 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -44,8 +44,6 @@
 #include <vector>
 
 // Private C library headers.
-#include "private/bionic_globals.h"
-#include "private/bionic_tls.h"
 #include "private/KernelArgumentBlock.h"
 #include "private/ScopedPthreadMutexLocker.h"
 #include "private/ScopeGuard.h"
@@ -56,6 +54,7 @@
 #include "linker_globals.h"
 #include "linker_debug.h"
 #include "linker_dlwarning.h"
+#include "linker_main.h"
 #include "linker_namespaces.h"
 #include "linker_sleb128.h"
 #include "linker_phdr.h"
@@ -65,7 +64,6 @@
 
 #include "android-base/strings.h"
 #include "android-base/stringprintf.h"
-#include "debuggerd/client.h"
 #include "ziparchive/zip_archive.h"
 
 extern void __libc_init_globals(KernelArgumentBlock&);
@@ -79,18 +77,12 @@
 
 static android_namespace_t* g_anonymous_namespace = &g_default_namespace;
 
-static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf);
-
 static LinkerTypeAllocator<soinfo> g_soinfo_allocator;
 static LinkerTypeAllocator<LinkedListEntry<soinfo>> g_soinfo_links_allocator;
 
 static LinkerTypeAllocator<android_namespace_t> g_namespace_allocator;
 static LinkerTypeAllocator<LinkedListEntry<android_namespace_t>> g_namespace_list_allocator;
 
-static soinfo* solist;
-static soinfo* sonext;
-static soinfo* somain; // main process, always the one after libdl_info
-
 #if defined(__LP64__)
 static const char* const kSystemLibDir     = "/system/lib64";
 static const char* const kVendorLibDir     = "/vendor/lib64";
@@ -193,14 +185,9 @@
 static const char* const* g_default_ld_paths;
 static std::vector<std::string> g_ld_preload_names;
 
-static std::vector<soinfo*> g_ld_preloads;
-
 static bool g_public_namespace_initialized;
 static soinfo_list_t g_public_namespace;
 
-int g_ld_debug_verbosity;
-abort_msg_t* g_abort_message = nullptr; // For debuggerd.
-
 #if STATS
 struct linker_stats_t {
   int count[kRelocMax];
@@ -259,19 +246,20 @@
   g_namespace_list_allocator.free(entry);
 }
 
-static soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
-                            struct stat* file_stat, off64_t file_offset,
-                            uint32_t rtld_flags) {
+soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
+                     struct stat* file_stat, off64_t file_offset,
+                     uint32_t rtld_flags) {
   if (strlen(name) >= PATH_MAX) {
     DL_ERR("library name \"%s\" too long", name);
     return nullptr;
   }
 
+  TRACE("name %s: allocating soinfo for ns=%p", name, ns);
+
   soinfo* si = new (g_soinfo_allocator.alloc()) soinfo(ns, name, file_stat,
                                                        file_offset, rtld_flags);
 
-  sonext->next = si;
-  sonext = si;
+  solist_add_soinfo(si);
 
   si->generate_handle();
   ns->add_soinfo(si);
@@ -295,33 +283,17 @@
     }
   }
 
-  soinfo *prev = nullptr, *trav;
-
   TRACE("name %s: freeing soinfo @ %p", si->get_realpath(), si);
 
-  for (trav = solist; trav != nullptr; trav = trav->next) {
-    if (trav == si) {
-      break;
-    }
-    prev = trav;
-  }
-
-  if (trav == nullptr) {
-    // si was not in solist
-    DL_ERR("name \"%s\"@%p is not in solist!", 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;
   }
 
   // clear links to/from si
   si->remove_all_links();
 
-  // prev will never be null, because the first entry in solist is
-  // always the static libdl_info.
-  prev->next = si->next;
-  if (si == sonext) {
-    sonext = prev;
-  }
-
   si->~soinfo();
   g_soinfo_allocator.free(si);
 }
@@ -339,17 +311,6 @@
   g_default_namespace.set_ld_library_paths(std::move(ld_libary_paths));
 }
 
-static void parse_LD_PRELOAD(const char* path) {
-  g_ld_preload_names.clear();
-  if (path != nullptr) {
-    // We have historically supported ':' as well as ' ' in LD_PRELOAD.
-    g_ld_preload_names = android::base::Split(path, " :");
-    std::remove_if(g_ld_preload_names.begin(),
-                   g_ld_preload_names.end(),
-                   [] (const std::string& s) { return s.empty(); });
-  }
-}
-
 static bool realpath_fd(int fd, std::string* realpath) {
   std::vector<char> buf(PATH_MAX), proc_self_fd(PATH_MAX);
   __libc_format_buffer(&proc_self_fd[0], proc_self_fd.size(), "/proc/self/fd/%d", fd);
@@ -375,7 +336,7 @@
 _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc, int* pcount) {
   uintptr_t addr = reinterpret_cast<uintptr_t>(pc);
 
-  for (soinfo* si = solist; si != 0; si = si->next) {
+  for (soinfo* si = solist_get_head(); si != 0; si = si->next) {
     if ((addr >= si->base) && (addr < (si->base + si->size))) {
         *pcount = si->ARM_exidx_count;
         return reinterpret_cast<_Unwind_Ptr>(si->ARM_exidx);
@@ -391,7 +352,7 @@
 // loaded libraries. gcc_eh does the rest.
 int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data) {
   int rv = 0;
-  for (soinfo* si = solist; si != nullptr; si = si->next) {
+  for (soinfo* si = solist_get_head(); si != nullptr; si = si->next) {
     dl_phdr_info dl_info;
     dl_info.dlpi_addr = si->link_map_head.l_addr;
     dl_info.dlpi_name = si->link_map_head.l_name;
@@ -499,33 +460,28 @@
   return true;
 }
 
-class ProtectedDataGuard {
- public:
-  ProtectedDataGuard() {
-    if (ref_count_++ == 0) {
-      protect_data(PROT_READ | PROT_WRITE);
-    }
+ProtectedDataGuard::ProtectedDataGuard() {
+  if (ref_count_++ == 0) {
+    protect_data(PROT_READ | PROT_WRITE);
+  }
+}
+
+ProtectedDataGuard::~ProtectedDataGuard() {
+  if (ref_count_ == 0) { // overflow
+    __libc_fatal("Too many nested calls to dlopen()");
   }
 
-  ~ProtectedDataGuard() {
-    if (ref_count_ == 0) { // overflow
-      __libc_fatal("Too many nested calls to dlopen()");
-    }
-
-    if (--ref_count_ == 0) {
-      protect_data(PROT_READ);
-    }
+  if (--ref_count_ == 0) {
+    protect_data(PROT_READ);
   }
- private:
-  void protect_data(int protection) {
-    g_soinfo_allocator.protect_all(protection);
-    g_soinfo_links_allocator.protect_all(protection);
-    g_namespace_allocator.protect_all(protection);
-    g_namespace_list_allocator.protect_all(protection);
-  }
+}
 
-  static size_t ref_count_;
-};
+void ProtectedDataGuard::protect_data(int protection) {
+  g_soinfo_allocator.protect_all(protection);
+  g_soinfo_links_allocator.protect_all(protection);
+  g_namespace_allocator.protect_all(protection);
+  g_namespace_list_allocator.protect_all(protection);
+}
 
 size_t ProtectedDataGuard::ref_count_ = 0;
 
@@ -782,7 +738,7 @@
   // Since RTLD_GLOBAL is always set for the main executable and all dt_needed shared
   // libraries and they are loaded in breath-first (correct) order we can just execute
   // dlsym(RTLD_DEFAULT, ...); instead of doing two stage lookup.
-  if (si == somain) {
+  if (si == solist_get_somain()) {
     return dlsym_linear_lookup(&g_default_namespace, name, vi, found, nullptr, RTLD_DEFAULT);
   }
 
@@ -855,7 +811,7 @@
 
 soinfo* find_containing_library(const void* p) {
   ElfW(Addr) address = reinterpret_cast<ElfW(Addr)>(p);
-  for (soinfo* si = solist; si != nullptr; si = si->next) {
+  for (soinfo* si = solist_get_head(); si != nullptr; si = si->next) {
     if (address >= si->base && address - si->base < si->size) {
       return si;
     }
@@ -1072,7 +1028,7 @@
   return fd;
 }
 
-static const char* fix_dt_needed(const char* dt_needed, const char* sopath __unused) {
+const char* fix_dt_needed(const char* dt_needed, const char* sopath __unused) {
 #if !defined(__LP64__)
   // Work around incorrect DT_NEEDED entries for old apps: http://b/21364029
   if (get_application_target_sdk_version() <= 22) {
@@ -1089,15 +1045,6 @@
 }
 
 template<typename F>
-static void for_each_dt_needed(const soinfo* si, F action) {
-  for (const ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
-    if (d->d_tag == DT_NEEDED) {
-      action(fix_dt_needed(si->get_string(d->d_un.d_val), si->get_realpath()));
-    }
-  }
-}
-
-template<typename F>
 static void for_each_dt_needed(const ElfReader& elf_reader, F action) {
   for (const ElfW(Dyn)* d = elf_reader.dynamic(); d->d_tag != DT_NULL; ++d) {
     if (d->d_tag == DT_NEEDED) {
@@ -1305,7 +1252,10 @@
       // "libdl.so" and global group. There is no point in skipping
       // them because relocation process is going to use them
       // in any case.
-      bool is_libdl = si == solist;
+
+      // TODO (dimitry): remove this once linker stops imposing as libdl.so
+      bool is_libdl = (si == solist_get_head());
+
       if (is_libdl || (si->get_dt_flags_1() & DF_1_GLOBAL) != 0 ||
           !si->is_linked() || si->get_target_sdk_version() == target_sdk_version ||
           ns != &g_default_namespace) {
@@ -1420,14 +1370,16 @@
 // not their transitive dependencies) as children of the start_with library.
 // This is false when find_libraries is called for dlopen(), when newly loaded
 // libraries must form a disjoint tree.
-static bool find_libraries(android_namespace_t* ns,
-                           soinfo* start_with,
-                           const char* const library_names[],
-                           size_t library_names_count, soinfo* soinfos[],
-                           std::vector<soinfo*>* ld_preloads,
-                           size_t ld_preloads_count, int rtld_flags,
-                           const android_dlextinfo* extinfo,
-                           bool add_as_children) {
+bool find_libraries(android_namespace_t* ns,
+                    soinfo* start_with,
+                    const char* const library_names[],
+                    size_t library_names_count,
+                    soinfo* soinfos[],
+                    std::vector<soinfo*>* ld_preloads,
+                    size_t ld_preloads_count,
+                    int rtld_flags,
+                    const android_dlextinfo* extinfo,
+                    bool add_as_children) {
   // Step 0: prepare.
   LoadTaskList load_tasks;
   std::unordered_map<const soinfo*, ElfReader> readers_map;
@@ -1592,7 +1544,7 @@
   soinfo* si;
 
   if (name == nullptr) {
-    si = somain;
+    si = solist_get_somain();
   } else if (!find_libraries(ns, needed_by, &name, 1, &si, nullptr, 0, rtld_flags,
                              extinfo, /* add_as_children */ false)) {
     return nullptr;
@@ -3081,7 +3033,9 @@
   // This workaround should keep them working. (applies only
   // for apps targeting sdk version <=22). Make an exception for
   // the main executable and linker; they do not need to have dt_soname
-  if (soname_ == nullptr && this != somain && (flags_ & FLAG_LINKER) == 0 &&
+  if (soname_ == nullptr &&
+      this != solist_get_somain() &&
+      (flags_ & FLAG_LINKER) == 0 &&
       get_application_target_sdk_version() <= 22) {
     soname_ = basename(realpath_.c_str());
     DL_WARN("%s: is missing DT_SONAME will use basename as a replacement: \"%s\"",
@@ -3245,66 +3199,12 @@
   return true;
 }
 
-/*
- * This function add vdso to internal dso list.
- * It helps to stack unwinding through signal handlers.
- * Also, it makes bionic more like glibc.
- */
-static void add_vdso(KernelArgumentBlock& args __unused) {
-#if defined(AT_SYSINFO_EHDR)
-  ElfW(Ehdr)* ehdr_vdso = reinterpret_cast<ElfW(Ehdr)*>(args.getauxval(AT_SYSINFO_EHDR));
-  if (ehdr_vdso == nullptr) {
-    return;
-  }
-
-  soinfo* si = soinfo_alloc(&g_default_namespace, "[vdso]", nullptr, 0, 0);
-
-  si->phdr = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<char*>(ehdr_vdso) + ehdr_vdso->e_phoff);
-  si->phnum = ehdr_vdso->e_phnum;
-  si->base = reinterpret_cast<ElfW(Addr)>(ehdr_vdso);
-  si->size = phdr_table_get_load_size(si->phdr, si->phnum);
-  si->load_bias = get_elf_exec_load_bias(ehdr_vdso);
-
-  si->prelink_image();
-  si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr);
-#endif
-}
-
-/* gdb expects the linker to be in the debug shared object list.
- * Without this, gdb has trouble locating the linker's ".text"
- * and ".plt" sections. Gdb could also potentially use this to
- * relocate the offset of our exported 'rtld_db_dlactivity' symbol.
- * Note that the linker shouldn't be on the soinfo list.
- */
-static void init_linker_info_for_gdb(ElfW(Addr) linker_base) {
-  static link_map linker_link_map_for_gdb;
-#if defined(__LP64__)
-  static char kLinkerPath[] = "/system/bin/linker64";
-#else
-  static char kLinkerPath[] = "/system/bin/linker";
-#endif
-
-  linker_link_map_for_gdb.l_addr = linker_base;
-  linker_link_map_for_gdb.l_name = kLinkerPath;
-
-  /*
-   * Set the dynamic field in the link map otherwise gdb will complain with
-   * the following:
-   *   warning: .dynamic section for "/system/bin/linker" is not at the
-   *   expected address (wrong library or version mismatch?)
-   */
-  ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_base);
-  ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_base + elf_hdr->e_phoff);
-  phdr_table_get_dynamic_section(phdr, elf_hdr->e_phnum, linker_base,
-                                 &linker_link_map_for_gdb.l_ld, nullptr);
-
-  insert_link_map_into_debug_map(&linker_link_map_for_gdb);
-}
-
-static void init_default_namespace() {
+void init_default_namespace() {
   g_default_namespace.set_name("(default)");
   g_default_namespace.set_isolated(false);
 
+  soinfo* somain = solist_get_somain();
+
   const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum,
                                                        somain->load_bias);
   const char* bname = basename(interp);
@@ -3323,359 +3223,3 @@
   g_default_namespace.set_default_library_paths(std::move(ld_default_paths));
 };
 
-extern "C" int __system_properties_init(void);
-
-static const char* get_executable_path() {
-  static std::string executable_path;
-  if (executable_path.empty()) {
-    char path[PATH_MAX];
-    ssize_t path_len = readlink("/proc/self/exe", path, sizeof(path));
-    if (path_len == -1 || path_len >= static_cast<ssize_t>(sizeof(path))) {
-      __libc_fatal("readlink('/proc/self/exe') failed: %s", strerror(errno));
-    }
-    executable_path = std::string(path, path_len);
-  }
-
-  return executable_path.c_str();
-}
-
-/*
- * This code is called after the linker has linked itself and
- * fixed it's own GOT. It is safe to make references to externs
- * and other non-local data at this point.
- */
-static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(Addr) linker_base) {
-#if TIMING
-  struct timeval t0, t1;
-  gettimeofday(&t0, 0);
-#endif
-
-  // Sanitize the environment.
-  __libc_init_AT_SECURE(args);
-
-  // Initialize system properties
-  __system_properties_init(); // may use 'environ'
-
-  // Register the debuggerd signal handler.
-  debuggerd_callbacks_t callbacks = {
-    .get_abort_message = []() {
-      return g_abort_message;
-    },
-    .post_dump = &notify_gdb_of_libraries,
-  };
-  debuggerd_init(&callbacks);
-
-  g_linker_logger.ResetState();
-
-  // Get a few environment variables.
-  const char* LD_DEBUG = getenv("LD_DEBUG");
-  if (LD_DEBUG != nullptr) {
-    g_ld_debug_verbosity = atoi(LD_DEBUG);
-  }
-
-#if defined(__LP64__)
-  INFO("[ Android dynamic linker (64-bit) ]");
-#else
-  INFO("[ Android dynamic linker (32-bit) ]");
-#endif
-
-  // These should have been sanitized by __libc_init_AT_SECURE, but the test
-  // doesn't cost us anything.
-  const char* ldpath_env = nullptr;
-  const char* ldpreload_env = nullptr;
-  if (!getauxval(AT_SECURE)) {
-    ldpath_env = getenv("LD_LIBRARY_PATH");
-    if (ldpath_env != nullptr) {
-      INFO("[ LD_LIBRARY_PATH set to \"%s\" ]", ldpath_env);
-    }
-    ldpreload_env = getenv("LD_PRELOAD");
-    if (ldpreload_env != nullptr) {
-      INFO("[ LD_PRELOAD set to \"%s\" ]", ldpreload_env);
-    }
-  }
-
-  struct stat file_stat;
-  // Stat "/proc/self/exe" instead of executable_path because
-  // the executable could be unlinked by this point and it should
-  // not cause a crash (see http://b/31084669)
-  if (TEMP_FAILURE_RETRY(stat("/proc/self/exe", &file_stat)) != 0) {
-    __libc_fatal("unable to stat \"/proc/self/exe\": %s", strerror(errno));
-  }
-
-  const char* executable_path = get_executable_path();
-  soinfo* si = soinfo_alloc(&g_default_namespace, executable_path, &file_stat, 0, RTLD_GLOBAL);
-  if (si == nullptr) {
-    __libc_fatal("Couldn't allocate soinfo: out of memory?");
-  }
-
-  /* bootstrap the link map, the main exe always needs to be first */
-  si->set_main_executable();
-  link_map* map = &(si->link_map_head);
-
-  // Register the main executable and the linker upfront to have
-  // gdb aware of them before loading the rest of the dependency
-  // tree.
-  map->l_addr = 0;
-  map->l_name = const_cast<char*>(executable_path);
-  insert_link_map_into_debug_map(map);
-  init_linker_info_for_gdb(linker_base);
-
-  // Extract information passed from the kernel.
-  si->phdr = reinterpret_cast<ElfW(Phdr)*>(args.getauxval(AT_PHDR));
-  si->phnum = args.getauxval(AT_PHNUM);
-
-  /* Compute the value of si->base. We can't rely on the fact that
-   * the first entry is the PHDR because this will not be true
-   * for certain executables (e.g. some in the NDK unit test suite)
-   */
-  si->base = 0;
-  si->size = phdr_table_get_load_size(si->phdr, si->phnum);
-  si->load_bias = 0;
-  for (size_t i = 0; i < si->phnum; ++i) {
-    if (si->phdr[i].p_type == PT_PHDR) {
-      si->load_bias = reinterpret_cast<ElfW(Addr)>(si->phdr) - si->phdr[i].p_vaddr;
-      si->base = reinterpret_cast<ElfW(Addr)>(si->phdr) - si->phdr[i].p_offset;
-      break;
-    }
-  }
-  si->dynamic = nullptr;
-
-  ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(si->base);
-  if (elf_hdr->e_type != ET_DYN) {
-    __libc_fatal("\"%s\": error: only position independent executables (PIE) are supported.",
-                 g_argv[0]);
-  }
-
-  // Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid).
-  parse_LD_LIBRARY_PATH(ldpath_env);
-  parse_LD_PRELOAD(ldpreload_env);
-
-  somain = si;
-
-  init_default_namespace();
-
-  if (!si->prelink_image()) {
-    __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
-  }
-
-  // add somain to global group
-  si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
-
-  // Load ld_preloads and dependencies.
-  StringLinkedList needed_library_name_list;
-  size_t needed_libraries_count = 0;
-  size_t ld_preloads_count = 0;
-
-  for (const auto& ld_preload_name : g_ld_preload_names) {
-    needed_library_name_list.push_back(ld_preload_name.c_str());
-    ++needed_libraries_count;
-    ++ld_preloads_count;
-  }
-
-  for_each_dt_needed(si, [&](const char* name) {
-    needed_library_name_list.push_back(name);
-    ++needed_libraries_count;
-  });
-
-  const char* needed_library_names[needed_libraries_count];
-
-  memset(needed_library_names, 0, sizeof(needed_library_names));
-  needed_library_name_list.copy_to_array(needed_library_names, needed_libraries_count);
-
-  if (needed_libraries_count > 0 &&
-      !find_libraries(&g_default_namespace, si, needed_library_names, needed_libraries_count,
-                      nullptr, &g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr,
-                      /* add_as_children */ true)) {
-    __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
-  } else if (needed_libraries_count == 0) {
-    if (!si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr)) {
-      __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
-    }
-    si->increment_ref_count();
-  }
-
-  add_vdso(args);
-
-  {
-    ProtectedDataGuard guard;
-
-    si->call_pre_init_constructors();
-
-    /* After the prelink_image, the si->load_bias is initialized.
-     * For so lib, the map->l_addr will be updated in notify_gdb_of_load.
-     * We need to update this value for so exe here. So Unwind_Backtrace
-     * for some arch like x86 could work correctly within so exe.
-     */
-    map->l_addr = si->load_bias;
-    si->call_constructors();
-  }
-
-#if TIMING
-  gettimeofday(&t1, nullptr);
-  PRINT("LINKER TIME: %s: %d microseconds", g_argv[0], (int) (
-           (((long long)t1.tv_sec * 1000000LL) + (long long)t1.tv_usec) -
-           (((long long)t0.tv_sec * 1000000LL) + (long long)t0.tv_usec)));
-#endif
-#if STATS
-  PRINT("RELO STATS: %s: %d abs, %d rel, %d copy, %d symbol", g_argv[0],
-         linker_stats.count[kRelocAbsolute],
-         linker_stats.count[kRelocRelative],
-         linker_stats.count[kRelocCopy],
-         linker_stats.count[kRelocSymbol]);
-#endif
-#if COUNT_PAGES
-  {
-    unsigned n;
-    unsigned i;
-    unsigned count = 0;
-    for (n = 0; n < 4096; n++) {
-      if (bitmask[n]) {
-        unsigned x = bitmask[n];
-#if defined(__LP64__)
-        for (i = 0; i < 32; i++) {
-#else
-        for (i = 0; i < 8; i++) {
-#endif
-          if (x & 1) {
-            count++;
-          }
-          x >>= 1;
-        }
-      }
-    }
-    PRINT("PAGES MODIFIED: %s: %d (%dKB)", g_argv[0], count, count * 4);
-  }
-#endif
-
-#if TIMING || STATS || COUNT_PAGES
-  fflush(stdout);
-#endif
-
-  ElfW(Addr) entry = args.getauxval(AT_ENTRY);
-  TRACE("[ Ready to execute \"%s\" @ %p ]", si->get_realpath(), reinterpret_cast<void*>(entry));
-  return entry;
-}
-
-/* Compute the load-bias of an existing executable. This shall only
- * be used to compute the load bias of an executable or shared library
- * that was loaded by the kernel itself.
- *
- * Input:
- *    elf    -> address of ELF header, assumed to be at the start of the file.
- * Return:
- *    load bias, i.e. add the value of any p_vaddr in the file to get
- *    the corresponding address in memory.
- */
-static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf) {
-  ElfW(Addr) offset = elf->e_phoff;
-  const ElfW(Phdr)* phdr_table =
-      reinterpret_cast<const ElfW(Phdr)*>(reinterpret_cast<uintptr_t>(elf) + offset);
-  const ElfW(Phdr)* phdr_end = phdr_table + elf->e_phnum;
-
-  for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_end; phdr++) {
-    if (phdr->p_type == PT_LOAD) {
-      return reinterpret_cast<ElfW(Addr)>(elf) + phdr->p_offset - phdr->p_vaddr;
-    }
-  }
-  return 0;
-}
-
-static void __linker_cannot_link(const char* argv0) {
-  __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", argv0, linker_get_error_buffer());
-}
-
-/*
- * This is the entry point for the linker, called from begin.S. This
- * method is responsible for fixing the linker's own relocations, and
- * then calling __linker_init_post_relocation().
- *
- * Because this method is called before the linker has fixed it's own
- * relocations, any attempt to reference an extern variable, extern
- * function, or other GOT reference will generate a segfault.
- */
-extern "C" ElfW(Addr) __linker_init(void* raw_args) {
-  KernelArgumentBlock args(raw_args);
-
-  ElfW(Addr) linker_addr = args.getauxval(AT_BASE);
-  ElfW(Addr) entry_point = args.getauxval(AT_ENTRY);
-  ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);
-  ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);
-
-  soinfo linker_so(nullptr, nullptr, nullptr, 0, 0);
-
-  // If the linker is not acting as PT_INTERP entry_point is equal to
-  // _start. Which means that the linker is running as an executable and
-  // already linked by PT_INTERP.
-  //
-  // This happens when user tries to run 'adb shell /system/bin/linker'
-  // see also https://code.google.com/p/android/issues/detail?id=63174
-  if (reinterpret_cast<ElfW(Addr)>(&_start) == entry_point) {
-    __libc_format_fd(STDOUT_FILENO,
-                     "This is %s, the helper program for shared library executables.\n",
-                     args.argv[0]);
-    exit(0);
-  }
-
-  linker_so.base = linker_addr;
-  linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);
-  linker_so.load_bias = get_elf_exec_load_bias(elf_hdr);
-  linker_so.dynamic = nullptr;
-  linker_so.phdr = phdr;
-  linker_so.phnum = elf_hdr->e_phnum;
-  linker_so.set_linker_flag();
-
-  // Prelink the linker so we can access linker globals.
-  if (!linker_so.prelink_image()) __linker_cannot_link(args.argv[0]);
-
-  // This might not be obvious... The reasons why we pass g_empty_list
-  // in place of local_group here are (1) we do not really need it, because
-  // linker is built with DT_SYMBOLIC and therefore relocates its symbols against
-  // itself without having to look into local_group and (2) allocators
-  // are not yet initialized, and therefore we cannot use linked_list.push_*
-  // functions at this point.
-  if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]);
-
-#if defined(__i386__)
-  // On x86, we can't make system calls before this point.
-  // We can't move this up because this needs to assign to a global.
-  // Note that until we call __libc_init_main_thread below we have
-  // no TLS, so you shouldn't make a system call that can fail, because
-  // it will SEGV when it tries to set errno.
-  __libc_init_sysinfo(args);
-#endif
-
-  // Initialize the main thread (including TLS, so system calls really work).
-  __libc_init_main_thread(args);
-
-  // We didn't protect the linker's RELRO pages in link_image because we
-  // couldn't make system calls on x86 at that point, but we can now...
-  if (!linker_so.protect_relro()) __linker_cannot_link(args.argv[0]);
-
-  // Initialize the linker's static libc's globals
-  __libc_init_globals(args);
-
-  // store argc/argv/envp to use them for calling constructors
-  g_argc = args.argc;
-  g_argv = args.argv;
-  g_envp = args.envp;
-
-  // Initialize the linker's own global variables
-  linker_so.call_constructors();
-
-  // Initialize static variables. Note that in order to
-  // get correct libdl_info we need to call constructors
-  // before get_libdl_info().
-  solist = get_libdl_info();
-  sonext = get_libdl_info();
-  g_default_namespace.add_soinfo(get_libdl_info());
-
-  // We have successfully fixed our own relocations. It's safe to run
-  // the main part of the linker now.
-  args.abort_message_ptr = &g_abort_message;
-  ElfW(Addr) start_address = __linker_init_post_relocation(args, linker_addr);
-
-  INFO("[ Jumping to _start (%p)... ]", reinterpret_cast<void*>(start_address));
-
-  // Return the address that the calling assembly stub should jump to.
-  return start_address;
-}
diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp
new file mode 100644
index 0000000..2e98bf0
--- /dev/null
+++ b/linker/linker_main.cpp
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2016 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 "linker_main.h"
+
+#include "linker_debug.h"
+#include "linker_gdb_support.h"
+#include "linker_globals.h"
+#include "linker_phdr.h"
+#include "linker_utils.h"
+
+#include "private/bionic_globals.h"
+#include "private/bionic_tls.h"
+#include "private/KernelArgumentBlock.h"
+
+#include "android-base/strings.h"
+#include "android-base/stringprintf.h"
+#include "debuggerd/client.h"
+
+#include <vector>
+
+extern void __libc_init_globals(KernelArgumentBlock&);
+extern void __libc_init_AT_SECURE(KernelArgumentBlock&);
+
+extern "C" void _start();
+
+static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf);
+
+// These should be preserved static to avoid emitting
+// RELATIVE relocations for the part of the code running
+// before linker links itself.
+
+// TODO (dimtiry): remove somain, rename solist to solist_head
+static soinfo* solist;
+static soinfo* sonext;
+static soinfo* somain; // main process, always the one after libdl_info
+
+void solist_add_soinfo(soinfo* si) {
+  sonext->next = si;
+  sonext = si;
+}
+
+bool solist_remove_soinfo(soinfo* si) {
+  soinfo *prev = nullptr, *trav;
+  for (trav = solist; trav != nullptr; trav = trav->next) {
+    if (trav == si) {
+      break;
+    }
+    prev = trav;
+  }
+
+  if (trav == nullptr) {
+    // si was not in solist
+    PRINT("name \"%s\"@%p is not in solist!", si->get_realpath(), si);
+    return false;
+  }
+
+  // prev will never be null, because the first entry in solist is
+  // always the static libdl_info.
+  prev->next = si->next;
+  if (si == sonext) {
+    sonext = prev;
+  }
+
+  return true;
+}
+
+soinfo* solist_get_head() {
+  return solist;
+}
+
+soinfo* solist_get_somain() {
+  return somain;
+}
+
+int g_ld_debug_verbosity;
+abort_msg_t* g_abort_message = nullptr; // For debuggerd.
+
+static std::vector<std::string> g_ld_preload_names;
+
+static std::vector<soinfo*> g_ld_preloads;
+
+static void parse_path(const char* path, const char* delimiters,
+                       std::vector<std::string>* resolved_paths) {
+  std::vector<std::string> paths;
+  split_path(path, delimiters, &paths);
+  resolve_paths(paths, resolved_paths);
+}
+
+static void parse_LD_LIBRARY_PATH(const char* path) {
+  std::vector<std::string> ld_libary_paths;
+  parse_path(path, ":", &ld_libary_paths);
+  g_default_namespace.set_ld_library_paths(std::move(ld_libary_paths));
+}
+
+static void parse_LD_PRELOAD(const char* path) {
+  g_ld_preload_names.clear();
+  if (path != nullptr) {
+    // We have historically supported ':' as well as ' ' in LD_PRELOAD.
+    g_ld_preload_names = android::base::Split(path, " :");
+    std::remove_if(g_ld_preload_names.begin(),
+                   g_ld_preload_names.end(),
+                   [] (const std::string& s) { return s.empty(); });
+  }
+}
+
+// An empty list of soinfos
+static soinfo_list_t g_empty_list;
+
+static void add_vdso(KernelArgumentBlock& args __unused) {
+#if defined(AT_SYSINFO_EHDR)
+  ElfW(Ehdr)* ehdr_vdso = reinterpret_cast<ElfW(Ehdr)*>(args.getauxval(AT_SYSINFO_EHDR));
+  if (ehdr_vdso == nullptr) {
+    return;
+  }
+
+  soinfo* si = soinfo_alloc(&g_default_namespace, "[vdso]", nullptr, 0, 0);
+
+  si->phdr = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<char*>(ehdr_vdso) + ehdr_vdso->e_phoff);
+  si->phnum = ehdr_vdso->e_phnum;
+  si->base = reinterpret_cast<ElfW(Addr)>(ehdr_vdso);
+  si->size = phdr_table_get_load_size(si->phdr, si->phnum);
+  si->load_bias = get_elf_exec_load_bias(ehdr_vdso);
+
+  si->prelink_image();
+  si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr);
+#endif
+}
+
+/* gdb expects the linker to be in the debug shared object list.
+ * Without this, gdb has trouble locating the linker's ".text"
+ * and ".plt" sections. Gdb could also potentially use this to
+ * relocate the offset of our exported 'rtld_db_dlactivity' symbol.
+ * Note that the linker shouldn't be on the soinfo list.
+ */
+static void init_linker_info_for_gdb(ElfW(Addr) linker_base) {
+  static link_map linker_link_map_for_gdb;
+#if defined(__LP64__)
+  static char kLinkerPath[] = "/system/bin/linker64";
+#else
+  static char kLinkerPath[] = "/system/bin/linker";
+#endif
+
+  linker_link_map_for_gdb.l_addr = linker_base;
+  linker_link_map_for_gdb.l_name = kLinkerPath;
+
+  /*
+   * Set the dynamic field in the link map otherwise gdb will complain with
+   * the following:
+   *   warning: .dynamic section for "/system/bin/linker" is not at the
+   *   expected address (wrong library or version mismatch?)
+   */
+  ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_base);
+  ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_base + elf_hdr->e_phoff);
+  phdr_table_get_dynamic_section(phdr, elf_hdr->e_phnum, linker_base,
+                                 &linker_link_map_for_gdb.l_ld, nullptr);
+
+  insert_link_map_into_debug_map(&linker_link_map_for_gdb);
+}
+
+extern "C" int __system_properties_init(void);
+
+static const char* get_executable_path() {
+  static std::string executable_path;
+  if (executable_path.empty()) {
+    char path[PATH_MAX];
+    ssize_t path_len = readlink("/proc/self/exe", path, sizeof(path));
+    if (path_len == -1 || path_len >= static_cast<ssize_t>(sizeof(path))) {
+      __libc_fatal("readlink('/proc/self/exe') failed: %s", strerror(errno));
+    }
+    executable_path = std::string(path, path_len);
+  }
+
+  return executable_path.c_str();
+}
+
+/*
+ * This code is called after the linker has linked itself and
+ * fixed it's own GOT. It is safe to make references to externs
+ * and other non-local data at this point.
+ */
+static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(Addr) linker_base) {
+#if TIMING
+  struct timeval t0, t1;
+  gettimeofday(&t0, 0);
+#endif
+
+  // Sanitize the environment.
+  __libc_init_AT_SECURE(args);
+
+  // Initialize system properties
+  __system_properties_init(); // may use 'environ'
+
+  // Register the debuggerd signal handler.
+  debuggerd_callbacks_t callbacks = {
+    .get_abort_message = []() {
+      return g_abort_message;
+    },
+    .post_dump = &notify_gdb_of_libraries,
+  };
+  debuggerd_init(&callbacks);
+
+  g_linker_logger.ResetState();
+
+  // Get a few environment variables.
+  const char* LD_DEBUG = getenv("LD_DEBUG");
+  if (LD_DEBUG != nullptr) {
+    g_ld_debug_verbosity = atoi(LD_DEBUG);
+  }
+
+#if defined(__LP64__)
+  INFO("[ Android dynamic linker (64-bit) ]");
+#else
+  INFO("[ Android dynamic linker (32-bit) ]");
+#endif
+
+  // These should have been sanitized by __libc_init_AT_SECURE, but the test
+  // doesn't cost us anything.
+  const char* ldpath_env = nullptr;
+  const char* ldpreload_env = nullptr;
+  if (!getauxval(AT_SECURE)) {
+    ldpath_env = getenv("LD_LIBRARY_PATH");
+    if (ldpath_env != nullptr) {
+      INFO("[ LD_LIBRARY_PATH set to \"%s\" ]", ldpath_env);
+    }
+    ldpreload_env = getenv("LD_PRELOAD");
+    if (ldpreload_env != nullptr) {
+      INFO("[ LD_PRELOAD set to \"%s\" ]", ldpreload_env);
+    }
+  }
+
+  struct stat file_stat;
+  // Stat "/proc/self/exe" instead of executable_path because
+  // the executable could be unlinked by this point and it should
+  // not cause a crash (see http://b/31084669)
+  if (TEMP_FAILURE_RETRY(stat("/proc/self/exe", &file_stat)) != 0) {
+    __libc_fatal("unable to stat \"/proc/self/exe\": %s", strerror(errno));
+  }
+
+  const char* executable_path = get_executable_path();
+  soinfo* si = soinfo_alloc(&g_default_namespace, executable_path, &file_stat, 0, RTLD_GLOBAL);
+  if (si == nullptr) {
+    __libc_fatal("Couldn't allocate soinfo: out of memory?");
+  }
+
+  /* bootstrap the link map, the main exe always needs to be first */
+  si->set_main_executable();
+  link_map* map = &(si->link_map_head);
+
+  // Register the main executable and the linker upfront to have
+  // gdb aware of them before loading the rest of the dependency
+  // tree.
+  map->l_addr = 0;
+  map->l_name = const_cast<char*>(executable_path);
+  insert_link_map_into_debug_map(map);
+  init_linker_info_for_gdb(linker_base);
+
+  // Extract information passed from the kernel.
+  si->phdr = reinterpret_cast<ElfW(Phdr)*>(args.getauxval(AT_PHDR));
+  si->phnum = args.getauxval(AT_PHNUM);
+
+  /* Compute the value of si->base. We can't rely on the fact that
+   * the first entry is the PHDR because this will not be true
+   * for certain executables (e.g. some in the NDK unit test suite)
+   */
+  si->base = 0;
+  si->size = phdr_table_get_load_size(si->phdr, si->phnum);
+  si->load_bias = 0;
+  for (size_t i = 0; i < si->phnum; ++i) {
+    if (si->phdr[i].p_type == PT_PHDR) {
+      si->load_bias = reinterpret_cast<ElfW(Addr)>(si->phdr) - si->phdr[i].p_vaddr;
+      si->base = reinterpret_cast<ElfW(Addr)>(si->phdr) - si->phdr[i].p_offset;
+      break;
+    }
+  }
+  si->dynamic = nullptr;
+
+  ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(si->base);
+  if (elf_hdr->e_type != ET_DYN) {
+    __libc_fatal("\"%s\": error: only position independent executables (PIE) are supported.",
+                 g_argv[0]);
+  }
+
+  // Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid).
+  parse_LD_LIBRARY_PATH(ldpath_env);
+  parse_LD_PRELOAD(ldpreload_env);
+
+  somain = si;
+
+  init_default_namespace();
+
+  if (!si->prelink_image()) {
+    __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
+  }
+
+  // add somain to global group
+  si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
+
+  // Load ld_preloads and dependencies.
+  std::vector<const char*> needed_library_name_list;
+  size_t ld_preloads_count = 0;
+
+  for (const auto& ld_preload_name : g_ld_preload_names) {
+    needed_library_name_list.push_back(ld_preload_name.c_str());
+    ++ld_preloads_count;
+  }
+
+  for_each_dt_needed(si, [&](const char* name) {
+    needed_library_name_list.push_back(name);
+  });
+
+  const char** needed_library_names = &needed_library_name_list[0];
+  size_t needed_libraries_count = needed_library_name_list.size();
+
+  if (needed_libraries_count > 0 &&
+      !find_libraries(&g_default_namespace, si, needed_library_names, needed_libraries_count,
+                      nullptr, &g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr,
+                      /* add_as_children */ true)) {
+    __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
+  } else if (needed_libraries_count == 0) {
+    if (!si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr)) {
+      __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer());
+    }
+    si->increment_ref_count();
+  }
+
+  add_vdso(args);
+
+  {
+    ProtectedDataGuard guard;
+
+    si->call_pre_init_constructors();
+
+    /* After the prelink_image, the si->load_bias is initialized.
+     * For so lib, the map->l_addr will be updated in notify_gdb_of_load.
+     * We need to update this value for so exe here. So Unwind_Backtrace
+     * for some arch like x86 could work correctly within so exe.
+     */
+    map->l_addr = si->load_bias;
+    si->call_constructors();
+  }
+
+#if TIMING
+  gettimeofday(&t1, nullptr);
+  PRINT("LINKER TIME: %s: %d microseconds", g_argv[0], (int) (
+           (((long long)t1.tv_sec * 1000000LL) + (long long)t1.tv_usec) -
+           (((long long)t0.tv_sec * 1000000LL) + (long long)t0.tv_usec)));
+#endif
+#if STATS
+  PRINT("RELO STATS: %s: %d abs, %d rel, %d copy, %d symbol", g_argv[0],
+         linker_stats.count[kRelocAbsolute],
+         linker_stats.count[kRelocRelative],
+         linker_stats.count[kRelocCopy],
+         linker_stats.count[kRelocSymbol]);
+#endif
+#if COUNT_PAGES
+  {
+    unsigned n;
+    unsigned i;
+    unsigned count = 0;
+    for (n = 0; n < 4096; n++) {
+      if (bitmask[n]) {
+        unsigned x = bitmask[n];
+#if defined(__LP64__)
+        for (i = 0; i < 32; i++) {
+#else
+        for (i = 0; i < 8; i++) {
+#endif
+          if (x & 1) {
+            count++;
+          }
+          x >>= 1;
+        }
+      }
+    }
+    PRINT("PAGES MODIFIED: %s: %d (%dKB)", g_argv[0], count, count * 4);
+  }
+#endif
+
+#if TIMING || STATS || COUNT_PAGES
+  fflush(stdout);
+#endif
+
+  ElfW(Addr) entry = args.getauxval(AT_ENTRY);
+  TRACE("[ Ready to execute \"%s\" @ %p ]", si->get_realpath(), reinterpret_cast<void*>(entry));
+  return entry;
+}
+
+/* Compute the load-bias of an existing executable. This shall only
+ * be used to compute the load bias of an executable or shared library
+ * that was loaded by the kernel itself.
+ *
+ * Input:
+ *    elf    -> address of ELF header, assumed to be at the start of the file.
+ * Return:
+ *    load bias, i.e. add the value of any p_vaddr in the file to get
+ *    the corresponding address in memory.
+ */
+static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf) {
+  ElfW(Addr) offset = elf->e_phoff;
+  const ElfW(Phdr)* phdr_table =
+      reinterpret_cast<const ElfW(Phdr)*>(reinterpret_cast<uintptr_t>(elf) + offset);
+  const ElfW(Phdr)* phdr_end = phdr_table + elf->e_phnum;
+
+  for (const ElfW(Phdr)* phdr = phdr_table; phdr < phdr_end; phdr++) {
+    if (phdr->p_type == PT_LOAD) {
+      return reinterpret_cast<ElfW(Addr)>(elf) + phdr->p_offset - phdr->p_vaddr;
+    }
+  }
+  return 0;
+}
+
+static void __linker_cannot_link(const char* argv0) {
+  __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", argv0, linker_get_error_buffer());
+}
+
+/*
+ * This is the entry point for the linker, called from begin.S. This
+ * method is responsible for fixing the linker's own relocations, and
+ * then calling __linker_init_post_relocation().
+ *
+ * Because this method is called before the linker has fixed it's own
+ * relocations, any attempt to reference an extern variable, extern
+ * function, or other GOT reference will generate a segfault.
+ */
+extern "C" ElfW(Addr) __linker_init(void* raw_args) {
+  KernelArgumentBlock args(raw_args);
+
+  ElfW(Addr) linker_addr = args.getauxval(AT_BASE);
+  ElfW(Addr) entry_point = args.getauxval(AT_ENTRY);
+  ElfW(Ehdr)* elf_hdr = reinterpret_cast<ElfW(Ehdr)*>(linker_addr);
+  ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(linker_addr + elf_hdr->e_phoff);
+
+  soinfo linker_so(nullptr, nullptr, nullptr, 0, 0);
+
+  // If the linker is not acting as PT_INTERP entry_point is equal to
+  // _start. Which means that the linker is running as an executable and
+  // already linked by PT_INTERP.
+  //
+  // This happens when user tries to run 'adb shell /system/bin/linker'
+  // see also https://code.google.com/p/android/issues/detail?id=63174
+  if (reinterpret_cast<ElfW(Addr)>(&_start) == entry_point) {
+    __libc_format_fd(STDOUT_FILENO,
+                     "This is %s, the helper program for shared library executables.\n",
+                     args.argv[0]);
+    exit(0);
+  }
+
+  linker_so.base = linker_addr;
+  linker_so.size = phdr_table_get_load_size(phdr, elf_hdr->e_phnum);
+  linker_so.load_bias = get_elf_exec_load_bias(elf_hdr);
+  linker_so.dynamic = nullptr;
+  linker_so.phdr = phdr;
+  linker_so.phnum = elf_hdr->e_phnum;
+  linker_so.set_linker_flag();
+
+  // Prelink the linker so we can access linker globals.
+  if (!linker_so.prelink_image()) __linker_cannot_link(args.argv[0]);
+
+  // This might not be obvious... The reasons why we pass g_empty_list
+  // in place of local_group here are (1) we do not really need it, because
+  // linker is built with DT_SYMBOLIC and therefore relocates its symbols against
+  // itself without having to look into local_group and (2) allocators
+  // are not yet initialized, and therefore we cannot use linked_list.push_*
+  // functions at this point.
+  if (!linker_so.link_image(g_empty_list, g_empty_list, nullptr)) __linker_cannot_link(args.argv[0]);
+
+#if defined(__i386__)
+  // On x86, we can't make system calls before this point.
+  // We can't move this up because this needs to assign to a global.
+  // Note that until we call __libc_init_main_thread below we have
+  // no TLS, so you shouldn't make a system call that can fail, because
+  // it will SEGV when it tries to set errno.
+  __libc_init_sysinfo(args);
+#endif
+
+  // Initialize the main thread (including TLS, so system calls really work).
+  __libc_init_main_thread(args);
+
+  // We didn't protect the linker's RELRO pages in link_image because we
+  // couldn't make system calls on x86 at that point, but we can now...
+  if (!linker_so.protect_relro()) __linker_cannot_link(args.argv[0]);
+
+  // Initialize the linker's static libc's globals
+  __libc_init_globals(args);
+
+  // store argc/argv/envp to use them for calling constructors
+  g_argc = args.argc;
+  g_argv = args.argv;
+  g_envp = args.envp;
+
+  // Initialize the linker's own global variables
+  linker_so.call_constructors();
+
+  // Initialize static variables. Note that in order to
+  // get correct libdl_info we need to call constructors
+  // before get_libdl_info().
+  solist = get_libdl_info();
+  sonext = get_libdl_info();
+  g_default_namespace.add_soinfo(get_libdl_info());
+
+  // We have successfully fixed our own relocations. It's safe to run
+  // the main part of the linker now.
+  args.abort_message_ptr = &g_abort_message;
+  ElfW(Addr) start_address = __linker_init_post_relocation(args, linker_addr);
+
+  INFO("[ Jumping to _start (%p)... ]", reinterpret_cast<void*>(start_address));
+
+  // Return the address that the calling assembly stub should jump to.
+  return start_address;
+}
diff --git a/linker/linker_main.h b/linker/linker_main.h
new file mode 100644
index 0000000..0030f49
--- /dev/null
+++ b/linker/linker_main.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef __LINKER_MAIN_H
+#define __LINKER_MAIN_H
+
+#include <android/dlext.h>
+
+#include "linker_namespaces.h"
+#include "linker_soinfo.h"
+
+class ProtectedDataGuard {
+ public:
+  ProtectedDataGuard();
+  ~ProtectedDataGuard();
+
+ private:
+  void protect_data(int protection);
+  static size_t ref_count_;
+};
+
+void init_default_namespace();
+soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
+                     struct stat* file_stat, off64_t file_offset,
+                     uint32_t rtld_flags);
+
+bool find_libraries(android_namespace_t* ns,
+                    soinfo* start_with,
+                    const char* const library_names[],
+                    size_t library_names_count,
+                    soinfo* soinfos[],
+                    std::vector<soinfo*>* ld_preloads,
+                    size_t ld_preloads_count,
+                    int rtld_flags,
+                    const android_dlextinfo* extinfo,
+                    bool add_as_children);
+
+void solist_add_soinfo(soinfo* si);
+bool solist_remove_soinfo(soinfo* si);
+soinfo* solist_get_head();
+soinfo* solist_get_somain();
+
+#endif
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index d7b584e..a0fe9e2 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -342,4 +342,15 @@
 // This function is used by dlvsym() to calculate hash of sym_ver
 uint32_t calculate_elf_hash(const char* name);
 
+const char* fix_dt_needed(const char* dt_needed, const char* sopath);
+
+template<typename F>
+void for_each_dt_needed(const soinfo* si, F action) {
+  for (const ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
+    if (d->d_tag == DT_NEEDED) {
+      action(fix_dt_needed(si->get_string(d->d_un.d_val), si->get_realpath()));
+    }
+  }
+}
+
 #endif  /* __LINKER_SOINFO_H */
diff --git a/linker/linker_utils.cpp b/linker/linker_utils.cpp
index e7447e4..05ac687 100644
--- a/linker/linker_utils.cpp
+++ b/linker/linker_utils.cpp
@@ -164,6 +164,11 @@
                    std::vector<std::string>* resolved_paths) {
   resolved_paths->clear();
   for (const auto& path : paths) {
+    // skip empty paths
+    if (path.empty()) {
+      continue;
+    }
+
     char resolved_path[PATH_MAX];
     const char* original_path = path.c_str();
     if (realpath(original_path, resolved_path) != nullptr) {
diff --git a/tests/Android.bp b/tests/Android.bp
index 0bbe4e3..ed42bde 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -151,6 +151,9 @@
         "libbase",
     ],
     host_ldlibs: ["-lrt"],
+    shared: {
+        enabled: false,
+    },
 }
 
 // -----------------------------------------------------------------------------
@@ -179,6 +182,9 @@
         "-D_FORTIFY_SOURCE=1",
         "-DTEST_NAME=Fortify1_gcc"
     ],
+    shared: {
+        enabled: false,
+    },
 }
 
 cc_test_library {
@@ -189,6 +195,9 @@
         "-D_FORTIFY_SOURCE=2",
         "-DTEST_NAME=Fortify2_gcc"
     ],
+    shared: {
+        enabled: false,
+    },
 }
 
 cc_test_library {
@@ -199,6 +208,9 @@
         "-D_FORTIFY_SOURCE=1",
         "-DTEST_NAME=Fortify1_clang"
     ],
+    shared: {
+        enabled: false,
+    },
 }
 
 cc_test_library {
@@ -209,6 +221,9 @@
         "-D_FORTIFY_SOURCE=2",
         "-DTEST_NAME=Fortify2_clang"
     ],
+    shared: {
+        enabled: false,
+    },
 }
 
 // -----------------------------------------------------------------------------
@@ -226,7 +241,7 @@
     ],
     shared: {
         enabled: false,
-    }
+    },
 }
 
 // -----------------------------------------------------------------------------
@@ -241,6 +256,9 @@
             enabled: true,
         },
     },
+    shared: {
+        enabled: false,
+    },
 }
 
 // -----------------------------------------------------------------------------
@@ -252,6 +270,9 @@
     defaults: ["bionic_tests_defaults"],
     srcs: ["gtest_main.cpp"],
     cppflags: ["-DUSING_GTEST_OUTPUT_FORMAT"],
+    shared: {
+        enabled: false,
+    },
 }
 
 // -----------------------------------------------------------------------------
@@ -313,6 +334,10 @@
                 "libz",
                 "libutils",
             ],
+            ldflags: [
+                "-Wl,--rpath,\\$${ORIGIN}/../bionic-loader-test-libs",
+                "-Wl,--enable-new-dtags",
+            ],
         },
     }
 }
diff --git a/tests/Android.build.prebuilt.mk b/tests/Android.build.prebuilt.mk
index db2dfe4..c98aee2 100644
--- a/tests/Android.build.prebuilt.mk
+++ b/tests/Android.build.prebuilt.mk
@@ -17,8 +17,8 @@
 include $(CLEAR_VARS)
 LOCAL_MULTILIB := both
 LOCAL_MODULE := $(bionic_tests_module)
-LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/prebuilt-elf-files
-LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/prebuilt-elf-files
+LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/bionic-loader-test-libs/prebuilt-elf-files
+LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/bionic-loader-test-libs/prebuilt-elf-files
 LOCAL_MODULE_CLASS := EXECUTABLES
 
 LOCAL_SRC_FILES_arm := prebuilt-elf-files/arm/$(bionic_tests_module)
@@ -28,3 +28,4 @@
 LOCAL_SRC_FILES_mips := prebuilt-elf-files/mips/$(bionic_tests_module)
 LOCAL_SRC_FILES_mips64 := prebuilt-elf-files/mips64/$(bionic_tests_module)
 include $(BUILD_PREBUILT)
+bionic-loader-test-libs-target: $(LOCAL_MODULE)
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index c3230e7..f597e61 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -55,9 +55,9 @@
 constexpr auto LIBSIZE = 1024 * 1024; // how much address space to reserve for it
 
 #if defined(__LP64__)
-#define NATIVE_TESTS_PATH "/nativetest64"
+#define NATIVE_TESTS_PATH "/nativetest64/bionic-loader-test-libs"
 #else
-#define NATIVE_TESTS_PATH "/nativetest"
+#define NATIVE_TESTS_PATH "/nativetest/bionic-loader-test-libs"
 #endif
 
 #define LIBPATH NATIVE_TESTS_PATH "/libdlext_test_fd/libdlext_test_fd.so"
@@ -150,11 +150,9 @@
 }
 
 TEST_F(DlExtTest, ExtInfoUseFdWithInvalidOffset) {
-  const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH;
   // lib_path is relative when $ANDROID_DATA is relative
-  char lib_realpath_buf[PATH_MAX];
-  ASSERT_TRUE(realpath(lib_path.c_str(), lib_realpath_buf) == lib_realpath_buf);
-  const std::string lib_realpath = std::string(lib_realpath_buf);
+  std::string lib_path;
+  ASSERT_TRUE(get_realpath(std::string(getenv("ANDROID_DATA")) + LIBZIPPATH, &lib_path)) << strerror(errno);
 
   android_dlextinfo extinfo;
   extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;
@@ -179,7 +177,7 @@
   extinfo.library_fd_offset = 0;
   handle_ = android_dlopen_ext("libname_ignored", RTLD_NOW, &extinfo);
   ASSERT_TRUE(handle_ == nullptr);
-  ASSERT_EQ("dlopen failed: \"" + lib_realpath + "\" has bad ELF magic", dlerror());
+  ASSERT_EQ("dlopen failed: \"" + lib_path + "\" has bad ELF magic", dlerror());
 
   // Check if dlsym works after unsuccessful dlopen().
   // Supply non-exiting one to make linker visit every soinfo.
@@ -235,7 +233,8 @@
 }
 
 TEST(dlfcn, dlopen_from_zip_absolute_path) {
-  const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH;
+  std::string lib_path;
+  ASSERT_TRUE(get_realpath(std::string(getenv("ANDROID_DATA")) + LIBZIPPATH, &lib_path)) << strerror(errno);
 
   void* handle = dlopen((lib_path + "!/libdir/libatest_simple_zip.so").c_str(), RTLD_NOW);
   ASSERT_TRUE(handle != nullptr) << dlerror();
@@ -248,7 +247,9 @@
 }
 
 TEST(dlfcn, dlopen_from_zip_with_dt_runpath) {
-  const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH_WITH_RUNPATH;
+  std::string data_path;
+  ASSERT_TRUE(get_realpath(std::string(getenv("ANDROID_DATA")), &data_path)) << strerror(errno);
+  const std::string lib_path = data_path + LIBZIPPATH_WITH_RUNPATH;
 
   void* handle = dlopen((lib_path + "!/libdir/libtest_dt_runpath_d_zip.so").c_str(), RTLD_NOW);
 
@@ -266,7 +267,9 @@
 }
 
 TEST(dlfcn, dlopen_from_zip_ld_library_path) {
-  const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH + "!/libdir";
+  std::string data_path;
+  ASSERT_TRUE(get_realpath(std::string(getenv("ANDROID_DATA")), &data_path)) << strerror(errno);
+  const std::string lib_path = data_path + LIBZIPPATH + "!/libdir";
 
   typedef void (*fn_t)(const char*);
   fn_t android_update_LD_LIBRARY_PATH =
@@ -1110,7 +1113,9 @@
   static const char* root_lib = "libnstest_root.so";
   std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib;
 
-  const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH;
+  std::string data_path;
+  ASSERT_TRUE(get_realpath(std::string(getenv("ANDROID_DATA")), &data_path)) << strerror(errno);
+  const std::string lib_path = data_path + NATIVE_TESTS_PATH;
 
   const std::string lib_public_path = lib_path + "/public_namespace_libs/" + g_public_lib;
   void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW);
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index f474ed9..34c4108 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -1129,8 +1129,20 @@
   dlclose(handle);
 }
 
+// Bionic specific tests
+#if defined(__BIONIC__)
+
+#if defined(__LP64__)
+#define NATIVE_TESTS_PATH "/nativetest64/bionic-loader-test-libs"
+#else
+#define NATIVE_TESTS_PATH "/nativetest/bionic-loader-test-libs"
+#endif
+
+#define PREBUILT_ELF_PATH NATIVE_TESTS_PATH "/prebuilt-elf-files"
+
 TEST(dlfcn, dt_runpath_absolute_path) {
-  void* handle = dlopen(PATH_TO_SYSTEM_LIB "libtest_dt_runpath_d.so", RTLD_NOW);
+  std::string libpath = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH + "/libtest_dt_runpath_d.so";
+  void* handle = dlopen(libpath.c_str(), RTLD_NOW);
   ASSERT_TRUE(handle != nullptr) << dlerror();
 
   typedef void *(* dlopen_b_fn)();
@@ -1143,19 +1155,10 @@
   dlclose(handle);
 }
 
-// Bionic specific tests
-#if defined(__BIONIC__)
-
-#if defined(__LP64__)
-#define NATIVE_TESTS_PATH "/nativetest64"
-#else
-#define NATIVE_TESTS_PATH "/nativetest"
-#endif
-
-#define PREBUILT_ELF_PATH NATIVE_TESTS_PATH "/prebuilt-elf-files"
-
 TEST(dlfcn, dlopen_invalid_rw_load_segment) {
-  std::string libpath = std::string(getenv("ANDROID_DATA")) + PREBUILT_ELF_PATH + "/libtest_invalid-rw_load_segment.so";
+  std::string data_path;
+  ASSERT_TRUE(get_realpath(std::string(getenv("ANDROID_DATA")), &data_path)) << strerror(errno);
+  const std::string libpath = data_path + PREBUILT_ELF_PATH + "/libtest_invalid-rw_load_segment.so";
   void* handle = dlopen(libpath.c_str(), RTLD_NOW);
   ASSERT_TRUE(handle == nullptr);
   std::string expected_dlerror = std::string("dlopen failed: \"") + libpath + "\": W + E load segments are not allowed";
@@ -1163,7 +1166,9 @@
 }
 
 TEST(dlfcn, dlopen_invalid_unaligned_shdr_offset) {
-  std::string libpath = std::string(getenv("ANDROID_DATA")) + PREBUILT_ELF_PATH + "/libtest_invalid-unaligned_shdr_offset.so";
+  std::string data_path;
+  ASSERT_TRUE(get_realpath(std::string(getenv("ANDROID_DATA")), &data_path)) << strerror(errno);
+  const std::string libpath = data_path + PREBUILT_ELF_PATH + "/libtest_invalid-unaligned_shdr_offset.so";
   void* handle = dlopen(libpath.c_str(), RTLD_NOW);
   ASSERT_TRUE(handle == nullptr);
   std::string expected_dlerror = std::string("dlopen failed: \"") + libpath + "\" has invalid shdr offset/size: ";
@@ -1171,7 +1176,9 @@
 }
 
 TEST(dlfcn, dlopen_invalid_zero_shentsize) {
-  std::string libpath = std::string(getenv("ANDROID_DATA")) + PREBUILT_ELF_PATH + "/libtest_invalid-zero_shentsize.so";
+  std::string data_path;
+  ASSERT_TRUE(get_realpath(std::string(getenv("ANDROID_DATA")), &data_path)) << strerror(errno);
+  const std::string libpath = data_path + PREBUILT_ELF_PATH + "/libtest_invalid-zero_shentsize.so";
   void* handle = dlopen(libpath.c_str(), RTLD_NOW);
   ASSERT_TRUE(handle == nullptr);
   std::string expected_dlerror = std::string("dlopen failed: \"") + libpath + "\" has unsupported e_shentsize: 0x0 (expected 0x";
@@ -1179,7 +1186,9 @@
 }
 
 TEST(dlfcn, dlopen_invalid_zero_shstrndx) {
-  std::string libpath = std::string(getenv("ANDROID_DATA")) + PREBUILT_ELF_PATH + "/libtest_invalid-zero_shstrndx.so";
+  std::string data_path;
+  ASSERT_TRUE(get_realpath(std::string(getenv("ANDROID_DATA")), &data_path)) << strerror(errno);
+  const std::string libpath = data_path + PREBUILT_ELF_PATH + "/libtest_invalid-zero_shstrndx.so";
   void* handle = dlopen(libpath.c_str(), RTLD_NOW);
   ASSERT_TRUE(handle == nullptr);
   std::string expected_dlerror = std::string("dlopen failed: \"") + libpath + "\" has invalid e_shstrndx";
@@ -1187,7 +1196,9 @@
 }
 
 TEST(dlfcn, dlopen_invalid_empty_shdr_table) {
-  std::string libpath = std::string(getenv("ANDROID_DATA")) + PREBUILT_ELF_PATH + "/libtest_invalid-empty_shdr_table.so";
+  std::string data_path;
+  ASSERT_TRUE(get_realpath(std::string(getenv("ANDROID_DATA")), &data_path)) << strerror(errno);
+  std::string libpath = data_path + PREBUILT_ELF_PATH + "/libtest_invalid-empty_shdr_table.so";
   void* handle = dlopen(libpath.c_str(), RTLD_NOW);
   ASSERT_TRUE(handle == nullptr);
   std::string expected_dlerror = std::string("dlopen failed: \"") + libpath + "\" has no section headers";
@@ -1195,7 +1206,9 @@
 }
 
 TEST(dlfcn, dlopen_invalid_zero_shdr_table_offset) {
-  std::string libpath = std::string(getenv("ANDROID_DATA")) + PREBUILT_ELF_PATH + "/libtest_invalid-zero_shdr_table_offset.so";
+  std::string data_path;
+  ASSERT_TRUE(get_realpath(std::string(getenv("ANDROID_DATA")), &data_path)) << strerror(errno);
+  const std::string libpath = data_path + PREBUILT_ELF_PATH + "/libtest_invalid-zero_shdr_table_offset.so";
   void* handle = dlopen(libpath.c_str(), RTLD_NOW);
   ASSERT_TRUE(handle == nullptr);
   std::string expected_dlerror = std::string("dlopen failed: \"") + libpath + "\" has invalid shdr offset/size: 0/";
@@ -1203,7 +1216,9 @@
 }
 
 TEST(dlfcn, dlopen_invalid_zero_shdr_table_content) {
-  std::string libpath = std::string(getenv("ANDROID_DATA")) + PREBUILT_ELF_PATH + "/libtest_invalid-zero_shdr_table_content.so";
+  std::string data_path;
+  ASSERT_TRUE(get_realpath(std::string(getenv("ANDROID_DATA")), &data_path)) << strerror(errno);
+  const std::string libpath = data_path + PREBUILT_ELF_PATH + "/libtest_invalid-zero_shdr_table_content.so";
   void* handle = dlopen(libpath.c_str(), RTLD_NOW);
   ASSERT_TRUE(handle == nullptr);
   std::string expected_dlerror = std::string("dlopen failed: \"") + libpath + "\" .dynamic section header was not found";
diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp
index 4abd37e..f2b7edd 100644
--- a/tests/gtest_main.cpp
+++ b/tests/gtest_main.cpp
@@ -55,6 +55,16 @@
   return g_executable_path;
 }
 
+bool get_realpath(const std::string& path, std::string* real_path) {
+  char realpath_buf[PATH_MAX];
+  if (realpath(path.c_str(), realpath_buf) != realpath_buf) {
+    return false;
+  }
+
+  *real_path = realpath_buf;
+  return true;
+}
+
 int get_argc() {
   return g_argc;
 }
diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp
index 0303138..8283633 100644
--- a/tests/libs/Android.bp
+++ b/tests/libs/Android.bp
@@ -17,6 +17,12 @@
 cc_defaults {
     name: "bionic_testlib_defaults",
     host_supported: true,
+    ldflags: [
+        "-Wl,--rpath,\\$${ORIGIN}",
+        "-Wl,--enable-new-dtags",
+    ],
+    relative_install_path: "bionic-loader-test-libs",
+    gtest: false,
     sanitize: {
         never: true,
     },
@@ -30,7 +36,7 @@
 // -----------------------------------------------------------------------------
 // Library to test gnu-styled hash
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libgnu-hash-table-library",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlext_test_library.cpp"],
@@ -48,7 +54,7 @@
 // -----------------------------------------------------------------------------
 // Library to test sysv-styled hash
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libsysv-hash-table-library",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlext_test_library.cpp"],
@@ -63,8 +69,9 @@
 // -----------------------------------------------------------------------------
 // Library used by dlext tests - without GNU RELRO program header
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libdlext_test_norelro",
+    defaults: ["bionic_testlib_defaults"],
     srcs: ["dlext_test_library.cpp"],
     ldflags: ["-Wl,-z,norelro"],
     shared_libs = ["libtest_simple"],
@@ -93,7 +100,7 @@
 // -----------------------------------------------------------------------------
 // Library used by dlfcn tests
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_simple",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_testlib_simple.cpp"],
@@ -102,7 +109,7 @@
 // -----------------------------------------------------------------------------
 // Library used by dlfcn nodelete tests
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_nodelete_1",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_nodelete_1.cpp"],
@@ -111,7 +118,7 @@
 // -----------------------------------------------------------------------------
 // Library used by dlfcn nodelete tests
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_nodelete_2",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_nodelete_2.cpp"],
@@ -120,7 +127,7 @@
 // -----------------------------------------------------------------------------
 // Library used by dlfcn nodelete tests
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_nodelete_dt_flags_1",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_nodelete_dt_flags_1.cpp"],
@@ -172,7 +179,7 @@
 //
 // libtest_with_dependency_loop -> a -> b -> c -> a
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_with_dependency_loop",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_testlib_loopy_root.cpp"],
@@ -182,7 +189,7 @@
 // -----------------------------------------------------------------------------
 // libtest_with_dependency_loop_a.so
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_with_dependency_loop_a",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_testlib_loopy_a.cpp"],
@@ -194,7 +201,7 @@
 //
 // this is temporary placeholder - will be removed
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_with_dependency_loop_b_tmp",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_testlib_loopy_invalid.cpp"],
@@ -204,7 +211,7 @@
 // -----------------------------------------------------------------------------
 // libtest_with_dependency_loop_b.so
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_with_dependency_loop_b",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_testlib_loopy_b.cpp"],
@@ -214,7 +221,7 @@
 // -----------------------------------------------------------------------------
 // libtest_with_dependency_loop_c.so
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_with_dependency_loop_c",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_testlib_loopy_c.cpp"],
@@ -230,7 +237,7 @@
 // -----------------------------------------------------------------------------
 
 
-cc_library {
+cc_test_library {
     name: "libtest_relo_check_dt_needed_order",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_testlib_relo_check_dt_needed_order.cpp"],
@@ -240,13 +247,13 @@
     ],
 }
 
-cc_library {
+cc_test_library {
     name: "libtest_relo_check_dt_needed_order_1",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_testlib_relo_check_dt_needed_order_1.cpp"],
 }
 
-cc_library {
+cc_test_library {
     name: "libtest_relo_check_dt_needed_order_2",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_testlib_relo_check_dt_needed_order_2.cpp"],
@@ -260,7 +267,7 @@
 // -----------------------------------------------------------------------------
 // Library used by ifunc tests
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_ifunc",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_testlib_ifunc.c"],
@@ -281,7 +288,7 @@
 // Library used by atexit tests
 // -----------------------------------------------------------------------------
 
-cc_library {
+cc_test_library {
     name: "libtest_atexit",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["atexit_testlib.cpp"],
@@ -291,7 +298,7 @@
 // This library is used by dl_load test to check symbol preempting
 // by main executable
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libdl_preempt_test_1",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dl_preempt_library_1.cpp"],
@@ -301,7 +308,7 @@
 // This library is used by dl_load test to check symbol preempting
 // by libdl_preempt_test_1.so
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libdl_preempt_test_2",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dl_preempt_library_2.cpp"],
@@ -310,7 +317,7 @@
 // -----------------------------------------------------------------------------
 // Library with DF_1_GLOBAL
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libdl_test_df_1_global",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dl_df_1_global.cpp"],
@@ -328,7 +335,7 @@
 // -----------------------------------------------------------------------------
 // Library using symbol from libdl_test_df_1_global
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_dlsym_df_1_global",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dl_df_1_use_global.cpp"],
@@ -337,7 +344,7 @@
 // -----------------------------------------------------------------------------
 // Library with weak function
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_dlsym_weak_func",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlsym_weak_function.cpp"],
@@ -346,7 +353,7 @@
 // -----------------------------------------------------------------------------
 // Library to check RTLD_LOCAL with dlsym in 'this'
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_dlsym_from_this",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlsym_from_this_symbol.cpp"],
@@ -360,7 +367,7 @@
 }
 
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_dlsym_from_this_child",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlsym_from_this_functions.cpp"],
@@ -368,7 +375,7 @@
 }
 
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_dlsym_from_this_grandchild",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlsym_from_this_symbol2.cpp"],
@@ -377,7 +384,7 @@
 // -----------------------------------------------------------------------------
 // Empty library
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_empty",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["empty.cpp"],
@@ -386,7 +393,7 @@
 // -----------------------------------------------------------------------------
 // Library with weak undefined function
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_dlopen_weak_undefined_func",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_weak_undefined.cpp"],
@@ -395,7 +402,7 @@
 // -----------------------------------------------------------------------------
 // Library with constructor that calls dlopen() b/7941716
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_dlopen_from_ctor",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["dlopen_testlib_dlopen_from_ctor.cpp"],
@@ -409,7 +416,7 @@
 // -----------------------------------------------------------------------------
 // Library that depends on the library with constructor that calls dlopen() b/7941716
 // -----------------------------------------------------------------------------
-cc_library {
+cc_test_library {
     name: "libtest_dlopen_from_ctor_main",
     defaults: ["bionic_testlib_defaults"],
     srcs: ["empty.cpp"],
@@ -434,10 +441,4 @@
         "libz",
         "libutils",
     ],
-
-    target: {
-        windows: {
-            enabled: true,
-        },
-    },
 }
diff --git a/tests/libs/Android.build.dlext_testzip.mk b/tests/libs/Android.build.dlext_testzip.mk
index 56be1e2..37499ba 100644
--- a/tests/libs/Android.build.dlext_testzip.mk
+++ b/tests/libs/Android.build.dlext_testzip.mk
@@ -26,7 +26,7 @@
 LOCAL_MODULE := libdlext_test_zip_zipaligned
 LOCAL_MODULE_SUFFIX := .zip
 LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE_PATH := $($(bionic_2nd_arch_prefix)TARGET_OUT_DATA_NATIVE_TESTS)/libdlext_test_zip
+LOCAL_MODULE_PATH := $($(bionic_2nd_arch_prefix)TARGET_OUT_DATA_NATIVE_TESTS)/bionic-loader-test-libs/libdlext_test_zip
 LOCAL_2ND_ARCH_VAR_PREFIX := $(bionic_2nd_arch_prefix)
 
 include $(BUILD_SYSTEM)/base_rules.mk
@@ -48,7 +48,7 @@
 LOCAL_MODULE := libdlext_test_runpath_zip_zipaligned
 LOCAL_MODULE_SUFFIX := .zip
 LOCAL_MODULE_TAGS := tests
-LOCAL_MODULE_PATH := $($(bionic_2nd_arch_prefix)TARGET_OUT_DATA_NATIVE_TESTS)/libdlext_test_runpath_zip
+LOCAL_MODULE_PATH := $($(bionic_2nd_arch_prefix)TARGET_OUT_DATA_NATIVE_TESTS)/bionic-loader-test-libs/libdlext_test_runpath_zip
 LOCAL_2ND_ARCH_VAR_PREFIX := $(bionic_2nd_arch_prefix)
 
 include $(BUILD_SYSTEM)/base_rules.mk
diff --git a/tests/libs/Android.build.linker_namespaces.mk b/tests/libs/Android.build.linker_namespaces.mk
index f913780..df6428c 100644
--- a/tests/libs/Android.build.linker_namespaces.mk
+++ b/tests/libs/Android.build.linker_namespaces.mk
@@ -35,24 +35,24 @@
 # -----------------------------------------------------------------------------
 libnstest_root_src_files := namespaces_root.cpp
 libnstest_root_shared_libraries := libnstest_public libnstest_private
-libnstest_root_install_to_out_data_dir := private_namespace_libs
+libnstest_root_relative_install_path := private_namespace_libs
 module := libnstest_root
-include $(LOCAL_PATH)/Android.build.target.testlib.mk
+include $(LOCAL_PATH)/Android.build.testlib.target.mk
 
 libnstest_public_src_files := namespaces_public.cpp
 module := libnstest_public
-libnstest_public_install_to_out_data_dir := public_namespace_libs
-include $(LOCAL_PATH)/Android.build.target.testlib.mk
+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_install_to_out_data_dir := private_namespace_libs
+libnstest_private_relative_install_path := private_namespace_libs
 module := libnstest_private
-include $(LOCAL_PATH)/Android.build.target.testlib.mk
+include $(LOCAL_PATH)/Android.build.testlib.target.mk
 
 libnstest_dlopened_src_files := namespaces_dlopened.cpp
-libnstest_dlopened_install_to_out_data_dir := private_namespace_libs
+libnstest_dlopened_relative_install_path := private_namespace_libs
 module := libnstest_dlopened
-include $(LOCAL_PATH)/Android.build.target.testlib.mk
+include $(LOCAL_PATH)/Android.build.testlib.target.mk
 
 # -----------------------------------------------------------------------------
 # This set of libraries is to test isolated namespaces
@@ -71,14 +71,14 @@
 # -----------------------------------------------------------------------------
 libnstest_root_not_isolated_src_files := namespaces_root.cpp
 libnstest_root_not_isolated_shared_libraries := libnstest_public libnstest_private_external
-libnstest_root_not_isolated_install_to_out_data_dir := private_namespace_libs
+libnstest_root_not_isolated_relative_install_path := private_namespace_libs
 libnstest_root_not_isolated_ldflags := -Wl,--rpath,\$$ORIGIN/../private_namespace_libs_external \
                                        -Wl,--enable-new-dtags
 
 module := libnstest_root_not_isolated
-include $(LOCAL_PATH)/Android.build.target.testlib.mk
+include $(LOCAL_PATH)/Android.build.testlib.target.mk
 
 libnstest_private_external_src_files := namespaces_private.cpp
-libnstest_private_external_install_to_out_data_dir := private_namespace_libs_external
+libnstest_private_external_relative_install_path := private_namespace_libs_external
 module := libnstest_private_external
-include $(LOCAL_PATH)/Android.build.target.testlib.mk
+include $(LOCAL_PATH)/Android.build.testlib.target.mk
diff --git a/tests/libs/Android.build.testlib.mk b/tests/libs/Android.build.testlib.mk
index 5b688e4..407a389 100644
--- a/tests/libs/Android.build.testlib.mk
+++ b/tests/libs/Android.build.testlib.mk
@@ -17,6 +17,7 @@
 build_target := SHARED_LIBRARY
 build_type := host
 include $(TEST_PATH)/Android.build.mk
-build_type := target
-include $(TEST_PATH)/Android.build.mk
+bionic-loader-test-libs-host: $(LOCAL_MODULE)
+
+include $(LOCAL_PATH)/Android.build.testlib.target.mk
 
diff --git a/tests/libs/Android.build.testlib.target.mk b/tests/libs/Android.build.testlib.target.mk
new file mode 100644
index 0000000..143be5b
--- /dev/null
+++ b/tests/libs/Android.build.testlib.target.mk
@@ -0,0 +1,30 @@
+#
+# 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.
+#
+
+build_type := target
+# 1. Install test libraries to /data/nativetests../bionic-loader-test-libs/
+#    by default.
+ifeq ($($(module)_relative_install_path),)
+  $(module)_install_to_out_data_dir := bionic-loader-test-libs
+else
+  $(module)_install_to_out_data_dir := bionic-loader-test-libs/$($(module)_relative_install_path)
+endif
+# 2. Set dt_runpath to origin to resolve dependencies
+$(module)_ldflags += -Wl,--rpath,\$${ORIGIN} -Wl,--enable-new-dtags
+include $(TEST_PATH)/Android.build.mk
+
+bionic-loader-test-libs-target: $(LOCAL_MODULE)
+
diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk
index 3c3d78b..94cc516 100644
--- a/tests/libs/Android.mk
+++ b/tests/libs/Android.mk
@@ -29,6 +29,7 @@
     $(LOCAL_PATH)/Android.build.linker_namespaces.mk \
     $(LOCAL_PATH)/Android.build.pthread_atfork.mk \
     $(LOCAL_PATH)/Android.build.testlib.mk \
+    $(LOCAL_PATH)/Android.build.testlib.target.mk \
     $(LOCAL_PATH)/Android.build.versioned_lib.mk \
     $(TEST_PATH)/Android.build.mk
 
@@ -51,12 +52,12 @@
 # create symlink to libdlext_test.so for symlink test
 # -----------------------------------------------------------------------------
 # Use = instead of := to defer the evaluation of $@
-$(TARGET_OUT)/lib/libdlext_test.so: PRIVATE_POST_INSTALL_CMD = \
+$(TARGET_OUT_DATA_NATIVE_TESTS)/bionic-loader-test-libs/libdlext_test.so: PRIVATE_POST_INSTALL_CMD = \
     $(hide) cd $(dir $@) && ln -sf $(notdir $@) libdlext_test_v2.so
 
 ifneq ($(TARGET_2ND_ARCH),)
 # link 64 bit .so
-$(TARGET_OUT)/lib64/libdlext_test.so: PRIVATE_POST_INSTALL_CMD = \
+$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/bionic-loader-test-libs/libdlext_test.so: PRIVATE_POST_INSTALL_CMD = \
     $(hide) cd $(dir $@) && ln -sf $(notdir $@) libdlext_test_v2.so
 endif
 
@@ -77,11 +78,12 @@
 
 libdlext_test_fd_shared_libraries := libtest_simple
 
-libdlext_test_fd_install_to_out_data_dir := $(module)
+libdlext_test_fd_relative_install_path := $(module)
+
+libdlext_test_fd_ldflags := -Wl,--rpath,\$${ORIGIN}/.. -Wl,--enable-new-dtags
+
 module_tag := optional
-build_type := target
-build_target := SHARED_LIBRARY
-include $(TEST_PATH)/Android.build.mk
+include $(LOCAL_PATH)/Android.build.testlib.target.mk
 
 
 # -----------------------------------------------------------------------------
@@ -94,22 +96,18 @@
 
 libdlext_test_zip_shared_libraries := libatest_simple_zip
 
-libdlext_test_zip_install_to_out_data_dir := $(module)
+libdlext_test_zip_relative_install_path := $(module)
 module_tag := optional
-build_type := target
-build_target := SHARED_LIBRARY
-include $(TEST_PATH)/Android.build.mk
+include $(LOCAL_PATH)/Android.build.testlib.target.mk
 
 module := libatest_simple_zip
 
 libatest_simple_zip_src_files := \
     dlopen_testlib_simple.cpp
 
-libatest_simple_zip_install_to_out_data_dir := $(module)
+libatest_simple_zip_relative_install_path := $(module)
 module_tag := optional
-build_type := target
-build_target := SHARED_LIBRARY
-include $(TEST_PATH)/Android.build.mk
+include $(LOCAL_PATH)/Android.build.testlib.target.mk
 
 # ----------------------------------------------------------------------------
 # Library with soname which does not match filename
diff --git a/tests/libs/bionic_tests_zipalign.cpp b/tests/libs/bionic_tests_zipalign.cpp
index a72820f..8e31474 100644
--- a/tests/libs/bionic_tests_zipalign.cpp
+++ b/tests/libs/bionic_tests_zipalign.cpp
@@ -19,6 +19,7 @@
 #include <stdlib.h>
 
 #include <algorithm>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -36,7 +37,7 @@
   fprintf(stderr, "    The output zip file that will be created from the input file.\n");
 }
 
-typedef std::pair<ZipEntry*, ZipString*> ZipData;
+using ZipData = std::pair<std::unique_ptr<ZipEntry>, std::unique_ptr<ZipString>>;
 
 static bool GetEntries(ZipArchiveHandle handle, std::vector<ZipData>* entries) {
   void* cookie;
@@ -49,14 +50,15 @@
   ZipEntry entry;
   ZipString name;
   while ((return_value = Next(cookie, &entry, &name)) == 0) {
-    entries->push_back(std::make_pair(new ZipEntry(entry), new ZipString(name)));
+    entries->emplace_back(std::make_pair(std::make_unique<ZipEntry>(entry),
+                                         std::make_unique<ZipString>(name)));
   }
   if (return_value != -1) {
     fprintf(stderr, "Error while iterating over zip entries: %s\n", ErrorCodeString(return_value));
   } else {
     // Sort by offset.
     std::sort(entries->begin(), entries->end(),
-              [](ZipData a, ZipData b) { return a.first->offset < b.first->offset; });
+              [](ZipData& a, ZipData& b) { return a.first->offset < b.first->offset; });
   }
 
   EndIteration(cookie);
@@ -75,8 +77,8 @@
 
   int32_t error;
   for (auto& entry : entries) {
-    ZipEntry* zip_entry = entry.first;
-    ZipString* zip_str = entry.second;
+    ZipEntry* zip_entry = entry.first.get();
+    ZipString* zip_str = entry.second.get();
 
     size_t flags = 0;
     if ((zip_entry->method & kCompressDeflated) != 0) {
diff --git a/tests/math_data_test.h b/tests/math_data_test.h
index 0aba701..42dc42c 100644
--- a/tests/math_data_test.h
+++ b/tests/math_data_test.h
@@ -16,6 +16,7 @@
 
 #include <gtest/gtest.h>
 
+#include <math.h>
 #include <fenv.h>
 
 template <typename RT, typename T1>
@@ -102,7 +103,28 @@
   uint64_t sign_magnitude;
 };
 
-// TODO: long double.
+template <> union fp_u<long double> {
+  long double value;
+#if defined(__LP64__)
+  struct {
+    unsigned fracl;
+    unsigned fraclm;
+    unsigned frachm;
+    unsigned frach:16;
+    unsigned exp:15;
+    unsigned sign:1;
+  } bits;
+  __int128_t sign_magnitude;
+#else
+  struct {
+      unsigned fracl;
+      unsigned frach:20;
+      unsigned exp:11;
+      unsigned sign:1;
+  } bits;
+  uint64_t sign_magnitude;
+#endif
+};
 
 template <typename T>
 static inline auto SignAndMagnitudeToBiased(const T& value) -> decltype(fp_u<T>::sign_magnitude) {
@@ -134,14 +156,8 @@
       return ::testing::AssertionSuccess();
     }
 
-    // Output the actual and expected values as hex floating point.
-    char expected_str[64];
-    char actual_str[64];
-    snprintf(expected_str, sizeof(expected_str), "%a", expected);
-    snprintf(actual_str, sizeof(actual_str), "%a", actual);
-
     return ::testing::AssertionFailure()
-        << "expected (" << expected_str << ") != actual (" << actual_str << ")";
+        << "expected (" << std::hexfloat << expected << ") != actual (" << actual << ")";
   }
 };
 
@@ -282,4 +298,3 @@
                         data[i].expected, f(data[i].input1, data[i].input2, data[i].input3)) << "Failed on element " << i;
   }
 }
-
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index ecba4ad..79c5e92 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -402,51 +402,135 @@
 }
 
 template <typename T>
-void CheckInfNan(int snprintf_fn(T*, size_t, const T*, ...),
-                 const T* fmt, const T* fmt_plus,
-                 const T* minus_inf, const T* inf_, const T* plus_inf,
-                 const T* minus_nan, const T* nan_, const T* plus_nan) {
+static void CheckInfNan(int snprintf_fn(T*, size_t, const T*, ...),
+                        int sscanf_fn(const T*, const T*, ...),
+                        const T* fmt_string, const T* fmt, const T* fmt_plus,
+                        const T* minus_inf, const T* inf_, const T* plus_inf,
+                        const T* minus_nan, const T* nan_, const T* plus_nan) {
   T buf[BUFSIZ];
+  float f;
 
-  snprintf_fn(buf, sizeof(buf), fmt, nan(""));
+  // NaN.
+
+  snprintf_fn(buf, sizeof(buf), fmt, nanf(""));
   EXPECT_STREQ(nan_, buf) << fmt;
-  snprintf_fn(buf, sizeof(buf), fmt, -nan(""));
+  EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+  EXPECT_TRUE(isnan(f));
+
+  snprintf_fn(buf, sizeof(buf), fmt, -nanf(""));
   EXPECT_STREQ(minus_nan, buf) << fmt;
-  snprintf_fn(buf, sizeof(buf), fmt_plus, nan(""));
+  EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+  EXPECT_TRUE(isnan(f));
+
+  snprintf_fn(buf, sizeof(buf), fmt_plus, nanf(""));
   EXPECT_STREQ(plus_nan, buf) << fmt_plus;
-  snprintf_fn(buf, sizeof(buf), fmt_plus, -nan(""));
+  EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+  EXPECT_TRUE(isnan(f));
+
+  snprintf_fn(buf, sizeof(buf), fmt_plus, -nanf(""));
   EXPECT_STREQ(minus_nan, buf) << fmt_plus;
+  EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+  EXPECT_TRUE(isnan(f));
 
-  snprintf_fn(buf, sizeof(buf), fmt, HUGE_VAL);
+  // Inf.
+
+  snprintf_fn(buf, sizeof(buf), fmt, HUGE_VALF);
   EXPECT_STREQ(inf_, buf) << fmt;
-  snprintf_fn(buf, sizeof(buf), fmt, -HUGE_VAL);
+  EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+  EXPECT_EQ(HUGE_VALF, f);
+
+  snprintf_fn(buf, sizeof(buf), fmt, -HUGE_VALF);
   EXPECT_STREQ(minus_inf, buf) << fmt;
-  snprintf_fn(buf, sizeof(buf), fmt_plus, HUGE_VAL);
+  EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+  EXPECT_EQ(-HUGE_VALF, f);
+
+  snprintf_fn(buf, sizeof(buf), fmt_plus, HUGE_VALF);
   EXPECT_STREQ(plus_inf, buf) << fmt_plus;
-  snprintf_fn(buf, sizeof(buf), fmt_plus, -HUGE_VAL);
+  EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+  EXPECT_EQ(HUGE_VALF, f);
+
+  snprintf_fn(buf, sizeof(buf), fmt_plus, -HUGE_VALF);
   EXPECT_STREQ(minus_inf, buf) << fmt_plus;
+  EXPECT_EQ(1, sscanf_fn(buf, fmt, &f));
+  EXPECT_EQ(-HUGE_VALF, f);
+
+  // Check case-insensitivity.
+  snprintf_fn(buf, sizeof(buf), fmt_string, "[InFiNiTy]");
+  EXPECT_EQ(1, sscanf_fn(buf, fmt, &f)) << buf;
+  EXPECT_EQ(HUGE_VALF, f);
+  snprintf_fn(buf, sizeof(buf), fmt_string, "[NaN]");
+  EXPECT_EQ(1, sscanf_fn(buf, fmt, &f)) << buf;
+  EXPECT_TRUE(isnan(f));
 }
 
-TEST(STDIO_TEST, snprintf_inf_nan) {
-  CheckInfNan(snprintf, "%a", "%+a", "-inf", "inf", "+inf", "-nan", "nan", "+nan");
-  CheckInfNan(snprintf, "%A", "%+A", "-INF", "INF", "+INF", "-NAN", "NAN", "+NAN");
-  CheckInfNan(snprintf, "%e", "%+e", "-inf", "inf", "+inf", "-nan", "nan", "+nan");
-  CheckInfNan(snprintf, "%E", "%+E", "-INF", "INF", "+INF", "-NAN", "NAN", "+NAN");
-  CheckInfNan(snprintf, "%f", "%+f", "-inf", "inf", "+inf", "-nan", "nan", "+nan");
-  CheckInfNan(snprintf, "%F", "%+F", "-INF", "INF", "+INF", "-NAN", "NAN", "+NAN");
-  CheckInfNan(snprintf, "%g", "%+g", "-inf", "inf", "+inf", "-nan", "nan", "+nan");
-  CheckInfNan(snprintf, "%G", "%+G", "-INF", "INF", "+INF", "-NAN", "NAN", "+NAN");
+TEST(STDIO_TEST, snprintf_sscanf_inf_nan) {
+  CheckInfNan(snprintf, sscanf, "%s",
+              "[%a]", "[%+a]",
+              "[-inf]", "[inf]", "[+inf]",
+              "[-nan]", "[nan]", "[+nan]");
+  CheckInfNan(snprintf, sscanf, "%s",
+              "[%A]", "[%+A]",
+              "[-INF]", "[INF]", "[+INF]",
+              "[-NAN]", "[NAN]", "[+NAN]");
+  CheckInfNan(snprintf, sscanf, "%s",
+              "[%e]", "[%+e]",
+              "[-inf]", "[inf]", "[+inf]",
+              "[-nan]", "[nan]", "[+nan]");
+  CheckInfNan(snprintf, sscanf, "%s",
+              "[%E]", "[%+E]",
+              "[-INF]", "[INF]", "[+INF]",
+              "[-NAN]", "[NAN]", "[+NAN]");
+  CheckInfNan(snprintf, sscanf, "%s",
+              "[%f]", "[%+f]",
+              "[-inf]", "[inf]", "[+inf]",
+              "[-nan]", "[nan]", "[+nan]");
+  CheckInfNan(snprintf, sscanf, "%s",
+              "[%F]", "[%+F]",
+              "[-INF]", "[INF]", "[+INF]",
+              "[-NAN]", "[NAN]", "[+NAN]");
+  CheckInfNan(snprintf, sscanf, "%s",
+              "[%g]", "[%+g]",
+              "[-inf]", "[inf]", "[+inf]",
+              "[-nan]", "[nan]", "[+nan]");
+  CheckInfNan(snprintf, sscanf, "%s",
+              "[%G]", "[%+G]",
+              "[-INF]", "[INF]", "[+INF]",
+              "[-NAN]", "[NAN]", "[+NAN]");
 }
 
-TEST(STDIO_TEST, wsprintf_inf_nan) {
-  CheckInfNan(swprintf, L"%a", L"%+a", L"-inf", L"inf", L"+inf", L"-nan", L"nan", L"+nan");
-  CheckInfNan(swprintf, L"%A", L"%+A", L"-INF", L"INF", L"+INF", L"-NAN", L"NAN", L"+NAN");
-  CheckInfNan(swprintf, L"%e", L"%+e", L"-inf", L"inf", L"+inf", L"-nan", L"nan", L"+nan");
-  CheckInfNan(swprintf, L"%E", L"%+E", L"-INF", L"INF", L"+INF", L"-NAN", L"NAN", L"+NAN");
-  CheckInfNan(swprintf, L"%f", L"%+f", L"-inf", L"inf", L"+inf", L"-nan", L"nan", L"+nan");
-  CheckInfNan(swprintf, L"%F", L"%+F", L"-INF", L"INF", L"+INF", L"-NAN", L"NAN", L"+NAN");
-  CheckInfNan(swprintf, L"%g", L"%+g", L"-inf", L"inf", L"+inf", L"-nan", L"nan", L"+nan");
-  CheckInfNan(swprintf, L"%G", L"%+G", L"-INF", L"INF", L"+INF", L"-NAN", L"NAN", L"+NAN");
+TEST(STDIO_TEST, swprintf_swscanf_inf_nan) {
+  CheckInfNan(swprintf, swscanf, L"%s",
+              L"[%a]", L"[%+a]",
+              L"[-inf]", L"[inf]", L"[+inf]",
+              L"[-nan]", L"[nan]", L"[+nan]");
+  CheckInfNan(swprintf, swscanf, L"%s",
+              L"[%A]", L"[%+A]",
+              L"[-INF]", L"[INF]", L"[+INF]",
+              L"[-NAN]", L"[NAN]", L"[+NAN]");
+  CheckInfNan(swprintf, swscanf, L"%s",
+              L"[%e]", L"[%+e]",
+              L"[-inf]", L"[inf]", L"[+inf]",
+              L"[-nan]", L"[nan]", L"[+nan]");
+  CheckInfNan(swprintf, swscanf, L"%s",
+              L"[%E]", L"[%+E]",
+              L"[-INF]", L"[INF]", L"[+INF]",
+              L"[-NAN]", L"[NAN]", L"[+NAN]");
+  CheckInfNan(swprintf, swscanf, L"%s",
+              L"[%f]", L"[%+f]",
+              L"[-inf]", L"[inf]", L"[+inf]",
+              L"[-nan]", L"[nan]", L"[+nan]");
+  CheckInfNan(swprintf, swscanf, L"%s",
+              L"[%F]", L"[%+F]",
+              L"[-INF]", L"[INF]", L"[+INF]",
+              L"[-NAN]", L"[NAN]", L"[+NAN]");
+  CheckInfNan(swprintf, swscanf, L"%s",
+              L"[%g]", L"[%+g]",
+              L"[-inf]", L"[inf]", L"[+inf]",
+              L"[-nan]", L"[nan]", L"[+nan]");
+  CheckInfNan(swprintf, swscanf, L"%s",
+              L"[%G]", L"[%+G]",
+              L"[-INF]", L"[INF]", L"[+INF]",
+              L"[-NAN]", L"[NAN]", L"[+NAN]");
 }
 
 TEST(STDIO_TEST, snprintf_d_INT_MAX) {
@@ -646,15 +730,34 @@
   fclose(fp);
 }
 
-TEST(STDIO_TEST, sscanf) {
-  char s1[123];
-  int i1;
-  double d1;
-  char s2[123];
-  ASSERT_EQ(3, sscanf("  hello 123 1.23 ", "%s %i %lf %s", s1, &i1, &d1, s2));
-  ASSERT_STREQ("hello", s1);
-  ASSERT_EQ(123, i1);
-  ASSERT_DOUBLE_EQ(1.23, d1);
+TEST(STDIO_TEST, sscanf_swscanf) {
+  struct stuff {
+    char s1[123];
+    int i1;
+    double d1;
+    float f1;
+    char s2[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);
+    }
+  } 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));
+  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));
+  s.Check();
 }
 
 TEST(STDIO_TEST, cantwrite_EBADF) {
diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp
index 773230f..5b9442f 100644
--- a/tests/stdlib_test.cpp
+++ b/tests/stdlib_test.cpp
@@ -17,16 +17,18 @@
 #include <gtest/gtest.h>
 
 #include "BionicDeathTest.h"
+#include "math_data_test.h"
 #include "TemporaryFile.h"
 #include "utils.h"
 
 #include <errno.h>
+#include <fcntl.h>
 #include <libgen.h>
 #include <limits.h>
+#include <math.h>
 #include <pthread.h>
 #include <stdint.h>
 #include <stdlib.h>
-#include <fcntl.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 
@@ -288,16 +290,65 @@
   ASSERT_DOUBLE_EQ(1.23, atof("1.23"));
 }
 
+template <typename T>
+static void CheckStrToFloat(T fn(const char* s, char** end)) {
+  FpUlpEq<0, T> pred;
+
+  EXPECT_PRED_FORMAT2(pred, 9.0, fn("9.0", nullptr));
+  EXPECT_PRED_FORMAT2(pred, 9.0, fn("0.9e1", nullptr));
+  EXPECT_PRED_FORMAT2(pred, 9.0, fn("0x1.2p3", nullptr));
+
+  EXPECT_TRUE(isnan(fn("+nan", nullptr)));
+  EXPECT_TRUE(isnan(fn("nan", nullptr)));
+  EXPECT_TRUE(isnan(fn("-nan", nullptr)));
+
+  EXPECT_TRUE(isnan(fn("+nan(0xff)", nullptr)));
+  EXPECT_TRUE(isnan(fn("nan(0xff)", nullptr)));
+  EXPECT_TRUE(isnan(fn("-nan(0xff)", nullptr)));
+
+  char* p;
+  EXPECT_TRUE(isnan(fn("+nanny", &p)));
+  EXPECT_STREQ("ny", p);
+  EXPECT_TRUE(isnan(fn("nanny", &p)));
+  EXPECT_STREQ("ny", p);
+  EXPECT_TRUE(isnan(fn("-nanny", &p)));
+  EXPECT_STREQ("ny", p);
+
+  EXPECT_EQ(0, fn("muppet", &p));
+  EXPECT_STREQ("muppet", p);
+  EXPECT_EQ(0, fn("  muppet", &p));
+  EXPECT_STREQ("  muppet", p);
+
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("+inf", nullptr));
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("inf", nullptr));
+  EXPECT_EQ(-std::numeric_limits<T>::infinity(), fn("-inf", nullptr));
+
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("+infinity", nullptr));
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("infinity", nullptr));
+  EXPECT_EQ(-std::numeric_limits<T>::infinity(), fn("-infinity", nullptr));
+
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("+infinitude", &p));
+  EXPECT_STREQ("initude", p);
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("infinitude", &p));
+  EXPECT_STREQ("initude", p);
+  EXPECT_EQ(-std::numeric_limits<T>::infinity(), fn("-infinitude", &p));
+  EXPECT_STREQ("initude", p);
+
+  // Check case-insensitivity.
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn("InFiNiTy", nullptr));
+  EXPECT_TRUE(isnan(fn("NaN", nullptr)));
+}
+
 TEST(stdlib, strtod) {
-  ASSERT_DOUBLE_EQ(1.23, strtod("1.23", NULL));
+  CheckStrToFloat(strtod);
 }
 
 TEST(stdlib, strtof) {
-  ASSERT_FLOAT_EQ(1.23, strtof("1.23", NULL));
+  CheckStrToFloat(strtof);
 }
 
 TEST(stdlib, strtold) {
-  ASSERT_DOUBLE_EQ(1.23, strtold("1.23", NULL));
+  CheckStrToFloat(strtold);
 }
 
 TEST(stdlib, strtof_2206701) {
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index 48c299a..2a46d8b 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -124,7 +124,10 @@
 TEST(time, mktime_EOVERFLOW) {
   struct tm t;
   memset(&t, 0, sizeof(tm));
-  t.tm_year = 0;
+
+  // LP32 year range is 1901-2038, so this year is guaranteed not to overflow.
+  t.tm_year = 2016 - 1900;
+
   t.tm_mon = 2;
   t.tm_mday = 10;
 
@@ -132,7 +135,10 @@
   ASSERT_NE(static_cast<time_t>(-1), mktime(&t));
   ASSERT_EQ(0, errno);
 
+  // This will overflow for LP32 or LP64.
   t.tm_year = INT_MAX;
+
+  errno = 0;
   ASSERT_EQ(static_cast<time_t>(-1), mktime(&t));
   ASSERT_EQ(EOVERFLOW, errno);
 }
diff --git a/tests/utils.h b/tests/utils.h
index 08e8cea..4391f41 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -122,6 +122,9 @@
 // The absolute path to the executable
 const std::string& get_executable_path();
 
+// Get realpath
+bool get_realpath(const std::string& path, std::string* realpath);
+
 // Access to argc/argv/envp
 int get_argc();
 char** get_argv();
diff --git a/tests/wchar_test.cpp b/tests/wchar_test.cpp
index 000dc98..db51c08 100644
--- a/tests/wchar_test.cpp
+++ b/tests/wchar_test.cpp
@@ -22,6 +22,8 @@
 #include <stdint.h>
 #include <wchar.h>
 
+#include "math_data_test.h"
+
 #define NUM_WCHARS(num_bytes) ((num_bytes)/sizeof(wchar_t))
 
 TEST(wchar, sizeof_wchar_t) {
@@ -389,14 +391,6 @@
   ASSERT_EQ('\x20', *invalid);
 }
 
-TEST(wchar, wcstod) {
-  ASSERT_DOUBLE_EQ(1.23, wcstod(L"1.23", NULL));
-}
-
-TEST(wchar, wcstof) {
-  ASSERT_FLOAT_EQ(1.23f, wcstof(L"1.23", NULL));
-}
-
 TEST(wchar, wcstol) {
   ASSERT_EQ(123L, wcstol(L"123", NULL, 0));
 }
@@ -405,10 +399,6 @@
   ASSERT_EQ(123LL, wcstol(L"123", NULL, 0));
 }
 
-TEST(wchar, wcstold) {
-  ASSERT_DOUBLE_EQ(1.23L, wcstold(L"1.23", NULL));
-}
-
 TEST(wchar, wcstoul) {
   ASSERT_EQ(123UL, wcstoul(L"123", NULL, 0));
 }
@@ -672,3 +662,64 @@
   wchar_t dst[6];
   ASSERT_EQ(&dst[4], wmempcpy(dst, L"hello", 4));
 }
+
+template <typename T>
+static void CheckWcsToFloat(T fn(const wchar_t* s, wchar_t** end)) {
+  FpUlpEq<0, T> pred;
+
+  EXPECT_PRED_FORMAT2(pred, 9.0, fn(L"9.0", nullptr));
+  EXPECT_PRED_FORMAT2(pred, 9.0, fn(L"0.9e1", nullptr));
+  EXPECT_PRED_FORMAT2(pred, 9.0, fn(L"0x1.2p3", nullptr));
+
+  EXPECT_TRUE(isnan(fn(L"+nan", nullptr)));
+  EXPECT_TRUE(isnan(fn(L"nan", nullptr)));
+  EXPECT_TRUE(isnan(fn(L"-nan", nullptr)));
+
+  EXPECT_TRUE(isnan(fn(L"+nan(0xff)", nullptr)));
+  EXPECT_TRUE(isnan(fn(L"nan(0xff)", nullptr)));
+  EXPECT_TRUE(isnan(fn(L"-nan(0xff)", nullptr)));
+
+  wchar_t* p;
+  EXPECT_TRUE(isnan(fn(L"+nanny", &p)));
+  EXPECT_STREQ(L"ny", p);
+  EXPECT_TRUE(isnan(fn(L"nanny", &p)));
+  EXPECT_STREQ(L"ny", p);
+  EXPECT_TRUE(isnan(fn(L"-nanny", &p)));
+  EXPECT_STREQ(L"ny", p);
+
+  EXPECT_EQ(0, fn(L"muppet", &p));
+  EXPECT_STREQ(L"muppet", p);
+  EXPECT_EQ(0, fn(L"  muppet", &p));
+  EXPECT_STREQ(L"  muppet", p);
+
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"+inf", nullptr));
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"inf", nullptr));
+  EXPECT_EQ(-std::numeric_limits<T>::infinity(), fn(L"-inf", nullptr));
+
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"+infinity", nullptr));
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"infinity", nullptr));
+  EXPECT_EQ(-std::numeric_limits<T>::infinity(), fn(L"-infinity", nullptr));
+
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"+infinitude", &p));
+  EXPECT_STREQ(L"initude", p);
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"infinitude", &p));
+  EXPECT_STREQ(L"initude", p);
+  EXPECT_EQ(-std::numeric_limits<T>::infinity(), fn(L"-infinitude", &p));
+  EXPECT_STREQ(L"initude", p);
+
+  // Check case-insensitivity.
+  EXPECT_EQ(std::numeric_limits<T>::infinity(), fn(L"InFiNiTy", nullptr));
+  EXPECT_TRUE(isnan(fn(L"NaN", nullptr)));
+}
+
+TEST(wchar, wcstod) {
+  CheckWcsToFloat(wcstod);
+}
+
+TEST(wchar, wcstof) {
+  CheckWcsToFloat(wcstof);
+}
+
+TEST(wchar, wcstold) {
+  CheckWcsToFloat(wcstold);
+}
diff --git a/tools/versioner/src/DeclarationDatabase.cpp b/tools/versioner/src/DeclarationDatabase.cpp
index 88f7b55..02383bb 100644
--- a/tools/versioner/src/DeclarationDatabase.cpp
+++ b/tools/versioner/src/DeclarationDatabase.cpp
@@ -84,6 +84,7 @@
     std::string declaration_name = getDeclName(named_decl);
     bool is_extern = named_decl->getFormalLinkage() == ExternalLinkage;
     bool is_definition = false;
+    bool no_guard = false;
 
     if (auto function_decl = dyn_cast<FunctionDecl>(decl)) {
       declaration_type = DeclarationType::function;
@@ -140,7 +141,9 @@
     // Find and parse __ANDROID_AVAILABILITY_DUMP__ annotations.
     for (const AnnotateAttr* attr : decl->specific_attrs<AnnotateAttr>()) {
       llvm::StringRef annotation = attr->getAnnotation();
-      if (annotation == "introduced_in_future") {
+      if (annotation == "versioner_no_guard") {
+        no_guard = true;
+      } else if (annotation == "introduced_in_future") {
         // Tag the compiled-for arch, since this can vary across archs.
         availability.arch_availability[type.arch].future = true;
       } else {
@@ -200,11 +203,13 @@
       declaration.location = location;
       declaration.is_extern = is_extern;
       declaration.is_definition = is_definition;
+      declaration.no_guard = no_guard;
       declaration.availability.insert(std::make_pair(type, availability));
       symbol_it->second.declarations.insert(std::make_pair(location, declaration));
     } else {
       if (declaration_it->second.is_extern != is_extern ||
-          declaration_it->second.is_definition != is_definition) {
+          declaration_it->second.is_definition != is_definition ||
+          declaration_it->second.no_guard != no_guard) {
         errx(1, "varying declaration of '%s' at %s:%u:%u", declaration_name.c_str(),
              location.filename.c_str(), location.start.line, location.start.column);
       }
diff --git a/tools/versioner/src/DeclarationDatabase.h b/tools/versioner/src/DeclarationDatabase.h
index b130ca9..8f43d79 100644
--- a/tools/versioner/src/DeclarationDatabase.h
+++ b/tools/versioner/src/DeclarationDatabase.h
@@ -148,6 +148,7 @@
 
   bool is_extern;
   bool is_definition;
+  bool no_guard;
   std::map<CompilationType, DeclarationAvailability> availability;
 
   bool calculateAvailability(DeclarationAvailability* output) const;
@@ -161,6 +162,9 @@
 
     fprintf(out, "%s ", is_extern ? "extern" : "static");
     fprintf(out, "%s ", is_definition ? "definition" : "declaration");
+    if (no_guard) {
+      fprintf(out, "no_guard ");
+    }
     fprintf(out, "@ %s:%u:%u", StripPrefix(location.filename, base_path).str().c_str(),
             location.start.line, location.start.column);
 
diff --git a/tools/versioner/src/Preprocessor.cpp b/tools/versioner/src/Preprocessor.cpp
index 8d0b943..3c86486 100644
--- a/tools/versioner/src/Preprocessor.cpp
+++ b/tools/versioner/src/Preprocessor.cpp
@@ -452,6 +452,11 @@
       const Location& location = decl_it.first;
       const Declaration& decl = decl_it.second;
 
+      if (decl.no_guard) {
+        // No guard required.
+        continue;
+      }
+
       DeclarationAvailability macro_guard = calculateRequiredGuard(decl);
       if (!macro_guard.empty()) {
         guards[location.filename][location] = macro_guard;
diff --git a/tools/versioner/src/versioner.cpp b/tools/versioner/src/versioner.cpp
index 432fd66..daf6f0b 100644
--- a/tools/versioner/src/versioner.cpp
+++ b/tools/versioner/src/versioner.cpp
@@ -77,6 +77,12 @@
     command.push_back("-D__ANDROID_API__="s + std::to_string(type.api_level));
     command.push_back("-D_FORTIFY_SOURCE=2");
     command.push_back("-D_GNU_SOURCE");
+    command.push_back("-Wall");
+    command.push_back("-Wextra");
+    command.push_back("-Werror");
+    command.push_back("-Wno-unused-macros");
+    command.push_back("-Wno-unused-function");
+    command.push_back("-Wno-unused-variable");
     command.push_back("-Wno-unknown-attributes");
     command.push_back("-Wno-pragma-once-outside-header");
     command.push_back("-target");
diff --git a/tools/versioner/tests/preprocessor_no_guard/expected/foo.h b/tools/versioner/tests/preprocessor_no_guard/expected/foo.h
new file mode 100644
index 0000000..2bf1dbf
--- /dev/null
+++ b/tools/versioner/tests/preprocessor_no_guard/expected/foo.h
@@ -0,0 +1 @@
+int foo() __VERSIONER_NO_GUARD __INTRODUCED_IN(14);
diff --git a/tools/versioner/tests/preprocessor_no_guard/headers/foo.h b/tools/versioner/tests/preprocessor_no_guard/headers/foo.h
new file mode 100644
index 0000000..2bf1dbf
--- /dev/null
+++ b/tools/versioner/tests/preprocessor_no_guard/headers/foo.h
@@ -0,0 +1 @@
+int foo() __VERSIONER_NO_GUARD __INTRODUCED_IN(14);
diff --git a/tools/versioner/tests/preprocessor_no_guard/run.sh b/tools/versioner/tests/preprocessor_no_guard/run.sh
new file mode 100644
index 0000000..1b0aae2
--- /dev/null
+++ b/tools/versioner/tests/preprocessor_no_guard/run.sh
@@ -0,0 +1,4 @@
+rm -rf out
+set -e
+versioner headers -i -o out
+diff -q -w -B out expected