Merge "Test __fpclassify/__isinf/__isnan."
diff --git a/libc/Android.bp b/libc/Android.bp
index 050d2f5..27e90be 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -524,8 +524,8 @@
     name: "libc_openbsd_large_stack",
     defaults: ["libc_defaults"],
     srcs: [
-        "stdio/vfprintf.c",
-        "stdio/vfwprintf.c",
+        "stdio/vfprintf.cpp",
+        "stdio/vfwprintf.cpp",
     ],
     cflags: [
         "-include openbsd-compat.h",
diff --git a/libc/stdio/floatio.h b/libc/stdio/floatio.h
deleted file mode 100644
index 9769030..0000000
--- a/libc/stdio/floatio.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*	$OpenBSD: floatio.h,v 1.4 2008/09/07 20:36:08 martynas Exp $	*/
-
-/*-
- * 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.
- */
-
-/*
- * Floating point scanf/printf (input/output) definitions.
- */
-
-/* 11-bit exponent (VAX G floating point) is 308 decimal digits */
-#define	MAXEXP		308
-/* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
-#define	MAXFRACT	39
-
-/*
- * MAXEXPDIG is the maximum number of decimal digits needed to store a
- * floating point exponent in the largest supported format.  It should
- * be ceil(log10(LDBL_MAX_10_EXP)) or, if hexadecimal floating point
- * conversions are supported, ceil(log10(LDBL_MAX_EXP)).  But since it
- * is presently never greater than 5 in practice, we fudge it.
- */
-#define	MAXEXPDIG	6
-#if LDBL_MAX_EXP > 999999
-#error "floating point buffers too small"
-#endif
-
-char *__hdtoa(double, const char *, int, int *, int *, char **);
-char *__hldtoa(long double, const char *, int, int *, int *, char **);
-char *__ldtoa(long double *, int, int, int *, int *, char **);
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index 155c70b..0ff0785 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -261,10 +261,35 @@
 size_t parsefloat(FILE*, char*, char*);
 size_t wparsefloat(FILE*, wchar_t*, wchar_t*);
 
-__END_DECLS
-
 // Sanity check a FILE* for nullptr, so we can emit a message while crashing
 // instead of doing a blind null-dereference.
 #define CHECK_FP(fp) if (fp == nullptr) __fortify_fatal("%s: null FILE*", __FUNCTION__)
 
+/*
+ * Floating point scanf/printf (input/output) definitions.
+ */
+
+/* 11-bit exponent (VAX G floating point) is 308 decimal digits */
+#define MAXEXP 308
+/* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
+#define MAXFRACT 39
+
+/*
+ * MAXEXPDIG is the maximum number of decimal digits needed to store a
+ * floating point exponent in the largest supported format.  It should
+ * be ceil(log10(LDBL_MAX_10_EXP)) or, if hexadecimal floating point
+ * conversions are supported, ceil(log10(LDBL_MAX_EXP)).  But since it
+ * is presently never greater than 5 in practice, we fudge it.
+ */
+#define MAXEXPDIG 6
+#if LDBL_MAX_EXP > 999999
+#error "floating point buffers too small"
+#endif
+
+char* __hdtoa(double, const char*, int, int*, int*, char**);
+char* __hldtoa(long double, const char*, int, int*, int*, char**);
+char* __ldtoa(long double*, int, int, int*, int*, char**);
+
+__END_DECLS
+
 #endif
diff --git a/libc/stdio/parsefloat.c b/libc/stdio/parsefloat.c
index e911da4..d81e713 100644
--- a/libc/stdio/parsefloat.c
+++ b/libc/stdio/parsefloat.c
@@ -34,7 +34,6 @@
 #include <stdlib.h>
 
 #include "local.h"
-#include "floatio.h"
 
 #define	BUF		513	/* Maximum length of numeric string. */
 
diff --git a/libc/stdio/vfprintf.c b/libc/stdio/vfprintf.cpp
similarity index 94%
rename from libc/stdio/vfprintf.c
rename to libc/stdio/vfprintf.cpp
index 5713b44..6d16828 100644
--- a/libc/stdio/vfprintf.c
+++ b/libc/stdio/vfprintf.cpp
@@ -31,18 +31,17 @@
  * SUCH DAMAGE.
  */
 
-/*
- * Actual printf innards.
- *
- * This code is large and complicated...
- */
+#define CHAR_TYPE char
 
 #include <sys/mman.h>
 #include <sys/types.h>
 
 #include <errno.h>
+#include <float.h>
 #include <langinfo.h>
 #include <limits.h>
+#include <locale.h>
+#include <math.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -53,6 +52,7 @@
 #include <wchar.h>
 
 #include "fvwrite.h"
+#include "gdtoa.h"
 #include "local.h"
 
 union arg {
@@ -172,7 +172,7 @@
       if (clen == (size_t)-1) return (NULL);
     }
   }
-  if ((convbuf = malloc(nbytes + 1)) == NULL) return (NULL);
+  if ((convbuf = static_cast<char*>(malloc(nbytes + 1))) == NULL) return NULL;
 
   /* Fill the output buffer. */
   p = wcsarg;
@@ -185,15 +185,43 @@
   return (convbuf);
 }
 
-#include <float.h>
-#include <locale.h>
-#include <math.h>
-#include "floatio.h"
-#include "gdtoa.h"
-
 #define DEFPREC 6
 
