Merge "linker: add experimental support for SHT_RELR sections."
diff --git a/android-changes-for-ndk-developers.md b/android-changes-for-ndk-developers.md
index 48800ca..e4eb48a 100644
--- a/android-changes-for-ndk-developers.md
+++ b/android-changes-for-ndk-developers.md
@@ -389,7 +389,7 @@
 Android allows `dlclose` to unload a library even if there are still
 thread-local variables with non-trivial destructors. This leads to
 crashes when a thread exits and attempts to call the destructor, the
-code for which has been unloaded (as in [issue 360]).
+code for which has been unloaded (as in [issue 360], fixed in P).
 
 [issue 360]: https://github.com/android-ndk/ndk/issues/360
 
@@ -397,8 +397,8 @@
 set (so that calls to `dlclose` don't actually unload the library)
 are possible workarounds.
 
-|                   | Pre-M                      | M+      |
-| ----------------- | -------------------------- | ------- |
-| No workaround     | Works for static STL       | Broken  |
-| `-Wl,-z,nodelete` | Works for static STL       | Works   |
-| No `dlclose`      | Works                      | Works   |
+|                   | Pre-M                      | M+      | P+    |
+| ----------------- | -------------------------- | ------- | ----- |
+| No workaround     | Works for static STL       | Broken  | Works |
+| `-Wl,-z,nodelete` | Works for static STL       | Works   | Works |
+| No `dlclose`      | Works                      | Works   | Works |
diff --git a/benchmarks/bionic_benchmarks.cpp b/benchmarks/bionic_benchmarks.cpp
index fe7a1b1..d8635cd 100644
--- a/benchmarks/bionic_benchmarks.cpp
+++ b/benchmarks/bionic_benchmarks.cpp
@@ -305,7 +305,7 @@
 }
 
 void RegisterGoogleBenchmarks(bench_opts_t primary_opts, bench_opts_t secondary_opts,
-                         std::string fn_name, args_vector_t* run_args) {
+                              const std::string& fn_name, args_vector_t* run_args) {
   if (g_str_to_func.find(fn_name) == g_str_to_func.end()) {
     errx(1, "ERROR: No benchmark for function %s", fn_name.c_str());
   }
@@ -320,7 +320,7 @@
   }
 
   benchmark_func_t benchmark_function = g_str_to_func.at(fn_name).first;