-static int exponent(char*, int, int);
+#define to_digit(c) ((c) - '0')
+#define is_digit(c) ((unsigned)to_digit(c) <= 9)
+#define to_char(n) ((CHAR_TYPE)((n) + '0'))
+
+template <typename CharT>
+static int exponent(CharT* p0, int exp, int fmtch) {
+  CharT* p = p0;
+  *p++ = fmtch;
+  if (exp < 0) {
+    exp = -exp;
+    *p++ = '-';
+  } else {
+    *p++ = '+';
+  }
+
+  CharT expbuf[MAXEXPDIG];
+  CharT* t = expbuf + MAXEXPDIG;
+  if (exp > 9) {
+    do {
+      *--t = to_char(exp % 10);
+    } while ((exp /= 10) > 9);
+    *--t = to_char(exp);
+    for (; t < expbuf + MAXEXPDIG; *p++ = *t++) /* nothing */;
+  } else {
+    /*
+     * Exponents for decimal floating point conversions
+     * (%[eEgG]) must be at least two characters long,
+     * whereas exponents for hexadecimal conversions can
+     * be only one character long.
+     */
+    if (fmtch == 'e' || fmtch == 'E') *p++ = '0';
+    *p++ = to_char(exp);
+  }
+  return (p - p0);
+}
 
 /*
  * The size of the buffer we use as scratch space for integer
@@ -207,13 +235,6 @@
 #define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
 
 /*
- * Macros for converting digits to letters and vice versa
- */
-#define to_digit(c) ((c) - '0')
-#define is_digit(c) ((unsigned)to_digit(c) <= 9)
-#define to_char(n) ((n) + '0')
-
-/*
  * Flags used during conversion.
  */
 #define ALT 0x0001      /* alternate form */
@@ -310,8 +331,8 @@
   static char zeroes[PADSIZE] = { '0', '0', '0', '0', '0', '0', '0', '0',
                                   '0', '0', '0', '0', '0', '0', '0', '0' };
 
-  static const char xdigs_lower[16] = "0123456789abcdef";
-  static const char xdigs_upper[16] = "0123456789ABCDEF";
+  static const char xdigs_lower[] = "0123456789abcdef";
+  static const char xdigs_upper[] = "0123456789ABCDEF";
 
   /*
    * BEWARE, these `goto error' on error, and PAD uses `n'.
@@ -612,6 +633,9 @@
       case 'z':
         flags |= SIZEINT;
         goto rflag;
+      case 'C':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
       case 'c':
         if (flags & LONGINT) {
           mbstate_t mbs;
@@ -712,10 +736,11 @@
       fp_common:
         if (signflag) sign = '-';
         if (expt == INT_MAX) { /* inf or nan */
-          if (*cp == 'N')
-            cp = (ch >= 'a') ? "nan" : "NAN";
-          else
-            cp = (ch >= 'a') ? "inf" : "INF";
+          if (*cp == 'N') {
+            cp = const_cast<char*>((ch >= 'a') ? "nan" : "NAN");
+          } else {
+            cp = const_cast<char*>((ch >= 'a') ? "inf" : "INF");
+          }
           size = 3;
           flags &= ~ZEROPAD;
           break;
@@ -794,6 +819,9 @@
         xdigs = xdigs_lower;
         ox[1] = 'x';
         goto nosign;
+      case 'S':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
       case 's':
         if (flags & LONGINT) {
           wchar_t* wcp;
@@ -801,7 +829,7 @@
           free(convbuf);
           convbuf = NULL;
           if ((wcp = GETARG(wchar_t*)) == NULL) {
-            cp = "(null)";
+            cp = const_cast<char*>("(null)");
           } else {
             convbuf = __wcsconv(wcp, prec);
             if (convbuf == NULL) {
@@ -810,17 +838,11 @@
             }
             cp = convbuf;
           }
-        } else if ((cp = GETARG(char*)) == NULL)
-          cp = "(null)";
+        } else if ((cp = GETARG(char*)) == NULL) {
+          cp = const_cast<char*>("(null)");
+        }
         if (prec >= 0) {
-          /*
-           * can't use strlen; can only look for the
-           * NUL in the first `prec' characters, and
-           * strlen() will go further.
-           */
-          char* p = memchr(cp, 0, prec);
-
-          size = p ? (p - cp) : prec;
+          size = strnlen(cp, prec);
         } else {
           size_t len;
 
@@ -897,15 +919,11 @@
               break;
 
             default:
-              cp = "bug in vfprintf: bad base";
-              size = strlen(cp);
-              goto skipsize;
+              abort();
           }
         }
         size = buf + BUF - cp;
-        if (size > BUF) /* should never happen */
-          abort();
-      skipsize:
+        if (size > BUF) abort(); /* should never happen */
         break;
       default: /* "%?" prints ?, unless ? is NUL */
         if (ch == '\0') goto done;
@@ -1228,6 +1246,9 @@
       case 'z':
         flags |= SIZEINT;
         goto rflag;
+      case 'C':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
       case 'c':
         if (flags & LONGINT)
           ADDTYPE(T_WINT);
@@ -1281,6 +1302,9 @@
       case 'p':
         ADDTYPE(TP_VOID);
         break;
+      case 'S':
+        flags |= LONGINT;
+        /*FALLTHROUGH*/
       case 's':
         if (flags & LONGINT)
           ADDTYPE(TP_WCHAR);
@@ -1306,8 +1330,10 @@
    */
   if (tablemax >= STATIC_ARG_TBL_SIZE) {
     *argtablesiz = sizeof(union arg) * (tablemax + 1);
-    *argtable = mmap(NULL, *argtablesiz, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
-    if (*argtable == MAP_FAILED) return (-1);
+    *argtable = static_cast<arg*>(mmap(NULL, *argtablesiz,
+                                       PROT_WRITE | PROT_READ,
+                                       MAP_ANON | MAP_PRIVATE, -1, 0));
+    if (*argtable == MAP_FAILED) return -1;
   }
 
 #if 0
@@ -1413,55 +1439,28 @@
  * Increase the size of the type table.
  */
 static int __grow_type_table(unsigned char** typetable, int* tablesize) {
-  unsigned char* oldtable = *typetable;
-  int newsize = *tablesize * 2;
+  unsigned char* old_table = *typetable;
+  int new_size = *tablesize * 2;
 
-  if (newsize < getpagesize()) newsize = getpagesize();
+  if (new_size < getpagesize()) new_size = getpagesize();
 
   if (*tablesize == STATIC_ARG_TBL_SIZE) {
-    *typetable = mmap(NULL, newsize, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
-    if (*typetable == MAP_FAILED) return (-1);
-    bcopy(oldtable, *typetable, *tablesize);
+    *typetable = static_cast<unsigned char*>(mmap(NULL, new_size,
+                                                  PROT_WRITE | PROT_READ,
+                                                  MAP_ANON | MAP_PRIVATE, -1, 0));
+    if (*typetable == MAP_FAILED) return -1;
+    bcopy(old_table, *typetable, *tablesize);
   } else {
-    unsigned char* new = mmap(NULL, newsize, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
-    if (new == MAP_FAILED) return (-1);
-    memmove(new, *typetable, *tablesize);
+    unsigned char* new_table = static_cast<unsigned char*>(mmap(NULL, new_size,
+                                                                PROT_WRITE | PROT_READ,
+                                                                MAP_ANON | MAP_PRIVATE, -1, 0));
+    if (new_table == MAP_FAILED) return -1;
+    memmove(new_table, *typetable, *tablesize);
     munmap(*typetable, *tablesize);
-    *typetable = new;
+    *typetable = new_table;
   }
-  memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize));
+  memset(*typetable + *tablesize, T_UNUSED, (new_size - *tablesize));
 
-  *tablesize = newsize;
-  return (0);
-}
-
-static int exponent(char* p0, int exp, int fmtch) {
-  char *p, *t;
-  char expbuf[MAXEXPDIG];
-
-  p = p0;
-  *p++ = fmtch;
-  if (exp < 0) {
-    exp = -exp;
-    *p++ = '-';
-  } else
-    *p++ = '+';
-  t = expbuf + MAXEXPDIG;
-  if (exp > 9) {
-    do {
-      *--t = to_char(exp % 10);
-    } while ((exp /= 10) > 9);
-    *--t = to_char(exp);
-    for (; t < expbuf + MAXEXPDIG; *p++ = *t++) /* nothing */;
-  } else {
-    /*
-     * Exponents for decimal floating point conversions
-     * (%[eEgG]) must be at least two characters long,
-     * whereas exponents for hexadecimal conversions can
-     * be only one character long.
-     */
-    if (fmtch == 'e' || fmtch == 'E') *p++ = '0';
-    *p++ = to_char(exp);
-  }
-  return (p - p0);
+  *tablesize = new_size;
+  return 0;
 }
diff --git a/libc/stdio/vfscanf.c b/libc/stdio/vfscanf.c
index c18e214..c09132f 100644
--- a/libc/stdio/vfscanf.c
+++ b/libc/stdio/vfscanf.c
@@ -41,8 +41,6 @@
 #include <wctype.h>
 #include "local.h"
 
-#include "floatio.h"
-
 #define BUF 513 /* Maximum length of numeric string. */
 
 /*
diff --git a/libc/stdio/vfwprintf.c b/libc/stdio/vfwprintf.cpp
similarity index 94%
rename from libc/stdio/vfwprintf.c
rename to libc/stdio/vfwprintf.cpp
index 647b59a..80eb78a 100644
--- a/libc/stdio/vfwprintf.c
+++ b/libc/stdio/vfwprintf.cpp
@@ -31,18 +31,17 @@
  * SUCH DAMAGE.
  */
 
-/*
- * Actual wprintf innards.
- *
- * This code is large and complicated...
- */
+#define CHAR_TYPE wchar_t
 
 #include <sys/mman.h>
 #include <sys/types.h>
 
 #include <errno.h>
+#include <float.h>
 #include <langinfo.h>
 #include <limits.h>
+#include <locale.h>
+#include <math.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -53,6 +52,7 @@
 #include <wchar.h>
 
 #include "fvwrite.h"
+#include "gdtoa.h"
 #include "local.h"
 
 union arg {
@@ -190,7 +190,7 @@
    * converting at most `size' bytes of the input multibyte string to
    * wide characters for printing.
    */
-  convbuf = calloc(insize + 1, sizeof(*convbuf));
+  convbuf = static_cast<wchar_t*>(calloc(insize + 1, sizeof(*convbuf)));
   if (convbuf == NULL) return (NULL);
   wcp = convbuf;
   p = mbsarg;
@@ -212,15 +212,43 @@
   return (convbuf);
 }
 
-#include <float.h>
-#include <locale.h>
-#include <math.h>
-#include "floatio.h"
-#include "gdtoa.h"
-
 #define DEFPREC 6
 
-static int exponent(wchar_t*, int, int);
+#define to_digit(c) ((c) - '0')
+#define is_digit(c) ((unsigned)to_digit(c) <= 9)
+#define to_char(n) ((CHAR_TYPE)((n) + '0'))
+
+template <typename CharT>
+static int exponent(CharT* p0, int exp, int fmtch) {
+  CharT* p = p0;
+  *p++ = fmtch;
+  if (exp < 0) {
+    exp = -exp;
+    *p++ = '-';
+  } else {
+    *p++ = '+';
+  }
+
+  CharT expbuf[MAXEXPDIG];
+  CharT* t = expbuf + MAXEXPDIG;
+  if (exp > 9) {
+    do {
+      *--t = to_char(exp % 10);
+    } while ((exp /= 10) > 9);
+    *--t = to_char(exp);
+    for (; t < expbuf + MAXEXPDIG; *p++ = *t++) /* nothing */;
+  } else {
+    /*
+     * Exponents for decimal floating point conversions
+     * (%[eEgG]) must be at least two characters long,
+     * whereas exponents for hexadecimal conversions can
+     * be only one character long.
+     */
+    if (fmtch == 'e' || fmtch == 'E') *p++ = '0';
+    *p++ = to_char(exp);
+  }
+  return (p - p0);
+}
 
 /*
  * The size of the buffer we use as scratch space for integer
@@ -234,13 +262,6 @@
 #define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
 
 /*
- * Macros for converting digits to letters and vice versa
- */
-#define to_digit(c) ((c) - '0')
-#define is_digit(c) ((unsigned)to_digit(c) <= 9)
-#define to_char(n) ((wchar_t)((n) + '0'))
-
-/*
  * Flags used during conversion.
  */
 #define ALT 0x0001      /* alternate form */
@@ -321,8 +342,8 @@
   static wchar_t zeroes[PADSIZE] = { '0', '0', '0', '0', '0', '0', '0', '0',
                                      '0', '0', '0', '0', '0', '0', '0', '0' };
 
-  static const char xdigs_lower[16] = "0123456789abcdef";
-  static const char xdigs_upper[16] = "0123456789ABCDEF";
+  static const char xdigs_lower[] = "0123456789abcdef";
+  static const char xdigs_upper[] = "0123456789ABCDEF";
 
   /*
    * BEWARE, these `goto error' on error, PRINT uses 'n3',
@@ -689,10 +710,11 @@
       fp_common:
         if (signflag) sign = '-';
         if (expt == INT_MAX) { /* inf or nan */
-          if (*cp == 'N')
-            cp = (ch >= 'a') ? L"nan" : L"NAN";
-          else
-            cp = (ch >= 'a') ? L"inf" : L"INF";
+          if (*cp == 'N') {
+            cp = const_cast<wchar_t*>((ch >= 'a') ? L"nan" : L"NAN");
+          } else {
+            cp = const_cast<wchar_t*>((ch >= 'a') ? L"inf" : L"INF");
+          }
           size = 3;
           flags &= ~ZEROPAD;
           break;
@@ -775,27 +797,21 @@
         /*FALLTHROUGH*/
       case 's':
         if (flags & LONGINT) {
-          if ((cp = GETARG(wchar_t*)) == NULL) cp = L"(null)";
+          if ((cp = GETARG(wchar_t*)) == NULL) cp = const_cast<wchar_t*>(L"(null)");
         } else {
           char* mbsarg;
-          if ((mbsarg = GETARG(char*)) == NULL) mbsarg = "(null)";
+          if ((mbsarg = GETARG(char*)) == NULL) mbsarg = const_cast<char*>("(null)");
           free(convbuf);
           convbuf = __mbsconv(mbsarg, prec);
           if (convbuf == NULL) {
             fp->_flags |= __SERR;
             goto error;
-          } else
+          } else {
             cp = convbuf;
+          }
         }
         if (prec >= 0) {
-          /*
-           * can't use wcslen; can only look for the
-           * NUL in the first `prec' characters, and
-           * wcslen() will go further.
-           */
-          wchar_t* p = wmemchr(cp, 0, prec);
-
-          size = p ? (p - cp) : prec;
+          size = wcsnlen(cp, prec);
         } else {
           size_t len;
 
@@ -872,15 +888,11 @@
               break;
 
             default:
-              cp = L"bug in vfwprintf: bad base";
-              size = wcslen(cp);
-              goto skipsize;
+              abort();
           }
         }
         size = buf + BUF - cp;
-        if (size > BUF) /* should never happen */
-          abort();
-      skipsize:
+        if (size > BUF) abort(); /* should never happen */
         break;
       default: /* "%?" prints ?, unless ? is NUL */
         if (ch == '\0') goto done;
@@ -1281,8 +1293,10 @@
    */
   if (tablemax >= STATIC_ARG_TBL_SIZE) {
     *argtablesiz = sizeof(union arg) * (tablemax + 1);
-    *argtable = mmap(NULL, *argtablesiz, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
-    if (*argtable == MAP_FAILED) return (-1);
+    *argtable = static_cast<arg*>(mmap(NULL, *argtablesiz,
+                                       PROT_WRITE | PROT_READ,
+                                       MAP_ANON | MAP_PRIVATE, -1, 0));
+    if (*argtable == MAP_FAILED) return -1;
   }
 
 #if 0
@@ -1382,55 +1396,28 @@
  * Increase the size of the type table.
  */
 static int __grow_type_table(unsigned char** typetable, int* tablesize) {
-  unsigned char* oldtable = *typetable;
-  int newsize = *tablesize * 2;
+  unsigned char* old_table = *typetable;
+  int new_size = *tablesize * 2;
 
-  if (newsize < getpagesize()) newsize = getpagesize();
+  if (new_size < getpagesize()) new_size = getpagesize();
 
   if (*tablesize == STATIC_ARG_TBL_SIZE) {
-    *typetable = mmap(NULL, newsize, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
-    if (*typetable == MAP_FAILED) return (-1);
-    bcopy(oldtable, *typetable, *tablesize);
+    *typetable = static_cast<unsigned char*>(mmap(NULL, new_size,
+                                                  PROT_WRITE | PROT_READ,
+                                                  MAP_ANON | MAP_PRIVATE, -1, 0));
+    if (*typetable == MAP_FAILED) return -1;
+    bcopy(old_table, *typetable, *tablesize);
   } else {
-    unsigned char* new = mmap(NULL, newsize, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
-    if (new == MAP_FAILED) return (-1);
-    memmove(new, *typetable, *tablesize);
+    unsigned char* new_table = static_cast<unsigned char*>(mmap(NULL, new_size,
+                                                                PROT_WRITE | PROT_READ,
+                                                                MAP_ANON | MAP_PRIVATE, -1, 0));
+    if (new_table == MAP_FAILED) return -1;
+    memmove(new_table, *typetable, *tablesize);
     munmap(*typetable, *tablesize);
-    *typetable = new;
+    *typetable = new_table;
   }
-  memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize));
+  memset(*typetable + *tablesize, T_UNUSED, (new_size - *tablesize));
 
-  *tablesize = newsize;
-  return (0);
-}
-
-static int exponent(wchar_t* p0, int exp, int fmtch) {
-  wchar_t *p, *t;
-  wchar_t expbuf[MAXEXPDIG];
-
-  p = p0;
-  *p++ = fmtch;
-  if (exp < 0) {
-    exp = -exp;
-    *p++ = '-';
-  } else
-    *p++ = '+';
-  t = expbuf + MAXEXPDIG;
-  if (exp > 9) {
-    do {
-      *--t = to_char(exp % 10);
-    } while ((exp /= 10) > 9);
-    *--t = to_char(exp);
-    for (; t < expbuf + MAXEXPDIG; *p++ = *t++) /* nothing */;
-  } else {
-    /*
-     * Exponents for decimal floating point conversions
-     * (%[eEgG]) must be at least two characters long,
-     * whereas exponents for hexadecimal conversions can
-     * be only one character long.
-     */
-    if (fmtch == 'e' || fmtch == 'E') *p++ = '0';
-    *p++ = to_char(exp);
-  }
-  return (p - p0);
+  *tablesize = new_size;
+  return 0;
 }
diff --git a/libc/stdio/vfwscanf.c b/libc/stdio/vfwscanf.c
index c74c6e7..bbe534c 100644
--- a/libc/stdio/vfwscanf.c
+++ b/libc/stdio/vfwscanf.c
@@ -42,8 +42,6 @@
 #include <wctype.h>
 #include "local.h"
 
-#include "floatio.h"
-
 #define BUF 513 /* Maximum length of numeric string. */
 
 /*
diff --git a/tests/Android.bp b/tests/Android.bp
index 742f44a..66d9aec 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -194,7 +194,6 @@
 cc_defaults {
     name: "bionic_fortify_tests_defaults",
     cflags: [
-        "-Wno-error",
         "-U_FORTIFY_SOURCE",
     ],
     srcs: ["fortify_test_main.cpp"],
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 97b222c..e07980e 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -302,6 +302,13 @@
   EXPECT_STREQ("<a>", buf);
 }
 
+TEST(STDIO_TEST, snprintf_C) { // Synonym for %lc.
+  char buf[BUFSIZ];
+  wchar_t wc = L'a';
+  EXPECT_EQ(3, snprintf(buf, sizeof(buf), "<%C>", wc));
+  EXPECT_STREQ("<a>", buf);
+}
+
 TEST(STDIO_TEST, snprintf_ls) {
   char buf[BUFSIZ];
   wchar_t* ws = NULL;
@@ -314,6 +321,18 @@
   EXPECT_STREQ("<hi>", buf);
 }
 
+TEST(STDIO_TEST, snprintf_S) { // Synonym for %ls.
+  char buf[BUFSIZ];
+  wchar_t* ws = NULL;
+  EXPECT_EQ(8, snprintf(buf, sizeof(buf), "<%S>", ws));
+  EXPECT_STREQ("<(null)>", buf);
+
+  wchar_t chars[] = { L'h', L'i', 0 };
+  ws = chars;
+  EXPECT_EQ(4, snprintf(buf, sizeof(buf), "<%S>", ws));
+  EXPECT_STREQ("<hi>", buf);
+}
+
 TEST(STDIO_TEST, snprintf_n) {
 #if defined(__BIONIC__)
   // http://b/14492135
@@ -584,6 +603,24 @@
   ASSERT_EQ(std::wstring(L"0x1.921fb54411744p+1"), buf);
 }
 
+TEST(STDIO_TEST, swprintf_lc) {
+  constexpr size_t nchars = 32;
+  wchar_t buf[nchars];
+
+  wint_t wc = L'a';
+  EXPECT_EQ(3, swprintf(buf, nchars, L"<%lc>", wc));
+  EXPECT_EQ(std::wstring(L"<a>"), buf);
+}
+
+TEST(STDIO_TEST, swprintf_C) { // Synonym for %lc.
+  constexpr size_t nchars = 32;
+  wchar_t buf[nchars];
+
+  wint_t wc = L'a';
+  EXPECT_EQ(3, swprintf(buf, nchars, L"<%C>", wc));
+  EXPECT_EQ(std::wstring(L"<a>"), buf);
+}
+
 TEST(STDIO_TEST, swprintf_ls) {
   constexpr size_t nchars = 32;
   wchar_t buf[nchars];
@@ -595,6 +632,17 @@
   ASSERT_EQ(std::wstring(kWideString), buf);
 }
 
+TEST(STDIO_TEST, swprintf_S) { // Synonym for %ls.
+  constexpr size_t nchars = 32;
+  wchar_t buf[nchars];
+
+  static const wchar_t kWideString[] = L"Hello\uff41 World";
+  ASSERT_EQ(12, swprintf(buf, nchars, L"%S", kWideString));
+  ASSERT_EQ(std::wstring(kWideString), buf);
+  ASSERT_EQ(12, swprintf(buf, 13, L"%S", kWideString));
+  ASSERT_EQ(std::wstring(kWideString), buf);
+}
+
 TEST(STDIO_TEST, snprintf_d_INT_MAX) {
   char buf[BUFSIZ];
   snprintf(buf, sizeof(buf), "%d", INT_MAX);
@@ -639,6 +687,30 @@
   EXPECT_STREQ("-9223372036854775808", buf);
 }
 
+TEST(STDIO_TEST, snprintf_o_UINT_MAX) {
+  char buf[BUFSIZ];
+  snprintf(buf, sizeof(buf), "%o", UINT_MAX);
+  EXPECT_STREQ("37777777777", buf);
+}
+
+TEST(STDIO_TEST, snprintf_u_UINT_MAX) {
+  char buf[BUFSIZ];
+  snprintf(buf, sizeof(buf), "%u", UINT_MAX);
+  EXPECT_STREQ("4294967295", buf);
+}
+
+TEST(STDIO_TEST, snprintf_x_UINT_MAX) {
+  char buf[BUFSIZ];
+  snprintf(buf, sizeof(buf), "%x", UINT_MAX);
+  EXPECT_STREQ("ffffffff", buf);
+}
+
+TEST(STDIO_TEST, snprintf_X_UINT_MAX) {
+  char buf[BUFSIZ];
+  snprintf(buf, sizeof(buf), "%X", UINT_MAX);
+  EXPECT_STREQ("FFFFFFFF", buf);
+}
+
 TEST(STDIO_TEST, snprintf_e) {
   char buf[BUFSIZ];
 
diff --git a/tools/versioner/src/Android.bp b/tools/versioner/src/Android.bp
index c5afa56..b86228f 100644
--- a/tools/versioner/src/Android.bp
+++ b/tools/versioner/src/Android.bp
@@ -1,6 +1,8 @@
 cc_binary_host {
     name: "versioner",
 
+    cpp_std: "gnu++17",
+
     srcs: [
         "versioner.cpp",
         "Arch.cpp",
@@ -29,6 +31,7 @@
         "-Wextra",
         "-Werror",
         "-Wno-unused-parameter",
+        "-fno-omit-frame-pointer",
 
         "-D__STDC_CONSTANT_MACROS",
         "-D__STDC_LIMIT_MACROS",
@@ -37,7 +40,6 @@
     target: {
         host: {
             cppflags: [
-                "-std=gnu++1z",
                 "-fno-rtti",
             ],
         },
diff --git a/tools/versioner/src/CompilationType.cpp b/tools/versioner/src/CompilationType.cpp
index e9d31cb..7e7bb5d 100644
--- a/tools/versioner/src/CompilationType.cpp
+++ b/tools/versioner/src/CompilationType.cpp
@@ -21,6 +21,7 @@
 
 std::string to_string(const CompilationType& type) {
   std::stringstream ss;
-  ss << to_string(type.arch) << "-" << type.api_level << " [fob = " << type.file_offset_bits << "]";
+  ss << to_string(type.arch) << "-" << type.api_level << " [" << (type.cpp ? "c++" : "c")
+     << ", fob = " << type.file_offset_bits << "]";
   return ss.str();
 }
diff --git a/tools/versioner/src/CompilationType.h b/tools/versioner/src/CompilationType.h
index 7d516b2..2f4cf5c 100644
--- a/tools/versioner/src/CompilationType.h
+++ b/tools/versioner/src/CompilationType.h
@@ -25,12 +25,13 @@
 
 struct CompilationType {
   Arch arch;
+  bool cpp;
   int api_level;
   int file_offset_bits;
 
  private:
   auto tie() const {
-    return std::tie(arch, api_level, file_offset_bits);
+    return std::tie(arch, cpp, api_level, file_offset_bits);
   }
 
  public:
@@ -49,11 +50,13 @@
   size_t operator()(CompilationType type) const {
     struct {
       int32_t arch : 3;
+      int32_t cpp : 1;
       int32_t api_level : 6;
       int32_t file_offset_bits : 1;
-      int32_t padding : 22;
+      int32_t padding : 21;
     } packed;
     packed.arch = static_cast<int32_t>(type.arch);
+    packed.cpp = type.cpp;
     packed.api_level = type.api_level;
     packed.file_offset_bits = (type.file_offset_bits == 64);
     packed.padding = 0;
diff --git a/tools/versioner/src/DeclarationDatabase.cpp b/tools/versioner/src/DeclarationDatabase.cpp
index aef4f55..4443834 100644
--- a/tools/versioner/src/DeclarationDatabase.cpp
+++ b/tools/versioner/src/DeclarationDatabase.cpp
@@ -35,6 +35,22 @@
 
 using namespace clang;
 
+static bool shouldMangle(MangleContext* mangler, NamedDecl* decl) {
+  // Passing a decl with static linkage to the mangler gives incorrect results.
+  // Check some things ourselves before handing it off to the mangler.
+  if (auto FD = dyn_cast<FunctionDecl>(decl)) {
+    if (FD->isExternC()) {
+      return false;
+    }
+
+    if (FD->isInExternCContext()) {
+      return false;
+    }
+  }
+
+  return mangler->shouldMangleDeclName(decl);
+}
+
 class Visitor : public RecursiveASTVisitor<Visitor> {
   HeaderDatabase& database;
   CompilationType type;
@@ -54,9 +70,14 @@
       }
     }
 
+    // <math.h> maps fool onto foo on 32-bit, since long double is the same as double.
+    if (auto asm_attr = decl->getAttr<AsmLabelAttr>()) {
+      return asm_attr->getLabel();
+    }
+
     // The decl might not have a name (e.g. bitfields).
     if (auto identifier = decl->getIdentifier()) {
-      if (mangler->shouldMangleDeclName(decl)) {
+      if (shouldMangle(mangler.get(), decl)) {
         std::string mangled;
         llvm::raw_string_ostream ss(mangled);
         mangler->mangleName(decl, ss);
diff --git a/tools/versioner/src/Driver.cpp b/tools/versioner/src/Driver.cpp
index 8a8e00a..8911190 100644
--- a/tools/versioner/src/Driver.cpp
+++ b/tools/versioner/src/Driver.cpp
@@ -98,9 +98,16 @@
                                    CompilationType type,
                                    const std::vector<std::string>& include_dirs) {
   std::vector<std::string> cmd = { "versioner" };
-  cmd.push_back("-std=c11");
-  cmd.push_back("-x");
-  cmd.push_back("c");
+  if (type.cpp) {
+    cmd.push_back("-std=gnu++11");
+    cmd.push_back("-x");
+    cmd.push_back("c++");
+  } else {
+    cmd.push_back("-std=gnu11");
+    cmd.push_back("-x");
+    cmd.push_back("c");
+  }
+
   cmd.push_back("-fsyntax-only");
 
   cmd.push_back("-Wall");
diff --git a/tools/versioner/src/versioner.cpp b/tools/versioner/src/versioner.cpp
index 83b4027..a082f07 100644
--- a/tools/versioner/src/versioner.cpp
+++ b/tools/versioner/src/versioner.cpp
@@ -36,6 +36,7 @@
 #include <set>
 #include <sstream>
 #include <string>
+#include <string_view>
 #include <thread>
 #include <unordered_map>
 #include <vector>
@@ -82,18 +83,28 @@
 
 namespace {
 struct HeaderLocationInformation {
-  std::string header_dir;
+  std::string header_path;
   std::string dependency_dir;
   // Absolute paths to ignore all children -- including subdirectories -- of.
   std::unordered_set<std::string> ignored_directories;
 };
 }
 
+static bool is_dir(const std::string& path) {
+  struct stat st;
+  return stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode);
+}
+
 static CompilationRequirements collectRequirements(const Arch& arch,
                                                    const HeaderLocationInformation& location) {
   std::vector<std::string> headers =
-      collectHeaders(location.header_dir, location.ignored_directories);
-  std::vector<std::string> dependencies = { location.header_dir };
+      collectHeaders(location.header_path, location.ignored_directories);
+  std::vector<std::string> dependencies;
+
+  if (is_dir(location.header_path)) {
+    dependencies.emplace_back(location.header_path);
+  }
+
   if (!location.dependency_dir.empty()) {
     auto collect_children = [&dependencies](const std::string& dir_path) {
       DIR* dir = opendir(dir_path.c_str());
@@ -159,10 +170,12 @@
       }
 
       for (int file_offset_bits : { 32, 64 }) {
-        CompilationType type = {
-          .arch = arch, .api_level = api_level, .file_offset_bits = file_offset_bits
-        };
-        result.insert(type);
+        for (bool cpp : { true, false }) {
+          CompilationType type = {
+            .arch = arch, .cpp = cpp, .api_level = api_level, .file_offset_bits = file_offset_bits
+          };
+          result.insert(type);
+        }
       }
     }
   }
@@ -175,7 +188,7 @@
     errx(1, "compileHeaders received no CompilationTypes");
   }
 
-  auto vfs = createCommonVFS(location.header_dir, location.dependency_dir, add_include);
+  auto vfs = createCommonVFS(location.header_path, location.dependency_dir, add_include);
 
   size_t thread_count = max_thread_count;
   std::vector<std::thread> threads;
@@ -282,7 +295,8 @@
       for (const auto& inline_def_it : inline_definitions) {
         auto intersection = Intersection(compilation_types, inline_def_it.second);
         if (!intersection.empty()) {
-          fprintf(stderr, "versioner: conflicting inline definitions:\n");
+          fprintf(stderr, "versioner: conflicting inline definitions for symbol %s:\n",
+                  symbol.name.c_str());
           fprintf(stderr, "  declarations visible in: %s\n", Join(intersection, ", ").c_str());
           decl->dump(cwd, stderr, 4);
           inline_def_it.first->dump(cwd, stderr, 4);
@@ -526,8 +540,8 @@
         if (stat(platform_dir.c_str(), &st) != 0) {
           err(1, "failed to stat platform directory '%s'", platform_dir.c_str());
         }
-        if (!S_ISDIR(st.st_mode)) {
-          errx(1, "'%s' is not a directory", optarg);
+        if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) {
+          errx(1, "'%s' is not a file or directory", optarg);
         }
         break;
       }
@@ -598,13 +612,13 @@
   if (optind == argc) {
     // Neither HEADER_PATH nor DEPS_PATH were specified, so try to figure them out.
     std::string versioner_dir = to_string(top) + "/bionic/tools/versioner";
-    location.header_dir = versioner_dir + "/current";
+    location.header_path = versioner_dir + "/current";
     location.dependency_dir = versioner_dir + "/dependencies";
     if (platform_dir.empty()) {
       platform_dir = versioner_dir + "/platforms";
     }
   } else {
-    if (!android::base::Realpath(argv[optind], &location.header_dir)) {
+    if (!android::base::Realpath(argv[optind], &location.header_path)) {
       err(1, "failed to get realpath for path '%s'", argv[optind]);
     }
 
@@ -616,8 +630,8 @@
   // Every file that lives in bits/fortify is logically a part of a header outside of bits/fortify.
   // This makes the files there impossible to build on their own.
   if (ignore_fortify_headers) {
-    std::string fortify_path = location.header_dir;
-    if (!android::base::EndsWith(location.header_dir, "/")) {
+    std::string fortify_path = location.header_path;
+    if (!android::base::EndsWith(location.header_path, "/")) {
       fortify_path += '/';
     }
     fortify_path += "bits/fortify";
@@ -634,10 +648,8 @@
 
 
   struct stat st;
-  if (const char *dir = location.header_dir.c_str(); stat(dir, &st) != 0) {
-    err(1, "failed to stat '%s'", dir);
-  } else if (!S_ISDIR(st.st_mode)) {
-    errx(1, "'%s' is not a directory", dir);
+  if (const char *path = location.header_path.c_str(); stat(path, &st) != 0) {
+    err(1, "failed to stat '%s'", path);
   }
 
   std::set<CompilationType> compilation_types;
@@ -663,7 +675,7 @@
 
   bool failed = false;
   if (dump) {
-    declaration_database->dump(location.header_dir + "/");
+    declaration_database->dump(location.header_path + "/");
   } else {
     if (!sanityCheck(declaration_database.get())) {
       printf("versioner: sanity check failed\n");
@@ -679,7 +691,8 @@
   }
 
   if (!preprocessor_output_path.empty() && (force || !failed)) {
-    failed = !preprocessHeaders(preprocessor_output_path, location.header_dir, declaration_database.get());
+    failed = !preprocessHeaders(preprocessor_output_path, location.header_path,
+                                declaration_database.get());
   }
   return failed;
 }
diff --git a/tools/versioner/tests/extern_cpp/headers/string.h b/tools/versioner/tests/extern_cpp/headers/string.h
new file mode 100644
index 0000000..5ac43ac
--- /dev/null
+++ b/tools/versioner/tests/extern_cpp/headers/string.h
@@ -0,0 +1,18 @@
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define __RENAME(x) __asm__(#x)
+
+#if defined(__cplusplus)
+extern "C++" char* basename(char*) __RENAME(__gnu_basename) __INTRODUCED_IN(23);
+extern "C++" const char* basename(const char*) __RENAME(__gnu_basename) __INTRODUCED_IN(23);
+#else
+char* basename(const char*) __RENAME(__gnu_basename) __INTRODUCED_IN(23);
+#endif
+
+char* foo() __INTRODUCED_IN(8);
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/tools/versioner/tests/extern_cpp/platforms/android-21/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/extern_cpp/platforms/android-21/arch-arm/symbols/libc.so.functions.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tools/versioner/tests/extern_cpp/platforms/android-21/arch-arm/symbols/libc.so.functions.txt
@@ -0,0 +1 @@
+
diff --git a/tools/versioner/tests/extern_cpp/platforms/android-23/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/extern_cpp/platforms/android-23/arch-arm/symbols/libc.so.functions.txt
new file mode 100644
index 0000000..36fe04f
--- /dev/null
+++ b/tools/versioner/tests/extern_cpp/platforms/android-23/arch-arm/symbols/libc.so.functions.txt
@@ -0,0 +1 @@
+__gnu_basename
diff --git a/tools/versioner/tests/extern_cpp/run.sh b/tools/versioner/tests/extern_cpp/run.sh
new file mode 100644
index 0000000..e320c95
--- /dev/null
+++ b/tools/versioner/tests/extern_cpp/run.sh
@@ -0,0 +1 @@
+versioner headers -p platforms -r arm -a 21 -a 23 -i
diff --git a/tools/versioner/tests/extern_cpp_mismatch/headers/string.h b/tools/versioner/tests/extern_cpp_mismatch/headers/string.h
new file mode 100644
index 0000000..66133d8
--- /dev/null
+++ b/tools/versioner/tests/extern_cpp_mismatch/headers/string.h
@@ -0,0 +1,16 @@
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#define __RENAME(x) __asm__(#x)
+
+#if defined(__cplusplus)
+extern "C++" char* basename(char*) __RENAME(__gnu_basename) __INTRODUCED_IN(23);
+extern "C++" const char* basename(const char*) __RENAME(__gnu_basename) __INTRODUCED_IN(23);
+#else
+char* basename(const char*) __RENAME(__gnu_basename) __INTRODUCED_IN(23);
+#endif
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/tools/versioner/tests/extern_cpp_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/extern_cpp_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/tools/versioner/tests/extern_cpp_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
@@ -0,0 +1 @@
+foo
diff --git a/tools/versioner/tests/extern_cpp_mismatch/run.sh b/tools/versioner/tests/extern_cpp_mismatch/run.sh
new file mode 100644
index 0000000..a34fda8
--- /dev/null
+++ b/tools/versioner/tests/extern_cpp_mismatch/run.sh
@@ -0,0 +1 @@
+versioner headers -p platforms -r arm -a 9 -i
\ No newline at end of file
diff --git a/tools/versioner/tests/multiple_definition/expected_fail b/tools/versioner/tests/multiple_definition/expected_fail
index 7070390..cb4acc6 100644
--- a/tools/versioner/tests/multiple_definition/expected_fail
+++ b/tools/versioner/tests/multiple_definition/expected_fail
@@ -1,5 +1,5 @@
-versioner: conflicting inline definitions:
-  declarations visible in: arm-9 [fob = 32], arm-9 [fob = 64], arm-12 [fob = 32], arm-12 [fob = 64]
+versioner: conflicting inline definitions for symbol foo:
+  declarations visible in: arm-9 [c, fob = 32], arm-9 [c, fob = 64], arm-12 [c, fob = 32], arm-12 [c, fob = 64], arm-9 [c++, fob = 32], arm-9 [c++, fob = 64], arm-12 [c++, fob = 32], arm-12 [c++, fob = 64]
     static definition @ headers/foo.h:5:1
       no availability
     static definition @ headers/bar.h:5:1
diff --git a/tools/versioner/tests/unnamed_bitfield/headers/foo.h b/tools/versioner/tests/unnamed_bitfield/headers/foo.h
new file mode 100644
index 0000000..58686c3
--- /dev/null
+++ b/tools/versioner/tests/unnamed_bitfield/headers/foo.h
@@ -0,0 +1,8 @@
+// <sys/timex.h> was causing a segfault when compiled in C++ mode because
+// versioner was trying to mangle the name of an unnamed bitfield.
+struct foo {
+  int : 32;
+  int : 32;
+  int : 32;
+  int : 32;
+};
diff --git a/tools/versioner/tests/unnamed_bitfield/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/unnamed_bitfield/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/tools/versioner/tests/unnamed_bitfield/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
@@ -0,0 +1 @@
+foo
diff --git a/tools/versioner/tests/unnamed_bitfield/run.sh b/tools/versioner/tests/unnamed_bitfield/run.sh
new file mode 100644
index 0000000..a34fda8
--- /dev/null
+++ b/tools/versioner/tests/unnamed_bitfield/run.sh
@@ -0,0 +1 @@
+versioner headers -p platforms -r arm -a 9 -i
\ No newline at end of file