-  for (std::vector<int> args : (*run_args)) {
+  for (const std::vector<int>& args : (*run_args)) {
     auto registration = benchmark::RegisterBenchmark(fn_name.c_str(), LockAndRun,
                                                      benchmark_function,
                                                      cpu_to_use)->Args(args);
@@ -335,13 +335,13 @@
   // Register any of the extra benchmarks that were specified in the options.
   args_vector_t arg_vector;
   args_vector_t* run_args = &arg_vector;
-  for (std::string extra_fn : cmdline_opts.extra_benchmarks) {
+  for (const std::string& extra_fn : cmdline_opts.extra_benchmarks) {
     android::base::Trim(extra_fn);
-    size_t first_space_pos = extra_fn.find(" ");
+    size_t first_space_pos = extra_fn.find(' ');
     std::string fn_name = extra_fn.substr(0, first_space_pos);
     std::string cmd_args;
     if (first_space_pos != std::string::npos) {
-      cmd_args = extra_fn.substr(extra_fn.find(" ") + 1);
+      cmd_args = extra_fn.substr(extra_fn.find(' ') + 1);
     } else {
       cmd_args = "";
     }
diff --git a/benchmarks/stdio_benchmark.cpp b/benchmarks/stdio_benchmark.cpp
index 965a3ae..9c81e0f 100644
--- a/benchmarks/stdio_benchmark.cpp
+++ b/benchmarks/stdio_benchmark.cpp
@@ -29,7 +29,7 @@
   memset(line, 'x', sizeof(line));
   line[sizeof(line) - 1] = '\0';
 
-  FILE* fp = fopen(tf.path, "w");
+  FILE* fp = fopen(tf.path, "we");
   for (size_t i = 0; i < 4096; ++i) fputs(line, fp);
   fclose(fp);
 }
diff --git a/benchmarks/util.h b/benchmarks/util.h
index a546764..3225bc2 100644
--- a/benchmarks/util.h
+++ b/benchmarks/util.h
@@ -29,7 +29,7 @@
 
 extern std::map<std::string, std::pair<benchmark_func_t, std::string>> g_str_to_func;
 
-static int  __attribute__((unused)) EmplaceBenchmark (std::string fn_name, benchmark_func_t fn_ptr, std::string arg = "") {
+static int  __attribute__((unused)) EmplaceBenchmark(const std::string& fn_name, benchmark_func_t fn_ptr, const std::string& arg = "") {
   g_map_lock.lock();
   g_str_to_func.emplace(std::string(fn_name), std::make_pair(fn_ptr, arg));
   g_map_lock.unlock();
diff --git a/libc/Android.bp b/libc/Android.bp
index 5f04e28..a8c518d 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -18,7 +18,7 @@
     "stdio/refill.c",
     "stdio/stdio.cpp",
     "stdio/stdio_ext.cpp",
-    "stdio/vfscanf.c",
+    "stdio/vfscanf.cpp",
     "stdio/vfwscanf.c",
     "stdlib/exit.c",
 ]
@@ -39,6 +39,7 @@
     "-Wall",
     "-Wextra",
     "-Wunused",
+    "-Wno-char-subscripts",
     "-Wno-deprecated-declarations",
     "-Wno-gcc-compat",
     "-Wframe-larger-than=2048",
@@ -1669,6 +1670,7 @@
 cc_library {
     defaults: ["libc_defaults"],
     name: "libc",
+    static_ndk_lib: true,
     product_variables: {
         platform_sdk_version: {
             asflags: ["-DPLATFORM_SDK_VERSION=%d"],
@@ -1781,6 +1783,7 @@
         "bionic/new.cpp",
     ],
     name: "libstdc++",
+    static_ndk_lib: true,
     system_shared_libs: ["libc"],
     static_libs: ["libasync_safe"],
 
diff --git a/libc/bionic/icu.cpp b/libc/bionic/icu.cpp
index 78e551b..c09c9ea 100644
--- a/libc/bionic/icu.cpp
+++ b/libc/bionic/icu.cpp
@@ -68,7 +68,7 @@
   }
   free(namelist);
 
-  if (max_version == -1 || max_version < ICUDATA_VERSION_MIN) {
+  if (max_version < ICUDATA_VERSION_MIN) {
     async_safe_write_log(ANDROID_LOG_ERROR, "bionic-icu", "couldn't find an ICU .dat file");
     return false;
   }
diff --git a/libc/include/android/legacy_errno_inlines.h b/libc/include/android/legacy_errno_inlines.h
index 8f08074..9f116fa 100644
--- a/libc/include/android/legacy_errno_inlines.h
+++ b/libc/include/android/legacy_errno_inlines.h
@@ -29,11 +29,12 @@
 #ifndef _ANDROID_LEGACY_ERRNO_INLINES_H
 #define _ANDROID_LEGACY_ERRNO_INLINES_H
 
-#include <errno.h>
 #include <sys/cdefs.h>
 
 #if __ANDROID_API__ < __ANDROID_API_L__
 
+#include <errno.h>
+
 __BEGIN_DECLS
 
 static __inline int __attribute__((deprecated)) __set_errno(int n) {
diff --git a/libc/include/android/legacy_fenv_inlines_arm.h b/libc/include/android/legacy_fenv_inlines_arm.h
index de024cf..6f2c959 100644
--- a/libc/include/android/legacy_fenv_inlines_arm.h
+++ b/libc/include/android/legacy_fenv_inlines_arm.h
@@ -29,10 +29,12 @@
 #ifndef ANDROID_LEGACY_FENV_INLINES_ARM_H
 #define ANDROID_LEGACY_FENV_INLINES_ARM_H
 
-#include <fenv.h>
+#include <sys/cdefs.h>
 
 #if __ANDROID_API__ < __ANDROID_API_L__ && defined(__arm__)
 
+#include <fenv.h>
+
 __BEGIN_DECLS
 
 #define FPSCR_RMODE_SHIFT 22
diff --git a/libc/include/android/legacy_fenv_inlines_mips.h b/libc/include/android/legacy_fenv_inlines_mips.h
index 10b93c0..43ad360 100644
--- a/libc/include/android/legacy_fenv_inlines_mips.h
+++ b/libc/include/android/legacy_fenv_inlines_mips.h
@@ -29,10 +29,12 @@
 #ifndef ANDROID_LEGACY_FENV_INLINES_MIPS_H
 #define ANDROID_LEGACY_FENV_INLINES_MIPS_H
 
-#include <fenv.h>
+#include <sys/cdefs.h>
 
 #if __ANDROID_API__ < __ANDROID_API_L__ && (defined(__mips__) && !defined(__LP64__))
 
+#include <fenv.h>
+
 __BEGIN_DECLS
 
 #define FCSR_CAUSE_SHIFT 10
diff --git a/libc/include/android/legacy_signal_inlines.h b/libc/include/android/legacy_signal_inlines.h
index 4d474b0..44c2f4f 100644
--- a/libc/include/android/legacy_signal_inlines.h
+++ b/libc/include/android/legacy_signal_inlines.h
@@ -29,16 +29,16 @@
 #ifndef _ANDROID_LEGACY_SIGNAL_INLINES_H_
 #define _ANDROID_LEGACY_SIGNAL_INLINES_H_
 
+#include <sys/cdefs.h>
+
+#if __ANDROID_API__ < __ANDROID_API_L__
+
 #include <errno.h>
 #include <signal.h>
 #include <string.h>
-#include <sys/cdefs.h>
-
 
 __BEGIN_DECLS
 
-#if __ANDROID_API__ < __ANDROID_API_L__
-
 sighandler_t bsd_signal(int __signal, sighandler_t __handler) __REMOVED_IN(21);
 
 /* These weren't introduced until L. */
@@ -117,8 +117,8 @@
   return bsd_signal(s, f);
 }
 
-#endif /* __ANDROID_API__ < __ANDROID_API_L__ */
-
 __END_DECLS
 
+#endif /* __ANDROID_API__ < __ANDROID_API_L__ */
+
 #endif /* _ANDROID_LEGACY_SIGNAL_INLINES_H_ */
diff --git a/libc/include/android/legacy_stdlib_inlines.h b/libc/include/android/legacy_stdlib_inlines.h
index a1cc590..4896d2e 100644
--- a/libc/include/android/legacy_stdlib_inlines.h
+++ b/libc/include/android/legacy_stdlib_inlines.h
@@ -29,9 +29,6 @@
 #ifndef _ANDROID_LEGACY_STDLIB_INLINES_H_
 #define _ANDROID_LEGACY_STDLIB_INLINES_H_
 
-#include <errno.h>
-#include <float.h>
-#include <stdlib.h>
 #include <sys/cdefs.h>
 
 #if __ANDROID_API__ < __ANDROID_API_K__
@@ -52,6 +49,10 @@
 
 #if __ANDROID_API__ < __ANDROID_API_L__
 
+#include <errno.h>
+#include <float.h>
+#include <stdlib.h>
+
 __BEGIN_DECLS
 
 static __inline float strtof(const char* nptr, char** endptr) {
diff --git a/libc/include/android/legacy_strings_inlines.h b/libc/include/android/legacy_strings_inlines.h
index 6679c30..5d63c5a 100644
--- a/libc/include/android/legacy_strings_inlines.h
+++ b/libc/include/android/legacy_strings_inlines.h
@@ -29,16 +29,19 @@
 #ifndef _ANDROID_LEGACY_STRINGS_INLINES_H_
 #define _ANDROID_LEGACY_STRINGS_INLINES_H_
 
-#include <strings.h>
 #include <sys/cdefs.h>
 
+#if defined(__i386__) && __ANDROID_API__ < __ANDROID_API_J_MR2__
+
+#include <strings.h>
+
 __BEGIN_DECLS
 
-#if defined(__i386__) && __ANDROID_API__ < __ANDROID_API_J_MR2__
 /* Everyone except x86 had ffs since the beginning. */
 static __inline int ffs(int __n) { return __builtin_ffs(__n); }
-#endif
 
 __END_DECLS
 
 #endif
+
+#endif
diff --git a/libc/include/android/legacy_sys_mman_inlines.h b/libc/include/android/legacy_sys_mman_inlines.h
index 7eb537e..160e6fe 100644
--- a/libc/include/android/legacy_sys_mman_inlines.h
+++ b/libc/include/android/legacy_sys_mman_inlines.h
@@ -29,12 +29,13 @@
 #pragma once
 
 #include <sys/cdefs.h>
-#include <sys/mman.h>
-#include <sys/syscall.h>
-#include <unistd.h>
 
 #if __ANDROID_API__ < __ANDROID_API_L__
 
+#include <errno.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
 __BEGIN_DECLS
 
 /*
diff --git a/libc/include/android/legacy_sys_stat_inlines.h b/libc/include/android/legacy_sys_stat_inlines.h
index bbf54c0..9521694 100644
--- a/libc/include/android/legacy_sys_stat_inlines.h
+++ b/libc/include/android/legacy_sys_stat_inlines.h
@@ -30,10 +30,11 @@
 #define _ANDROID_LEGACY_SYS_STAT_INLINES_H_
 
 #include <sys/cdefs.h>
-#include <sys/stat.h>
 
 #if __ANDROID_API__ < __ANDROID_API_L__
 
+#include <sys/stat.h>
+
 __BEGIN_DECLS
 
 static __inline int mkfifo(const char* __path, mode_t __mode) {
diff --git a/libc/include/android/legacy_sys_wait_inlines.h b/libc/include/android/legacy_sys_wait_inlines.h
index 1124f8e..4298d76 100644
--- a/libc/include/android/legacy_sys_wait_inlines.h
+++ b/libc/include/android/legacy_sys_wait_inlines.h
@@ -30,12 +30,13 @@
 #define _ANDROID_LEGACY_SYS_WAIT_INLINES_H_
 
 #include <sys/cdefs.h>
+
+#if __ANDROID_API__ < __ANDROID_API_J_MR2__
+
 #include <sys/syscall.h>
 #include <sys/wait.h>
 #include <unistd.h>
 
-#if __ANDROID_API__ < __ANDROID_API_J_MR2__
-
 __BEGIN_DECLS
 
 static __inline pid_t wait4(pid_t pid, int* status, int options, struct rusage* rusage) {
diff --git a/libc/include/android/legacy_termios_inlines.h b/libc/include/android/legacy_termios_inlines.h
index 4ed56f0..a63dba1 100644
--- a/libc/include/android/legacy_termios_inlines.h
+++ b/libc/include/android/legacy_termios_inlines.h
@@ -30,14 +30,16 @@
 #define _ANDROID_LEGACY_TERMIOS_INLINES_H_
 
 #include <sys/cdefs.h>
+
+#if __ANDROID_API__ < __ANDROID_API_L__
+
+#include <linux/termios.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 
-#include <linux/termios.h>
-
-#if __ANDROID_API__ < __ANDROID_API_L__
 #define __BIONIC_TERMIOS_INLINE static __inline
 #include <bits/termios_inlines.h>
+
 #endif
 
 #endif /* _ANDROID_LEGACY_TERMIOS_INLINES_H_ */
diff --git a/libc/malloc_debug/MapData.h b/libc/malloc_debug/MapData.h
index 0238139..895f78f 100644
--- a/libc/malloc_debug/MapData.h
+++ b/libc/malloc_debug/MapData.h
@@ -41,7 +41,7 @@
   MapEntry(uintptr_t start, uintptr_t end, uintptr_t offset, const char* name, size_t name_len)
       : start(start), end(end), offset(offset), name(name, name_len) {}
 
-  MapEntry(uintptr_t pc) : start(pc), end(pc) {}
+  explicit MapEntry(uintptr_t pc) : start(pc), end(pc) {}
 
   uintptr_t start;
   uintptr_t end;
diff --git a/libc/malloc_debug/OptionData.h b/libc/malloc_debug/OptionData.h
index 80190f5..5c2d272 100644
--- a/libc/malloc_debug/OptionData.h
+++ b/libc/malloc_debug/OptionData.h
@@ -34,7 +34,7 @@
 
 class OptionData {
  public:
-  OptionData(DebugData* debug) : debug_(debug) {}
+  explicit OptionData(DebugData* debug) : debug_(debug) {}
   ~OptionData() = default;
 
  protected:
diff --git a/libc/malloc_debug/RecordData.h b/libc/malloc_debug/RecordData.h
index 6e19923..ccabac2 100644
--- a/libc/malloc_debug/RecordData.h
+++ b/libc/malloc_debug/RecordData.h
@@ -66,7 +66,7 @@
 
 class AllocEntry : public RecordEntry {
  public:
-  AllocEntry(void* pointer);
+  explicit AllocEntry(void* pointer);
   virtual ~AllocEntry() = default;
 
  protected:
@@ -92,7 +92,7 @@
 
 class FreeEntry : public AllocEntry {
  public:
-  FreeEntry(void* pointer);
+  explicit FreeEntry(void* pointer);
   virtual ~FreeEntry() = default;
 
   std::string GetString() const override;
diff --git a/libc/malloc_debug/TrackData.h b/libc/malloc_debug/TrackData.h
index f7486e9..9a649b9 100644
--- a/libc/malloc_debug/TrackData.h
+++ b/libc/malloc_debug/TrackData.h
@@ -46,7 +46,7 @@
 
 class TrackData : public OptionData {
  public:
-  TrackData(DebugData* debug_data);
+  explicit TrackData(DebugData* debug_data);
   virtual ~TrackData() = default;
 
   void GetList(std::vector<const Header*>* list);
diff --git a/libc/private/CachedProperty.h b/libc/private/CachedProperty.h
index 84ead01..bd67d74 100644
--- a/libc/private/CachedProperty.h
+++ b/libc/private/CachedProperty.h
@@ -38,7 +38,7 @@
 class CachedProperty {
  public:
   // The lifetime of `property_name` must be greater than that of this CachedProperty.
-  CachedProperty(const char* property_name)
+  explicit CachedProperty(const char* property_name)
     : property_name_(property_name),
       prop_info_(nullptr),
       cached_area_serial_(0),
diff --git a/libc/private/KernelArgumentBlock.h b/libc/private/KernelArgumentBlock.h
index 68d4999..747186c 100644
--- a/libc/private/KernelArgumentBlock.h
+++ b/libc/private/KernelArgumentBlock.h
@@ -32,7 +32,7 @@
 // constituents for easy access.
 class KernelArgumentBlock {
  public:
-  KernelArgumentBlock(void* raw_args) {
+  explicit KernelArgumentBlock(void* raw_args) {
     uintptr_t* args = reinterpret_cast<uintptr_t*>(raw_args);
     argc = static_cast<int>(*args);
     argv = reinterpret_cast<char**>(args + 1);
diff --git a/libc/private/bionic_mbstate.h b/libc/private/bionic_mbstate.h
index 292959a..352115a 100644
--- a/libc/private/bionic_mbstate.h
+++ b/libc/private/bionic_mbstate.h
@@ -29,6 +29,7 @@
 #ifndef _BIONIC_MBSTATE_H
 #define _BIONIC_MBSTATE_H
 
+#include <errno.h>
 #include <wchar.h>
 
 __BEGIN_DECLS
diff --git a/libc/stdio/vfscanf.c b/libc/stdio/vfscanf.cpp
similarity index 71%
rename from libc/stdio/vfscanf.c
rename to libc/stdio/vfscanf.cpp
index f0ed4ae..49d6bf6 100644
--- a/libc/stdio/vfscanf.c
+++ b/libc/stdio/vfscanf.cpp
@@ -37,74 +37,68 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/param.h>
 #include <wctype.h>
 #include "local.h"
 
 #include <private/bionic_ctype.h>
+#include <private/bionic_fortify.h>
+#include <private/bionic_mbstate.h>
 
 #define BUF 513 /* Maximum length of numeric string. */
 
-/*
- * Flags used during conversion.
- */
-#define LONG 0x00001       /* l: long or double */
-#define LONGDBL 0x00002    /* L: long double */
-#define SHORT 0x00004      /* h: short */
-#define SHORTSHORT 0x00008 /* hh: 8 bit integer */
-#define LLONG 0x00010      /* ll: long long (+ deprecated q: quad) */
-#define POINTER 0x00020    /* p: void * (as hex) */
-#define SIZEINT 0x00040    /* z: (signed) size_t */
-#define MAXINT 0x00080     /* j: intmax_t */
-#define PTRINT 0x00100     /* t: ptrdiff_t */
-#define NOSKIP 0x00200     /* [ or c: do not skip blanks */
-#define SUPPRESS 0x00400   /* *: suppress assignment */
-#define UNSIGNED 0x00800   /* %[oupxX] conversions */
+// Flags used during conversion.
+// Size/type:
+#define LONG       0x00001 // l: long or double
+#define LONGDBL    0x00002 // L: long double
+#define SHORT      0x00004 // h: short
+#define SHORTSHORT 0x00008 // hh: 8 bit integer
+#define LLONG      0x00010 // ll: long long (+ deprecated q: quad)
+#define POINTER    0x00020 // p: void* (as hex)
+#define SIZEINT    0x00040 // z: (signed) size_t
+#define MAXINT     0x00080 // j: intmax_t
+#define PTRINT     0x00100 // t: ptrdiff_t
+#define NOSKIP     0x00200 // [ or c: do not skip blanks
+// Modifiers:
+#define SUPPRESS   0x00400 // *: suppress assignment
+#define UNSIGNED   0x00800 // %[oupxX] conversions
+#define ALLOCATE   0x01000 // m: allocate a char*
+// Internal use during integer parsing:
+#define SIGNOK     0x02000 // +/- is (still) legal
+#define HAVESIGN   0x04000 // Sign detected
+#define NDIGITS    0x08000 // No digits detected
+#define PFXOK      0x10000 // "0x" prefix is (still) legal
+#define NZDIGITS   0x20000 // No zero digits detected
 
-/*
- * The following are used in numeric conversions only:
- * SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point;
- * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral.
- */
-#define SIGNOK 0x01000   /* +/- is (still) legal */
-#define HAVESIGN 0x02000 /* sign detected */
-#define NDIGITS 0x04000  /* no digits detected */
+// Conversion types.
+#define CT_CHAR 0   // %c conversion
+#define CT_CCL 1    // %[...] conversion
+#define CT_STRING 2 // %s conversion
+#define CT_INT 3    // Integer: strtoimax/strtoumax
+#define CT_FLOAT 4  // Float: strtod
 
-#define DPTOK 0x08000 /* (float) decimal point is still legal */
-#define EXPOK 0x10000 /* (float) exponent (e+3, etc) still legal */
-
-#define PFXOK 0x08000    /* 0x prefix is (still) legal */
-#define NZDIGITS 0x10000 /* no zero digits detected */
-
-/*
- * Conversion types.
- */
-#define CT_CHAR 0   /* %c conversion */
-#define CT_CCL 1    /* %[...] conversion */
-#define CT_STRING 2 /* %s conversion */
-#define CT_INT 3    /* integer, i.e., strtoimax or strtoumax */
-#define CT_FLOAT 4  /* floating, i.e., strtod */
-
-static u_char* __sccl(char*, u_char*);
+static const unsigned char* __sccl(char*, const unsigned char*);
 
 /*
  * Internal, unlocked version of vfscanf
  */
-int __svfscanf(FILE* fp, const char* fmt0, __va_list ap) {
-  u_char* fmt = (u_char*)fmt0;
+int __svfscanf(FILE* fp, const char* fmt0, va_list ap) {
+  const unsigned char* fmt = reinterpret_cast<const unsigned char*>(fmt0);
   int c;            /* character from format, or conversion */
   size_t width;     /* field width, or 0 */
-  char* p;          /* points into all kinds of strings */
-  int n;            /* handy integer */
+  char* p;
+  wchar_t* wcp;
+  size_t n;
   int flags;        /* flags as defined above */
-  char* p0;         /* saves original value of p when necessary */
   int nassigned;    /* number of fields assigned */
   int nread;        /* number of characters consumed from fp */
   int base;         /* base argument to strtoimax/strtouimax */
   char ccltab[256]; /* character class table for %[...] */
   char buf[BUF];    /* buffer for numeric conversions */
-  wchar_t* wcp;     /* handy wide character pointer */
   size_t nconv;     /* length of multibyte sequence converted */
   mbstate_t mbs;
+  void* allocation = NULL; // Allocated but unassigned result for %mc/%ms/%m[.
+  size_t capacity = 0; // Number of char/wchar_t units allocated in `allocation`.
 
   /* `basefix' is used to avoid `if' tests in the integer scanner */
   static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
@@ -113,10 +107,9 @@
 
   nassigned = 0;
   nread = 0;
-  base = 0; /* XXX just to keep gcc happy */
   for (;;) {
     c = *fmt++;
-    if (c == 0) return (nassigned);
+    if (c == 0) return nassigned;
     if (IsSpace(c)) {
       while ((fp->_r > 0 || __srefill(fp) == 0) && IsSpace(*fp->_p)) nread++, fp->_r--, fp->_p++;
       continue;
@@ -164,6 +157,9 @@
           flags |= LONG;
         }
         goto again;
+      case 'm':
+        flags |= ALLOCATE;
+        goto again;
       case 'q':
         flags |= LLONG; /* deprecated */
         goto again;
@@ -239,6 +235,8 @@
         break;
 
       case 's':
+        memset(ccltab, 1, 256);
+        ccltab['\t'] = ccltab['\n'] = ccltab['\v'] = ccltab['\f'] = ccltab['\r'] = ccltab[' '] = 0;
         c = CT_STRING;
         break;
 
@@ -262,29 +260,30 @@
 
       case 'n':
         if (flags & SUPPRESS) continue;
-        if (flags & SHORTSHORT)
+        if (flags & SHORTSHORT) {
           *va_arg(ap, signed char*) = nread;
-        else if (flags & SHORT)
+        } else if (flags & SHORT) {
           *va_arg(ap, short*) = nread;
-        else if (flags & LONG)
+        } else if (flags & LONG) {
           *va_arg(ap, long*) = nread;
-        else if (flags & SIZEINT)
+        } else if (flags & SIZEINT) {
           *va_arg(ap, ssize_t*) = nread;
-        else if (flags & PTRINT)
+        } else if (flags & PTRINT) {
           *va_arg(ap, ptrdiff_t*) = nread;
-        else if (flags & LLONG)
+        } else if (flags & LLONG) {
           *va_arg(ap, long long*) = nread;
-        else if (flags & MAXINT)
+        } else if (flags & MAXINT) {
           *va_arg(ap, intmax_t*) = nread;
-        else
+        } else {
           *va_arg(ap, int*) = nread;
+        }
         continue;
 
       /*
        * Disgusting backwards compatibility hacks.	XXX
        */
       case '\0': /* compat */
-        return (EOF);
+        return EOF;
 
       default: /* compat */
         if (IsUpper(c)) flags |= LONG;
@@ -293,6 +292,13 @@
         break;
     }
 
+    if ((flags & ALLOCATE) != 0 && c > CT_STRING) {
+      __fortify_fatal("scanf 'm' only works with %%c/%%s/%%[");
+    }
+    if ((flags & (ALLOCATE|SUPPRESS)) == (ALLOCATE|SUPPRESS)) {
+      __fortify_fatal("scanf 'm' makes no sense with '*'");
+    }
+
     /*
      * We have a conversion that requires input.
      */
@@ -326,42 +332,53 @@
         /* scan arbitrary characters (sets NOSKIP) */
         if (width == 0) width = 1;
         if (flags & LONG) {
-          wcp = ((flags & SUPPRESS) == 0) ? va_arg(ap, wchar_t*) : NULL;
-          n = 0;
+          if (flags & ALLOCATE) {
+            allocation = wcp = reinterpret_cast<wchar_t*>(malloc(width * sizeof(wchar_t)));
+            if (allocation == NULL) goto allocation_failure;
+          } else if (flags & SUPPRESS) {
+            wcp = NULL;
+          } else {
+            wcp = va_arg(ap, wchar_t*);
+          }
+          size_t bytes = 0;
           while (width != 0) {
-            if (n == (int)MB_CUR_MAX) {
+            if (bytes == MB_CUR_MAX) {
               fp->_flags |= __SERR;
               goto input_failure;
             }
-            buf[n++] = *fp->_p;
+            buf[bytes++] = *fp->_p;
             fp->_p++;
             fp->_r--;
             memset(&mbs, 0, sizeof(mbs));
-            nconv = mbrtowc(wcp, buf, n, &mbs);
-            if (nconv == (size_t)-1) {
+            nconv = mbrtowc(wcp, buf, bytes, &mbs);
+            if (nconv == __MB_ERR_ILLEGAL_SEQUENCE) {
               fp->_flags |= __SERR;
               goto input_failure;
             }
             if (nconv == 0 && !(flags & SUPPRESS)) *wcp = L'\0';
-            if (nconv != (size_t)-2) {
-              nread += n;
+            if (nconv != __MB_ERR_INCOMPLETE_SEQUENCE) {
+              nread += bytes;
               width--;
               if (!(flags & SUPPRESS)) wcp++;
-              n = 0;
+              bytes = 0;
             }
             if (fp->_r <= 0 && __srefill(fp)) {
-              if (n != 0) {
+              if (bytes != 0) {
                 fp->_flags |= __SERR;
                 goto input_failure;
               }
               break;
             }
           }
+          if (allocation != NULL) {
+            *va_arg(ap, wchar_t**) = reinterpret_cast<wchar_t*>(allocation);
+            allocation = NULL;
+          }
           if (!(flags & SUPPRESS)) nassigned++;
         } else if (flags & SUPPRESS) {
           size_t sum = 0;
           for (;;) {
-            if ((n = fp->_r) < (int)width) {
+            if ((n = fp->_r) < width) {
               sum += n;
               width -= n;
               fp->_p += n;
@@ -378,9 +395,18 @@
           }
           nread += sum;
         } else {
-          size_t r = fread((void*)va_arg(ap, char*), 1, width, fp);
-
+          if (flags & ALLOCATE) {
+            allocation = p = reinterpret_cast<char*>(malloc(width));
+            if (allocation == NULL) goto allocation_failure;
+          } else {
+            p = va_arg(ap, char*);
+          }
+          size_t r = fread(p, 1, width, fp);
           if (r == 0) goto input_failure;
+          if (allocation != NULL) {
+            *va_arg(ap, char**) = reinterpret_cast<char*>(allocation);
+            allocation = NULL;
+          }
           nread += r;
           nassigned++;
         }
@@ -390,58 +416,75 @@
       case CT_STRING:
         // CT_CCL: scan a (nonempty) character class (sets NOSKIP).
         // CT_STRING: like CCL, but zero-length string OK, & no NOSKIP.
-        if (width == 0) width = (size_t)~0; // 'infinity'.
+        if (width == 0) width = SIZE_MAX;
         if (flags & LONG) {
-          wchar_t twc;
-          int nchars = 0;
-
-          wcp = (flags & SUPPRESS) == 0 ? va_arg(ap, wchar_t*) : &twc;
+          // TODO: since no-one cares, replace this with a simple fgetwc loop?
           n = 0;
+          if (flags & ALLOCATE) {
+            capacity = MIN(width, 32);
+            allocation = wcp = reinterpret_cast<wchar_t*>(malloc(sizeof(wchar_t) * capacity));
+            if (allocation == NULL) goto allocation_failure;
+          } else if (flags & SUPPRESS) {
+            wcp = NULL;
+          } else {
+            wcp = va_arg(ap, wchar_t*);
+          }
+          size_t bytes = 0;
           while ((c == CT_CCL || !IsSpace(*fp->_p)) && width != 0) {
-            if (n == (int)MB_CUR_MAX) {
+            if (bytes == MB_CUR_MAX) {
               fp->_flags |= __SERR;
               goto input_failure;
             }
-            buf[n++] = *fp->_p;
+            buf[bytes++] = *fp->_p;
             fp->_p++;
             fp->_r--;
+            wchar_t wc = L'\0';
             memset(&mbs, 0, sizeof(mbs));
-            nconv = mbrtowc(wcp, buf, n, &mbs);
-            if (nconv == (size_t)-1) {
+            nconv = mbrtowc(&wc, buf, bytes, &mbs);
+            if (nconv == __MB_ERR_ILLEGAL_SEQUENCE) {
               fp->_flags |= __SERR;
               goto input_failure;
             }
-            if (nconv == 0) *wcp = L'\0';
-            if (nconv != (size_t)-2) {
-              if ((c == CT_CCL && wctob(*wcp) != EOF && !ccltab[wctob(*wcp)]) || (c == CT_STRING && iswspace(*wcp))) {
-                while (n != 0) {
-                  n--;
-                  ungetc(buf[n], fp);
+            if (nconv != __MB_ERR_INCOMPLETE_SEQUENCE) {
+              if ((c == CT_CCL && wctob(wc) != EOF && !ccltab[wctob(wc)]) || (c == CT_STRING && iswspace(wc))) {
+                while (bytes != 0) {
+                  bytes--;
+                  ungetc(buf[bytes], fp);
                 }
                 break;
               }
-              nread += n;
+              if (wcp) wcp[n] = wc;
+              n++;
+              if (allocation != NULL && n == capacity) {
+                capacity *= 2;
+                wchar_t* new_allocation =
+                    reinterpret_cast<wchar_t*>(realloc(allocation, sizeof(wchar_t) * capacity));
+                if (new_allocation == NULL) goto allocation_failure;
+                allocation = wcp = new_allocation;
+              }
+              nread += bytes;
               width--;
-              if (!(flags & SUPPRESS)) wcp++;
-              nchars++;
-              n = 0;
+              bytes = 0;
             }
             if (fp->_r <= 0 && __srefill(fp)) {
-              if (n != 0) {
+              if (bytes != 0) {
                 fp->_flags |= __SERR;
                 goto input_failure;
               }
               break;
             }
           }
-          if (c == CT_CCL && n != 0) {
+          if (c == CT_CCL && bytes != 0) {
             fp->_flags |= __SERR;
             goto input_failure;
           }
-          n = nchars;
+          if (allocation != NULL) {
+            *va_arg(ap, wchar_t**) = reinterpret_cast<wchar_t*>(allocation);
+            allocation = NULL;
+          }
         } else if (flags & SUPPRESS) {
           n = 0;
-          while ((c == CT_CCL && ccltab[*fp->_p]) || (c == CT_STRING && !IsSpace(*fp->_p))) {
+          while (ccltab[*fp->_p]) {
             n++, fp->_r--, fp->_p++;
             if (--width == 0) break;
             if (fp->_r <= 0 && __srefill(fp)) {
@@ -449,29 +492,46 @@
               break;
             }
           }
+          nread += n;
         } else {
-          p0 = p = va_arg(ap, char*);
-          while ((c == CT_CCL && ccltab[*fp->_p]) || (c == CT_STRING && !IsSpace(*fp->_p))) {
+          if (flags & ALLOCATE) {
+            capacity = MIN(width, 32);
+            allocation = p = reinterpret_cast<char*>(malloc(capacity));
+            if (allocation == NULL) goto allocation_failure;
+          } else {
+            p = va_arg(ap, char*);
+          }
+          n = 0;
+          while (ccltab[*fp->_p]) {
             fp->_r--;
-            *p++ = *fp->_p++;
+            p[n++] = *fp->_p++;
+            if (allocation != NULL && n == capacity) {
+              capacity *= 2;
+              char* new_allocation = reinterpret_cast<char*>(realloc(allocation, capacity));
+              if (new_allocation == NULL) goto allocation_failure;
+              allocation = p = new_allocation;
+            }
             if (--width == 0) break;
             if (fp->_r <= 0 && __srefill(fp)) {
-              if (c == CT_CCL && p == p0) goto input_failure;
+              if (c == CT_CCL && n == 0) goto input_failure;
               break;
             }
           }
-          n = p - p0;
+          nread += n;
+          if (allocation != NULL) {
+            *va_arg(ap, char**) = reinterpret_cast<char*>(allocation);
+            allocation = NULL;
+          }
         }
         if (c == CT_CCL && n == 0) goto match_failure;
         if (!(flags & SUPPRESS)) {
           if (flags & LONG) {
-            *wcp = L'\0';
+            wcp[n] = L'\0';
           } else {
-            *p = '\0';
+            p[n] = '\0';
           }
           ++nassigned;
         }
-        nread += n;
         break;
 
       case CT_INT:
@@ -610,28 +670,30 @@
           uintmax_t res;
 
           *p = '\0';
-          if (flags & UNSIGNED)
+          if (flags & UNSIGNED) {
             res = strtoumax(buf, NULL, base);
-          else
+          } else {
             res = strtoimax(buf, NULL, base);
-          if (flags & POINTER)
+          }
+          if (flags & POINTER) {
             *va_arg(ap, void**) = (void*)(uintptr_t)res;
-          else if (flags & MAXINT)
+          } else if (flags & MAXINT) {
             *va_arg(ap, intmax_t*) = res;
-          else if (flags & LLONG)
+          } else if (flags & LLONG) {
             *va_arg(ap, long long*) = res;
-          else if (flags & SIZEINT)
+          } else if (flags & SIZEINT) {
             *va_arg(ap, ssize_t*) = res;
-          else if (flags & PTRINT)
+          } else if (flags & PTRINT) {
             *va_arg(ap, ptrdiff_t*) = res;
-          else if (flags & LONG)
+          } else if (flags & LONG) {
             *va_arg(ap, long*) = res;
-          else if (flags & SHORT)
+          } else if (flags & SHORT) {
             *va_arg(ap, short*) = res;
-          else if (flags & SHORTSHORT)
+          } else if (flags & SHORTSHORT) {
             *va_arg(ap, signed char*) = res;
-          else
+          } else {
             *va_arg(ap, int*) = res;
+          }
           nassigned++;
         }
         nread += p - buf;
@@ -659,10 +721,12 @@
         break;
     }
   }
+allocation_failure:
 input_failure:
+  free(allocation);
   if (nassigned == 0) nassigned = -1;
 match_failure:
-  return (nassigned);
+  return nassigned;
 }
 
 /*
@@ -671,7 +735,7 @@
  * closing `]'.  The table has a 1 wherever characters should be
  * considered part of the scanset.
  */
-static u_char* __sccl(char* tab, u_char* fmt) {
+static const unsigned char* __sccl(char* tab, const unsigned char* fmt) {
   int c, n, v;
 
   /* first `clear' the whole table */
@@ -679,10 +743,10 @@
   if (c == '^') {
     v = 1;      /* default => accept */
     c = *fmt++; /* get new first char */
-  } else
+  } else {
     v = 0; /* default => reject */
-  /* should probably use memset here */
-  for (n = 0; n < 256; n++) tab[n] = v;
+  }
+  memset(tab, v, 256);
   if (c == 0) return (fmt - 1); /* format ended before closing ] */
 
   /*
@@ -744,7 +808,7 @@
         break;
 
       case ']': /* end of scanset */
-        return (fmt);
+        return fmt;
 
       default: /* just another character */
         c = n;
diff --git a/libc/system_properties/include/system_properties/system_properties.h b/libc/system_properties/include/system_properties/system_properties.h
index c74f875..83ac00c 100644
--- a/libc/system_properties/include/system_properties/system_properties.h
+++ b/libc/system_properties/include/system_properties/system_properties.h
@@ -48,7 +48,7 @@
   SystemProperties() {
   }
   // Special constructor for testing that also zero initializes the important members.
-  SystemProperties(bool initialized) : initialized_(initialized) {
+  explicit SystemProperties(bool initialized) : initialized_(initialized) {
   }
 
   DISALLOW_COPY_AND_ASSIGN(SystemProperties);
diff --git a/libdl/Android.bp b/libdl/Android.bp
index 3889bdb..0603d06 100644
--- a/libdl/Android.bp
+++ b/libdl/Android.bp
@@ -25,6 +25,7 @@
 
 cc_library {
     name: "libdl",
+    static_ndk_lib: true,
 
     defaults: ["linux_bionic_supported"],
 
diff --git a/libm/Android.bp b/libm/Android.bp
index 6d6fafa..76a4699 100644
--- a/libm/Android.bp
+++ b/libm/Android.bp
@@ -6,6 +6,7 @@
 cc_library {
     name: "libm",
     defaults: ["linux_bionic_supported"],
+    static_ndk_lib: true,
 
     srcs: [
         "upstream-freebsd/lib/msun/bsdsrc/b_exp.c",
diff --git a/linker/linker.cpp b/linker/linker.cpp
index abcff2a..05efc55 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -3686,7 +3686,7 @@
   std::string error_msg;
 
   std::string ld_config_vndk = kLdConfigFilePath;
-  size_t insert_pos = ld_config_vndk.find_last_of(".");
+  size_t insert_pos = ld_config_vndk.find_last_of('.');
   if (insert_pos == std::string::npos) {
     insert_pos = ld_config_vndk.length();
   }
diff --git a/tests/TemporaryFile.h b/tests/TemporaryFile.h
index 5c2fe4f..070b71a 100644
--- a/tests/TemporaryFile.h
+++ b/tests/TemporaryFile.h
@@ -22,7 +22,7 @@
 template <typename T = int (*)(char*)>
 class GenericTemporaryFile {
  public:
-  GenericTemporaryFile(T mk_fn = mkstemp) : mk_fn(mk_fn) {
+  explicit GenericTemporaryFile(T mk_fn = mkstemp) : mk_fn(mk_fn) {
     // Since we might be running on the host or the target, and if we're
     // running on the host we might be running under bionic or glibc,
     // let's just try both possible temporary directories and take the
@@ -33,7 +33,7 @@
     }
   }
 
-  GenericTemporaryFile(const char* dirpath, T mk_fn = mkstemp) : mk_fn(mk_fn) {
+  explicit GenericTemporaryFile(const char* dirpath, T mk_fn = mkstemp) : mk_fn(mk_fn) {
     init(dirpath);
   }
 
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 04b83f2..64cfa08 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -425,7 +425,7 @@
   }
 
   void CreateRelroFile(const char* lib, const char* relro_file) {
-    int relro_fd = open(relro_file, O_RDWR | O_TRUNC);
+    int relro_fd = open(relro_file, O_RDWR | O_TRUNC | O_CLOEXEC);
     ASSERT_NOERROR(relro_fd);
 
     pid_t pid = fork();
@@ -447,7 +447,7 @@
     AssertChildExited(pid, 0);
 
     // reopen file for reading so it can be used
-    relro_fd = open(relro_file, O_RDONLY);
+    relro_fd = open(relro_file, O_RDONLY | O_CLOEXEC);
     ASSERT_NOERROR(relro_fd);
     extinfo_.flags |= ANDROID_DLEXT_USE_RELRO;
     extinfo_.relro_fd = relro_fd;
diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp
index 33dbfd9..6b20094 100644
--- a/tests/gtest_main.cpp
+++ b/tests/gtest_main.cpp
@@ -90,7 +90,6 @@
 }  // namespace testing
 
 using testing::internal::GTestColor;
-using testing::internal::COLOR_DEFAULT;
 using testing::internal::COLOR_RED;
 using testing::internal::COLOR_GREEN;
 using testing::internal::COLOR_YELLOW;
@@ -274,8 +273,7 @@
 }
 
 static bool EnumerateTests(int argc, char** argv, std::vector<TestCase>& testcase_list) {
-  std::vector<const char*> args;
-  for (int i = 0; i < argc; ++i) args.push_back(argv[i]);
+  std::vector<const char*> args(argv, argv + argc);
   args.push_back("--gtest_list_tests");
   args.push_back(nullptr);
 
@@ -543,7 +541,7 @@
                                 const std::vector<TestCase>& testcase_list,
                                 time_t epoch_iteration_start_time,
                                 int64_t elapsed_time_ns) {
-  FILE* fp = fopen(xml_output_filename.c_str(), "w");
+  FILE* fp = fopen(xml_output_filename.c_str(), "we");
   if (fp == NULL) {
     fprintf(stderr, "failed to open '%s': %s\n", xml_output_filename.c_str(), strerror(errno));
     exit(1);
@@ -1185,10 +1183,7 @@
   g_argc = argc;
   g_argv = argv;
   g_envp = envp;
-  std::vector<char*> arg_list;
-  for (int i = 0; i < argc; ++i) {
-    arg_list.push_back(argv[i]);
-  }
+  std::vector<char*> arg_list(argv, argv + argc);
 
   IsolationTestOptions options;
   if (PickOptions(arg_list, options) == false) {
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index d0d9130..c1a51a8 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -1016,6 +1016,138 @@
   CheckScanf(swscanf, L"+,-/.", L"%[+--/]", 1, "+,-/");
 }
 
+template <typename T1, typename T2>
+static void CheckScanfM(int sscanf_fn(const T1*, const T1*, ...),
+                        const T1* input, const T1* fmt,
+                        int expected_count, const T2* expected_string) {
+  T2* result = nullptr;
+  ASSERT_EQ(expected_count, sscanf_fn(input, fmt, &result)) << fmt;
+  if (expected_string == nullptr) {
+    ASSERT_EQ(nullptr, result);
+  } else {
+    ASSERT_STREQ(expected_string, result) << fmt;
+  }
+  free(result);
+}
+
+TEST(STDIO_TEST, sscanf_mc) {
+  char* p1 = nullptr;
+  char* p2 = nullptr;
+  ASSERT_EQ(2, sscanf("hello", "%mc%mc", &p1, &p2));
+  ASSERT_EQ('h', *p1);
+  ASSERT_EQ('e', *p2);
+  free(p1);
+  free(p2);
+
+  p1 = nullptr;
+  ASSERT_EQ(1, sscanf("hello", "%4mc", &p1));
+  ASSERT_EQ('h', p1[0]);
+  ASSERT_EQ('e', p1[1]);
+  ASSERT_EQ('l', p1[2]);
+  ASSERT_EQ('l', p1[3]);
+  free(p1);
+
+  p1 = nullptr;
+  ASSERT_EQ(1, sscanf("hello world", "%30mc", &p1));
+  ASSERT_EQ('h', p1[0]);
+  ASSERT_EQ('e', p1[1]);
+  ASSERT_EQ('l', p1[2]);
+  ASSERT_EQ('l', p1[3]);
+  ASSERT_EQ('o', p1[4]);
+  free(p1);
+}
+
+
+TEST(STDIO_TEST, sscanf_mlc) {
+  // This is so useless that clang doesn't even believe it exists...
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wformat-extra-args"
+
+  wchar_t* p1 = nullptr;
+  wchar_t* p2 = nullptr;
+  ASSERT_EQ(2, sscanf("hello", "%mlc%mlc", &p1, &p2));
+  ASSERT_EQ(L'h', *p1);
+  ASSERT_EQ(L'e', *p2);
+  free(p1);
+  free(p2);
+
+  p1 = nullptr;
+  ASSERT_EQ(1, sscanf("hello", "%4mlc", &p1));
+  ASSERT_EQ(L'h', p1[0]);
+  ASSERT_EQ(L'e', p1[1]);
+  ASSERT_EQ(L'l', p1[2]);
+  ASSERT_EQ(L'l', p1[3]);
+  free(p1);
+
+  p1 = nullptr;
+  ASSERT_EQ(1, sscanf("hello world", "%30mlc", &p1));
+  ASSERT_EQ(L'h', p1[0]);
+  ASSERT_EQ(L'e', p1[1]);
+  ASSERT_EQ(L'l', p1[2]);
+  ASSERT_EQ(L'l', p1[3]);
+  ASSERT_EQ(L'o', p1[4]);
+  free(p1);
+#pragma clang diagnostic pop
+}
+
+
+TEST(STDIO_TEST, sscanf_ms) {
+  CheckScanfM(sscanf, "hello", "%ms", 1, "hello");
+  CheckScanfM(sscanf, "hello", "%4ms", 1, "hell");
+  CheckScanfM(sscanf, "hello world", "%30ms", 1, "hello");
+}
+
+TEST(STDIO_TEST, sscanf_mls) {
+  CheckScanfM(sscanf, "hello", "%mls", 1, L"hello");
+  CheckScanfM(sscanf, "hello", "%4mls", 1, L"hell");
+  CheckScanfM(sscanf, "hello world", "%30mls", 1, L"hello");
+}
+
+TEST(STDIO_TEST, sscanf_m_ccl) {
+  CheckScanfM(sscanf, "hello", "%m[a-z]", 1, "hello");
+  CheckScanfM(sscanf, "hello", "%4m[a-z]", 1, "hell");
+  CheckScanfM(sscanf, "hello world", "%30m[a-z]", 1, "hello");
+}
+
+TEST(STDIO_TEST, sscanf_ml_ccl) {
+  CheckScanfM(sscanf, "hello", "%ml[a-z]", 1, L"hello");
+  CheckScanfM(sscanf, "hello", "%4ml[a-z]", 1, L"hell");
+  CheckScanfM(sscanf, "hello world", "%30ml[a-z]", 1, L"hello");
+}
+
+TEST(STDIO_TEST, sscanf_ls) {
+  wchar_t w[32] = {};
+  ASSERT_EQ(1, sscanf("hello world", "%ls", w));
+  ASSERT_EQ(L"hello", std::wstring(w));
+}
+
+TEST(STDIO_TEST, sscanf_ls_suppress) {
+  ASSERT_EQ(0, sscanf("hello world", "%*ls %*ls"));
+}
+
+TEST(STDIO_TEST, sscanf_ls_n) {
+  setlocale(LC_ALL, "C.UTF-8");
+  wchar_t w[32] = {};
+  int pos = 0;
+  ASSERT_EQ(1, sscanf("\xc4\x80", "%ls%n", w, &pos));
+  ASSERT_EQ(static_cast<wchar_t>(256), w[0]);
+  ASSERT_EQ(2, pos);
+}
+
+TEST(STDIO_TEST, sscanf_ls_realloc) {
+  // This is so useless that clang doesn't even believe it exists...
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-invalid-specifier"
+#pragma clang diagnostic ignored "-Wformat-extra-args"
+  wchar_t* p1 = nullptr;
+  wchar_t* p2 = nullptr;
+  ASSERT_EQ(2, sscanf("1234567890123456789012345678901234567890 world", "%mls %mls", &p1, &p2));
+  ASSERT_EQ(L"1234567890123456789012345678901234567890", std::wstring(p1));
+  ASSERT_EQ(L"world", std::wstring(p2));
+#pragma clang diagnostic pop
+}
+
 // https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=202240
 TEST(STDIO_TEST, scanf_wscanf_EOF) {
   EXPECT_EQ(0, sscanf("b", "ab"));
diff --git a/tests/utils.h b/tests/utils.h
index a5783af..3f4218e 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -180,10 +180,10 @@
     return const_cast<char**>(env_.data());
   }
 
-  void SetArgs(const std::vector<const char*> args) {
+  void SetArgs(const std::vector<const char*>& args) {
     args_ = args;
   }
-  void SetEnv(const std::vector<const char*> env) {
+  void SetEnv(const std::vector<const char*>& env) {
     env_ = env;
   }
 
diff --git a/tools/versioner/src/DeclarationDatabase.cpp b/tools/versioner/src/DeclarationDatabase.cpp
index 19c2f0d..d9cff17 100644
--- a/tools/versioner/src/DeclarationDatabase.cpp
+++ b/tools/versioner/src/DeclarationDatabase.cpp
@@ -102,21 +102,18 @@
       return true;
     }
 
-    DeclarationType declaration_type;
     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;
       is_definition = function_decl->isThisDeclarationADefinition();
     } else if (auto var_decl = dyn_cast<VarDecl>(decl)) {
       if (!var_decl->isFileVarDecl()) {
         return true;
       }
 
-      declaration_type = DeclarationType::variable;
       switch (var_decl->isThisDeclarationADefinition()) {
         case VarDecl::DeclarationOnly:
           is_definition = false;
diff --git a/tools/versioner/src/versioner.cpp b/tools/versioner/src/versioner.cpp
index efb39bd..af0c067 100644
--- a/tools/versioner/src/versioner.cpp
+++ b/tools/versioner/src/versioner.cpp
@@ -496,7 +496,6 @@
 
 int main(int argc, char** argv) {
   std::string cwd = getWorkingDir() + "/";
-  bool default_args = true;
   std::string platform_dir;
   std::set<Arch> selected_architectures;
   std::set<int> selected_levels;
@@ -507,7 +506,6 @@
 
   int c;
   while ((c = getopt(argc, argv, "a:r:p:so:fdj:vhFi")) != -1) {
-    default_args = false;
     switch (c) {
       case 'a': {
         char* end;