Merge "Differentiate the wctype functions we need to improve."
diff --git a/libc/Android.bp b/libc/Android.bp
index 62ca943..00932de 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -1209,6 +1209,7 @@
         "bionic/arpa_inet.cpp",
         "bionic/assert.cpp",
         "bionic/atof.cpp",
+        "bionic/bionic_arc4random.cpp",
         "bionic/bionic_netlink.cpp",
         "bionic/bionic_systrace.cpp",
         "bionic/bionic_time_conversions.cpp",
diff --git a/libc/NOTICE b/libc/NOTICE
index a2f2b0a..cdf29e3 100644
--- a/libc/NOTICE
+++ b/libc/NOTICE
@@ -5503,7 +5503,7 @@
 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 Emulation of getentropy(2) as documented at:
-http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man2/getentropy.2
+http://man.openbsd.org/getentropy.2
 
 -------------------------------------------------------------------
 
diff --git a/libc/bionic/__libc_init_main_thread.cpp b/libc/bionic/__libc_init_main_thread.cpp
index b5a83f4..f3dbfa5 100644
--- a/libc/bionic/__libc_init_main_thread.cpp
+++ b/libc/bionic/__libc_init_main_thread.cpp
@@ -29,6 +29,7 @@
 #include "libc_init_common.h"
 
 #include "private/KernelArgumentBlock.h"
+#include "private/bionic_arc4random.h"
 #include "private/bionic_auxv.h"
 #include "private/bionic_globals.h"
 #include "private/bionic_ssp.h"
@@ -41,9 +42,7 @@
 uintptr_t __stack_chk_guard = 0;
 
 void __libc_init_global_stack_chk_guard(KernelArgumentBlock& args) {
-  // AT_RANDOM is a pointer to 16 bytes of randomness on the stack.
-  // Take the first 4/8 for the -fstack-protector implementation.
-  __stack_chk_guard = *reinterpret_cast<uintptr_t*>(args.getauxval(AT_RANDOM));
+  __libc_safe_arc4random_buf(&__stack_chk_guard, sizeof(__stack_chk_guard), args);
 }
 
 // Setup for the main thread. For dynamic executables, this is called by the
diff --git a/libc/bionic/bionic_arc4random.cpp b/libc/bionic/bionic_arc4random.cpp
new file mode 100644
index 0000000..d20cb68
--- /dev/null
+++ b/libc/bionic/bionic_arc4random.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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 "private/bionic_arc4random.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/auxv.h>
+#include <syscall.h>
+#include <unistd.h>
+
+#include "private/KernelArgumentBlock.h"
+#include "private/libc_logging.h"
+
+void __libc_safe_arc4random_buf(void* buf, size_t n, KernelArgumentBlock& args) {
+  static bool have_getrandom = syscall(SYS_getrandom, nullptr, 0, 0) == -1 && errno != ENOSYS;
+  static bool have_urandom = access("/dev/urandom", R_OK) == 0;
+  static size_t at_random_bytes_consumed = 0;
+
+  if (have_getrandom || have_urandom) {
+    arc4random_buf(buf, n);
+    return;
+  }
+
+  if (at_random_bytes_consumed + n > 16) {
+    __libc_fatal("ran out of AT_RANDOM bytes, have %zu, requested %zu",
+                 16 - at_random_bytes_consumed, n);
+  }
+
+  memcpy(buf, reinterpret_cast<char*>(args.getauxval(AT_RANDOM)) + at_random_bytes_consumed, n);
+  at_random_bytes_consumed += n;
+  return;
+}
diff --git a/libc/bionic/getentropy_linux.c b/libc/bionic/getentropy_linux.c
index 409bd7d..98fb6fb 100644
--- a/libc/bionic/getentropy_linux.c
+++ b/libc/bionic/getentropy_linux.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: getentropy_linux.c,v 1.28 2014/07/20 03:24:10 deraadt Exp $	*/
+/*	$OpenBSD: getentropy_linux.c,v 1.42 2016/04/19 20:20:24 tj Exp $	*/
 
 /*
  * Copyright (c) 2014 Theo de Raadt <deraadt@openbsd.org>
@@ -17,7 +17,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *
  * Emulation of getentropy(2) as documented at:
- * http://www.openbsd.org/cgi-bin/man.cgi/OpenBSD-current/man2/getentropy.2
+ * http://man.openbsd.org/getentropy.2
  */
 
 #define	_POSIX_C_SOURCE	199309L
@@ -27,8 +27,8 @@
 #include <sys/ioctl.h>
 #include <sys/resource.h>
 #include <sys/syscall.h>
-#ifdef HAVE_SYS_SYSCTL_H
-#include <sys/sysctl.h>
+#ifdef SYS__sysctl
+#include <linux/sysctl.h>
 #endif
 #include <sys/statvfs.h>
 #include <sys/socket.h>
@@ -51,8 +51,8 @@
 #include <openssl/sha.h>
 #endif
 
+#include <linux/types.h>
 #include <linux/random.h>
-#include <linux/sysctl.h>
 #ifdef HAVE_GETAUXVAL
 #include <sys/auxv.h>
 #endif
@@ -76,9 +76,7 @@
 int	getentropy(void *buf, size_t len);
 
 static int gotdata(char *buf, size_t len);
-#ifdef SYS__getrandom
 static int getentropy_getrandom(void *buf, size_t len);
-#endif
 static int getentropy_urandom(void *buf, size_t len);
 #ifdef SYS__sysctl
 static int getentropy_sysctl(void *buf, size_t len);
@@ -95,17 +93,17 @@
 
 	if (len > 256) {
 		errno = EIO;
-		return -1;
+		return (-1);
 	}
 
-#ifdef SYS__getrandom
 	/*
 	 * Try descriptor-less getrandom()
 	 */
 	ret = getentropy_getrandom(buf, len);
 	if (ret != -1)
 		return (ret);
-#endif
+	if (errno != ENOSYS)
+		return (-1);
 
 	/*
 	 * Try to get entropy with /dev/urandom
@@ -122,7 +120,7 @@
 	 * Try to use sysctl CTL_KERN, KERN_RANDOM, RANDOM_UUID.
 	 * sysctl is a failsafe API, so it guarantees a result.  This
 	 * should work inside a chroot, or when file descriptors are
-	 * exhuasted.
+	 * exhausted.
 	 *
 	 * However this can fail if the Linux kernel removes support
 	 * for sysctl.  Starting in 2007, there have been efforts to
@@ -190,39 +188,26 @@
 	for (i = 0; i < len; ++i)
 		any_set |= buf[i];
 	if (any_set == 0)
-		return -1;
-	return 0;
+		return (-1);
+	return (0);
 }
 
-#ifdef SYS__getrandom
 static int
 getentropy_getrandom(void *buf, size_t len)
 {
-#if 0
-
-/* Hand-definitions until the API becomes commonplace */
-#ifndef SYS__getrandom
-#ifdef __LP64__
-#define SYS__getrandom 317
-#else
-#define SYS__getrandom 354
-#endif
-#endif
-	struct __getrandom_args args = {
-		.buf = buf;
-		.len = len;
-		.flags = 0;
-	};
-
+	int pre_errno = errno;
+	int ret;
 	if (len > 256)
 		return (-1);
-	ret = syscall(SYS__getrandom, &args);
-	if (ret == len)
-		return (0);
-#endif
-	return -1;
+	do {
+		ret = syscall(SYS_getrandom, buf, len, 0);
+	} while (ret == -1 && errno == EINTR);
+
+	if ((size_t)ret != len)
+		return (-1);
+	errno = pre_errno;
+	return (0);
 }
-#endif
 
 static int
 getentropy_urandom(void *buf, size_t len)
@@ -275,11 +260,11 @@
 	close(fd);
 	if (gotdata(buf, len) == 0) {
 		errno = save_errno;
-		return 0;		/* satisfied */
+		return (0);		/* satisfied */
 	}
 nodevrandom:
 	errno = EIO;
-	return -1;
+	return (-1);
 }
 
 #ifdef SYS__sysctl
@@ -297,7 +282,7 @@
 		struct __sysctl_args args = {
 			.name = mib,
 			.nlen = 3,
-			.oldval = (char*) buf + i,
+			.oldval = (char *)buf + i,
 			.oldlenp = &chunk,
 		};
 		if (syscall(SYS__sysctl, &args) != 0)
@@ -310,13 +295,12 @@
 	}
 sysctlfailed:
 	errno = EIO;
-	return -1;
+	return (-1);
 }
 #endif /* SYS__sysctl */
 
 #ifdef HAVE_OPENSSL
-
-static int cl[] = {
+static const int cl[] = {
 	CLOCK_REALTIME,
 #ifdef CLOCK_MONOTONIC
 	CLOCK_MONOTONIC,
@@ -347,7 +331,7 @@
 	SHA512_CTX *ctx = data;
 
 	SHA512_Update(ctx, &info->dlpi_addr, sizeof (info->dlpi_addr));
-	return 0;
+	return (0);
 }
 
 static int
@@ -556,10 +540,9 @@
 	memset(results, 0, sizeof results);
 	if (gotdata(buf, len) == 0) {
 		errno = save_errno;
-		return 0;		/* satisfied */
+		return (0);		/* satisfied */
 	}
 	errno = EIO;
-	return -1;
+	return (-1);
 }
-
 #endif /* HAVE_OPENSSL */
diff --git a/libc/bionic/setjmp_cookie.cpp b/libc/bionic/setjmp_cookie.cpp
index 3be675a..4fa68c2 100644
--- a/libc/bionic/setjmp_cookie.cpp
+++ b/libc/bionic/setjmp_cookie.cpp
@@ -34,14 +34,14 @@
 #include <sys/auxv.h>
 #include <sys/cdefs.h>
 
+#include "private/bionic_arc4random.h"
 #include "private/bionic_globals.h"
 #include "private/libc_logging.h"
 #include "private/KernelArgumentBlock.h"
 
-void __libc_init_setjmp_cookie(libc_globals* globals,
-                               KernelArgumentBlock& args) {
-  char* random_data = reinterpret_cast<char*>(args.getauxval(AT_RANDOM));
-  long value = *reinterpret_cast<long*>(random_data + 8);
+void __libc_init_setjmp_cookie(libc_globals* globals, KernelArgumentBlock& args) {
+  long value;
+  __libc_safe_arc4random_buf(&value, sizeof(value), args);
 
   // Mask off the last bit to store the signal flag.
   globals->setjmp_cookie = value & ~1;
diff --git a/libc/include/android/legacy_termios_inlines.h b/libc/include/android/legacy_termios_inlines.h
index 41ea955..92cb22d 100644
--- a/libc/include/android/legacy_termios_inlines.h
+++ b/libc/include/android/legacy_termios_inlines.h
@@ -29,11 +29,12 @@
 #ifndef _ANDROID_LEGACY_TERMIOS_INLINES_H_
 #define _ANDROID_LEGACY_TERMIOS_INLINES_H_
 
-#include <linux/termios.h>
 #include <sys/cdefs.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 
+#include <linux/termios.h>
+
 #if __ANDROID_API__ < 21
 
 __BEGIN_DECLS
diff --git a/libc/include/elf.h b/libc/include/elf.h
index 1a99337..ae05d3d 100644
--- a/libc/include/elf.h
+++ b/libc/include/elf.h
@@ -29,11 +29,12 @@
 #ifndef _ELF_H
 #define _ELF_H
 
+#include <sys/cdefs.h>
+
 #include <linux/auxvec.h>
 #include <linux/elf.h>
 #include <linux/elf-em.h>
 #include <machine/elf_machdep.h>
-#include <sys/cdefs.h>
 
 #define ELF32_R_INFO(sym, type) ((((Elf32_Word)sym) << 8) | ((type) & 0xff))
 #define ELF64_R_INFO(sym, type) ((((Elf64_Xword)sym) << 32) | ((type) & 0xffffffff))
diff --git a/libc/include/link.h b/libc/include/link.h
index 92ecceb..0d07cb9 100644
--- a/libc/include/link.h
+++ b/libc/include/link.h
@@ -28,10 +28,11 @@
 #ifndef _LINK_H_
 #define _LINK_H_
 
-#include <elf.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
+#include <elf.h>
+
 __BEGIN_DECLS
 
 #if defined(__LP64__)
diff --git a/libc/include/signal.h b/libc/include/signal.h
index a9df04b..17bc57d 100644
--- a/libc/include/signal.h
+++ b/libc/include/signal.h
@@ -29,12 +29,13 @@
 #ifndef _SIGNAL_H_
 #define _SIGNAL_H_
 
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
 #include <asm/sigcontext.h>
 #include <bits/pthread_types.h>
 #include <bits/timespec.h>
 #include <limits.h>
-#include <sys/cdefs.h>
-#include <sys/types.h>
 
 #if defined(__LP64__) || defined(__mips__)
 /* For 64-bit (and mips), the kernel's struct sigaction doesn't match the POSIX one,
diff --git a/libc/include/sys/select.h b/libc/include/sys/select.h
index 5a8a81d..df118e3 100644
--- a/libc/include/sys/select.h
+++ b/libc/include/sys/select.h
@@ -29,11 +29,12 @@
 #ifndef _SYS_SELECT_H_
 #define _SYS_SELECT_H_
 
-#include <linux/time.h>
-#include <signal.h>
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
+#include <linux/time.h>
+#include <signal.h>
+
 __BEGIN_DECLS
 
 #define FD_SETSIZE 1024
diff --git a/libc/include/sys/signalfd.h b/libc/include/sys/signalfd.h
index a21c57b..79f9490 100644
--- a/libc/include/sys/signalfd.h
+++ b/libc/include/sys/signalfd.h
@@ -29,9 +29,10 @@
 #ifndef _SYS_SIGNALFD_H_
 #define _SYS_SIGNALFD_H_
 
+#include <sys/cdefs.h>
+
 #include <linux/signalfd.h>
 #include <signal.h>
-#include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
diff --git a/libc/include/sys/ucontext.h b/libc/include/sys/ucontext.h
index fdb3015..4eddf23 100644
--- a/libc/include/sys/ucontext.h
+++ b/libc/include/sys/ucontext.h
@@ -29,8 +29,9 @@
 #ifndef _SYS_UCONTEXT_H_
 #define _SYS_UCONTEXT_H_
 
-#include <signal.h>
 #include <sys/cdefs.h>
+
+#include <signal.h>
 #include <sys/user.h>
 
 __BEGIN_DECLS
diff --git a/libc/include/syslog.h b/libc/include/syslog.h
index ee288e5..4b7eecb 100644
--- a/libc/include/syslog.h
+++ b/libc/include/syslog.h
@@ -89,7 +89,11 @@
 void openlog(const char* _Nullable, int, int);
 int setlogmask(int);
 void syslog(int, const char* _Nonnull, ...) __printflike(2, 3);
+#if defined(__arm__) || defined(__aarch64__) || defined(__x86_64__)
 void vsyslog(int, const char* _Nonnull, va_list) __printflike(2, 0);
+#else /* defined(__mips__) || defined(__i386__) */
+void vsyslog(int, const char* _Nonnull, va_list _Nonnull) __printflike(2, 0);
+#endif
 
 __END_DECLS
 
diff --git a/libc/private/bionic_arc4random.h b/libc/private/bionic_arc4random.h
new file mode 100644
index 0000000..d26a4e7
--- /dev/null
+++ b/libc/private/bionic_arc4random.h
@@ -0,0 +1,45 @@
+/*
+ * 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 _PRIVATE_BIONIC_ARC4RANDOM_H_
+#define _PRIVATE_BIONIC_ARC4RANDOM_H_
+
+#include <stddef.h>
+
+#include "private/KernelArgumentBlock.h"
+
+/*
+ * arc4random aborts if it's unable to fetch entropy, which is always the case
+ * for init on devices without getrandom(2), since /dev/random hasn't been
+ * created yet. Provide a wrapper function that falls back to AT_RANDOM if
+ * we don't have getrandom and /dev/urandom is missing.
+ */
+
+void __libc_safe_arc4random_buf(void* buf, size_t n, KernelArgumentBlock& args);
+
+#endif
diff --git a/tools/versioner/src/Android.mk b/tools/versioner/src/Android.mk
index c43a525..c90e02f 100644
--- a/tools/versioner/src/Android.mk
+++ b/tools/versioner/src/Android.mk
@@ -26,12 +26,15 @@
 LOCAL_SRC_FILES := \
   versioner.cpp \
   Arch.cpp \
+  CompilationType.cpp \
   DeclarationDatabase.cpp \
+  Driver.cpp \
   Preprocessor.cpp \
   SymbolDatabase.cpp \
-  Utils.cpp
+  Utils.cpp \
+  VFS.cpp
 
-LOCAL_SHARED_LIBRARIES := libclang libLLVM
+LOCAL_SHARED_LIBRARIES := libclang libLLVM libbase
 
 include $(CLANG_HOST_BUILD_MK)
 include $(CLANG_TBLGEN_RULES_MK)
diff --git a/tools/versioner/src/CompilationType.cpp b/tools/versioner/src/CompilationType.cpp
new file mode 100644
index 0000000..e9d31cb
--- /dev/null
+++ b/tools/versioner/src/CompilationType.cpp
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "CompilationType.h"
+
+#include <sstream>
+#include <string>
+
+std::string to_string(const CompilationType& type) {
+  std::stringstream ss;
+  ss << to_string(type.arch) << "-" << type.api_level << " [fob = " << type.file_offset_bits << "]";
+  return ss.str();
+}
diff --git a/tools/versioner/src/CompilationType.h b/tools/versioner/src/CompilationType.h
new file mode 100644
index 0000000..7d516b2
--- /dev/null
+++ b/tools/versioner/src/CompilationType.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <utility>
+
+#include "Arch.h"
+
+struct CompilationType {
+  Arch arch;
+  int api_level;
+  int file_offset_bits;
+
+ private:
+  auto tie() const {
+    return std::tie(arch, api_level, file_offset_bits);
+  }
+
+ public:
+  bool operator<(const CompilationType& other) const {
+    return tie() < other.tie();
+  }
+
+  bool operator==(const CompilationType& other) const {
+    return tie() == other.tie();
+  }
+};
+
+namespace std {
+template <>
+struct hash<CompilationType> {
+  size_t operator()(CompilationType type) const {
+    struct {
+      int32_t arch : 3;
+      int32_t api_level : 6;
+      int32_t file_offset_bits : 1;
+      int32_t padding : 22;
+    } packed;
+    packed.arch = static_cast<int32_t>(type.arch);
+    packed.api_level = type.api_level;
+    packed.file_offset_bits = (type.file_offset_bits == 64);
+    packed.padding = 0;
+    int32_t value;
+    memcpy(&value, &packed, sizeof(value));
+    return std::hash<int32_t>()(value);
+  }
+};
+}
+
+std::string to_string(const CompilationType& type);
diff --git a/tools/versioner/src/DeclarationDatabase.cpp b/tools/versioner/src/DeclarationDatabase.cpp
index 9c9bc83..e9ba37e 100644
--- a/tools/versioner/src/DeclarationDatabase.cpp
+++ b/tools/versioner/src/DeclarationDatabase.cpp
@@ -284,19 +284,12 @@
   return false;
 }
 
-void HeaderDatabase::parseAST(CompilationType type, ASTUnit* ast) {
+void HeaderDatabase::parseAST(CompilationType type, ASTContext& ctx) {
   std::unique_lock<std::mutex> lock(this->mutex);
-  ASTContext& ctx = ast->getASTContext();
   Visitor visitor(*this, type, ctx);
   visitor.TraverseDecl(ctx.getTranslationUnitDecl());
 }
 
-std::string to_string(const CompilationType& type) {
-  std::stringstream ss;
-  ss << to_string(type.arch) << "-" << type.api_level << " [fob = " << type.file_offset_bits << "]";
-  return ss.str();
-}
-
 std::string to_string(const AvailabilityValues& av) {
   std::stringstream ss;
 
diff --git a/tools/versioner/src/DeclarationDatabase.h b/tools/versioner/src/DeclarationDatabase.h
index cf3589b..0daa2cd 100644
--- a/tools/versioner/src/DeclarationDatabase.h
+++ b/tools/versioner/src/DeclarationDatabase.h
@@ -27,10 +27,11 @@
 #include <llvm/ADT/StringRef.h>
 
 #include "Arch.h"
+#include "CompilationType.h"
 #include "Utils.h"
 
 namespace clang {
-class ASTUnit;
+class ASTContext;
 class Decl;
 }
 
@@ -40,28 +41,6 @@
   inconsistent,
 };
 
-struct CompilationType {
-  Arch arch;
-  int api_level;
-  int file_offset_bits;
-
- private:
-  auto tie() const {
-    return std::tie(arch, api_level, file_offset_bits);
-  }
-
- public:
-  bool operator<(const CompilationType& other) const {
-    return tie() < other.tie();
-  }
-
-  bool operator==(const CompilationType& other) const {
-    return tie() == other.tie();
-  }
-};
-
-std::string to_string(const CompilationType& type);
-
 struct AvailabilityValues {
   bool future = false;
   int introduced = 0;
@@ -219,7 +198,7 @@
  public:
   std::map<std::string, Symbol> symbols;
 
-  void parseAST(CompilationType type, clang::ASTUnit* ast);
+  void parseAST(CompilationType type, clang::ASTContext& ast);
 
   void dump(const std::string& base_path = "", FILE* out = stdout) const {
     fprintf(out, "HeaderDatabase contains %zu symbols:\n", symbols.size());
diff --git a/tools/versioner/src/Driver.cpp b/tools/versioner/src/Driver.cpp
new file mode 100644
index 0000000..215dc3c
--- /dev/null
+++ b/tools/versioner/src/Driver.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Driver.h"
+
+#include <err.h>
+#include <string.h>
+
+#include <chrono>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <unordered_map>
+#include <vector>
+
+#include <clang/AST/ASTConsumer.h>
+#include <clang/Basic/Diagnostic.h>
+#include <clang/Basic/TargetInfo.h>
+#include <clang/Basic/VirtualFileSystem.h>
+#include <clang/Driver/Compilation.h>
+#include <clang/Driver/Driver.h>
+#include <clang/Frontend/CompilerInstance.h>
+#include <clang/Frontend/CompilerInvocation.h>
+#include <clang/Frontend/FrontendAction.h>
+#include <clang/Frontend/FrontendActions.h>
+#include <clang/Frontend/TextDiagnosticPrinter.h>
+#include <clang/Frontend/Utils.h>
+#include <clang/FrontendTool/Utils.h>
+#include <llvm/ADT/IntrusiveRefCntPtr.h>
+#include <llvm/ADT/SmallVector.h>
+#include <llvm/ADT/StringRef.h>
+
+#include "Arch.h"
+#include "DeclarationDatabase.h"
+#include "versioner.h"
+
+using namespace std::chrono_literals;
+using namespace std::string_literals;
+
+using namespace clang;
+
+class VersionerASTConsumer : public clang::ASTConsumer {
+ public:
+  HeaderDatabase* header_database;
+  CompilationType type;
+
+  VersionerASTConsumer(HeaderDatabase* header_database, CompilationType type)
+      : header_database(header_database), type(type) {
+  }
+
+  virtual void HandleTranslationUnit(ASTContext& ctx) override {
+    header_database->parseAST(type, ctx);
+  }
+};
+
+class VersionerASTAction : public clang::ASTFrontendAction {
+ public:
+  HeaderDatabase* header_database;
+  CompilationType type;
+
+  VersionerASTAction(HeaderDatabase* header_database, CompilationType type)
+      : header_database(header_database), type(type) {
+  }
+
+  std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance&, llvm::StringRef) override {
+    return std::make_unique<VersionerASTConsumer>(header_database, type);
+  }
+};
+
+static IntrusiveRefCntPtr<DiagnosticsEngine> constructDiags() {
+  IntrusiveRefCntPtr<DiagnosticOptions> diag_opts(new DiagnosticOptions());
+  auto diag_printer = std::make_unique<TextDiagnosticPrinter>(llvm::errs(), diag_opts.get());
+  IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs());
+  IntrusiveRefCntPtr<DiagnosticsEngine> diags(
+      new DiagnosticsEngine(diag_ids.get(), diag_opts.get(), diag_printer.release()));
+  return diags;
+}
+
+// clang's driver is slow compared to the work it performs to compile our headers.
+// Run it once to generate flags for each target, and memoize the results.
+static std::unordered_map<CompilationType, std::vector<std::string>> cc1_flags;
+static const char* filename_placeholder = "__VERSIONER_PLACEHOLDER__";
+static void generateTargetCC1Flags(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,
+                                   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-header");
+  cmd.push_back("-fsyntax-only");
+
+  cmd.push_back("-Wall");
+  cmd.push_back("-Wextra");
+  cmd.push_back("-Werror");
+  cmd.push_back("-Wundef");
+  cmd.push_back("-Wno-unused-macros");
+  cmd.push_back("-Wno-unused-function");
+  cmd.push_back("-Wno-unused-variable");
+  cmd.push_back("-Wno-unknown-attributes");
+  cmd.push_back("-Wno-pragma-once-outside-header");
+
+  cmd.push_back("-target");
+  cmd.push_back(arch_targets[type.arch]);
+
+  cmd.push_back("-DANDROID");
+  cmd.push_back("-D__ANDROID_API__="s + std::to_string(type.api_level));
+  cmd.push_back("-D_FORTIFY_SOURCE=2");
+  cmd.push_back("-D_GNU_SOURCE");
+  cmd.push_back("-D_FILE_OFFSET_BITS="s + std::to_string(type.file_offset_bits));
+
+  cmd.push_back("-nostdinc");
+
+  if (add_include) {
+    cmd.push_back("-include");
+    cmd.push_back("android/versioning.h");
+  }
+
+  for (const auto& dir : include_dirs) {
+    cmd.push_back("-isystem");
+    cmd.push_back(dir);
+  }
+
+  cmd.push_back(filename_placeholder);
+
+  auto diags = constructDiags();
+  driver::Driver driver("versioner", llvm::sys::getDefaultTargetTriple(), *diags, vfs);
+  driver.setCheckInputsExist(false);
+
+  llvm::SmallVector<const char*, 32> driver_args;
+  for (const std::string& str : cmd) {
+    driver_args.push_back(str.c_str());
+  }
+
+  std::unique_ptr<driver::Compilation> Compilation(driver.BuildCompilation(driver_args));
+  const driver::JobList& jobs = Compilation->getJobs();
+  if (jobs.size() != 1) {
+    errx(1, "driver returned %zu jobs for %s", jobs.size(), to_string(type).c_str());
+  }
+
+  const driver::Command& driver_cmd = llvm::cast<driver::Command>(*jobs.begin());
+  const driver::ArgStringList& cc_args = driver_cmd.getArguments();
+
+  if (cc_args.size() == 0) {
+    errx(1, "driver returned empty command for %s", to_string(type).c_str());
+  }
+
+  std::vector<std::string> result(cc_args.begin(), cc_args.end());
+
+  {
+    static std::mutex cc1_init_mutex;
+    std::unique_lock<std::mutex> lock(cc1_init_mutex);
+    if (cc1_flags.count(type) > 0) {
+      errx(1, "attemped to generate cc1 flags for existing CompilationType %s",
+           to_string(type).c_str());
+    }
+
+    cc1_flags.emplace(std::make_pair(type, std::move(result)));
+  }
+}
+
+static std::vector<const char*> getCC1Command(CompilationType type, const std::string& filename) {
+  const auto& target_flag_it = cc1_flags.find(type);
+  if (target_flag_it == cc1_flags.end()) {
+    errx(1, "failed to find target flags for CompilationType %s", to_string(type).c_str());
+  }
+
+  std::vector<const char*> result;
+  for (const std::string& flag : target_flag_it->second) {
+    if (flag == "-disable-free") {
+      continue;
+    } else if (flag == filename_placeholder) {
+      result.push_back(filename.c_str());
+    } else {
+      result.push_back(flag.c_str());
+    }
+  }
+  return result;
+}
+
+void initializeTargetCC1FlagCache(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,
+                                  const std::set<CompilationType>& types,
+                                  const std::unordered_map<Arch, CompilationRequirements>& reqs) {
+  if (!cc1_flags.empty()) {
+    errx(1, "reinitializing target CC1 flag cache?");
+  }
+
+  auto start = std::chrono::high_resolution_clock::now();
+  std::vector<std::thread> threads;
+  for (const CompilationType type : types) {
+    threads.emplace_back([type, &vfs, &reqs]() {
+      const auto& arch_req_it = reqs.find(type.arch);
+      if (arch_req_it == reqs.end()) {
+        errx(1, "CompilationRequirement map missing entry for CompilationType %s",
+             to_string(type).c_str());
+      }
+
+      generateTargetCC1Flags(vfs, type, arch_req_it->second.dependencies);
+    });
+  }
+  for (auto& thread : threads) {
+    thread.join();
+  }
+  auto end = std::chrono::high_resolution_clock::now();
+
+  if (verbose) {
+    auto diff = (end - start) / 1.0ms;
+    printf("Generated compiler flags for %zu targets in %0.2Lfms\n", types.size(), diff);
+  }
+
+  if (cc1_flags.empty()) {
+    errx(1, "failed to initialize target CC1 flag cache");
+  }
+}
+
+void compileHeader(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,
+                   HeaderDatabase* header_database, CompilationType type,
+                   const std::string& filename) {
+  auto diags = constructDiags();
+  std::vector<const char*> cc1_flags = getCC1Command(type, filename);
+  auto invocation = std::make_unique<CompilerInvocation>();
+  if (!CompilerInvocation::CreateFromArgs(*invocation.get(), &cc1_flags.front(),
+                                          &cc1_flags.front() + cc1_flags.size(), *diags)) {
+    errx(1, "failed to create CompilerInvocation");
+  }
+
+  clang::CompilerInstance Compiler;
+  Compiler.setInvocation(invocation.release());
+  Compiler.setDiagnostics(diags.get());
+  Compiler.setVirtualFileSystem(vfs);
+
+  VersionerASTAction versioner_action(header_database, type);
+  if (!Compiler.ExecuteAction(versioner_action)) {
+    errx(1, "compilation generated warnings or errors");
+  }
+
+  if (diags->getNumWarnings() || diags->hasErrorOccurred()) {
+    errx(1, "compilation generated warnings or errors");
+  }
+}
diff --git a/tools/versioner/src/Driver.h b/tools/versioner/src/Driver.h
new file mode 100644
index 0000000..48683e4
--- /dev/null
+++ b/tools/versioner/src/Driver.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include <set>
+#include <string>
+#include <unordered_map>
+
+#include <llvm/ADT/IntrusiveRefCntPtr.h>
+
+#include "Arch.h"
+#include "DeclarationDatabase.h"
+#include "VFS.h"
+
+struct CompilationRequirements {
+  std::vector<std::string> headers;
+  std::vector<std::string> dependencies;
+};
+
+void initializeTargetCC1FlagCache(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,
+                                  const std::set<CompilationType>& types,
+                                  const std::unordered_map<Arch, CompilationRequirements>& reqs);
+
+void compileHeader(llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> vfs,
+                   HeaderDatabase* header_database, CompilationType type,
+                   const std::string& filename);
diff --git a/tools/versioner/src/Preprocessor.cpp b/tools/versioner/src/Preprocessor.cpp
index a52ce53..c863e6c 100644
--- a/tools/versioner/src/Preprocessor.cpp
+++ b/tools/versioner/src/Preprocessor.cpp
@@ -121,33 +121,6 @@
   return result;
 }
 
-static std::string dirname(const std::string& path) {
-  std::unique_ptr<char, decltype(&free)> path_copy(strdup(path.c_str()), free);
-  return dirname(path_copy.get());
-}
-
-static bool mkdirs(const std::string& path) {
-  struct stat st;
-  if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
-    return true;
-  }
-
-  std::string parent = dirname(path);
-  if (parent == path) {
-    return false;
-  }
-
-  if (!mkdirs(parent)) {
-    return false;
-  }
-
-  if (mkdir(path.c_str(), 0700) != 0) {
-    return false;
-  }
-
-  return true;
-}
-
 static void writeFileLines(const std::string& path, const std::deque<std::string>& lines) {
   if (!mkdirs(dirname(path))) {
     err(1, "failed to create directory '%s'", dirname(path).c_str());
diff --git a/tools/versioner/src/Utils.h b/tools/versioner/src/Utils.h
index d49fc88..7876f23 100644
--- a/tools/versioner/src/Utils.h
+++ b/tools/versioner/src/Utils.h
@@ -16,6 +16,11 @@
 
 #pragma once
 
+#include <errno.h>
+#include <libgen.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
 #include <string>
 #include <vector>
 
@@ -24,16 +29,53 @@
 std::string getWorkingDir();
 std::vector<std::string> collectFiles(const std::string& directory);
 
-static __attribute__((unused)) std::string to_string(const char* c) {
+static inline std::string dirname(const std::string& path) {
+  std::unique_ptr<char, decltype(&free)> path_copy(strdup(path.c_str()), free);
+  return dirname(path_copy.get());
+}
+
+static inline bool is_directory(const std::string& path) {
+  struct stat st;
+  if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
+    return true;
+  }
+  return false;
+}
+
+static inline bool mkdirs(const std::string& path) {
+  if (is_directory(path)) {
+    return true;
+  }
+
+  std::string parent = dirname(path);
+  if (parent == path) {
+    return false;
+  }
+
+  if (!mkdirs(parent)) {
+    return false;
+  }
+
+  if (mkdir(path.c_str(), 0700) != 0) {
+    if (errno != EEXIST) {
+      return false;
+    }
+    return is_directory(path);
+  }
+
+  return true;
+}
+
+static inline std::string to_string(const char* c) {
   return c;
 }
 
-static __attribute__((unused)) const std::string& to_string(const std::string& str) {
+static inline const std::string& to_string(const std::string& str) {
   return str;
 }
 
 template <typename Collection>
-static std::string Join(Collection c, const std::string& delimiter = ", ") {
+static inline std::string Join(Collection c, const std::string& delimiter = ", ") {
   std::string result;
   for (const auto& item : c) {
     using namespace std;
diff --git a/tools/versioner/src/VFS.cpp b/tools/versioner/src/VFS.cpp
new file mode 100644
index 0000000..cd6d367
--- /dev/null
+++ b/tools/versioner/src/VFS.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <err.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <clang/Basic/VirtualFileSystem.h>
+#include <llvm/ADT/IntrusiveRefCntPtr.h>
+#include <llvm/Support/MemoryBuffer.h>
+
+#include "Utils.h"
+
+using android::base::unique_fd;
+using namespace clang::vfs;
+
+static void addDirectoryToVFS(InMemoryFileSystem* vfs, const std::string& path) {
+  char* paths[] = { const_cast<char*>(path.c_str()), nullptr };
+  FTS* fts = fts_open(paths, FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOCHDIR, nullptr);
+  if (!fts) {
+    err(1, "failed to open directory %s", path.c_str());
+  }
+
+  while (FTSENT* ent = fts_read(fts)) {
+    if ((ent->fts_info & FTS_F) == 0) {
+      continue;
+    }
+
+    const char* file_path = ent->fts_accpath;
+    unique_fd fd(open(file_path, O_RDONLY | O_CLOEXEC));
+    if (fd == -1) {
+      err(1, "failed to open header '%s'", file_path);
+    }
+
+    auto buffer_opt = llvm::MemoryBuffer::getOpenFileSlice(fd, file_path, -1, 0);
+    if (!buffer_opt) {
+      errx(1, "failed to map header '%s'", file_path);
+    }
+
+    if (!vfs->addFile(file_path, ent->fts_statp->st_mtim.tv_sec, std::move(buffer_opt.get()))) {
+      errx(1, "failed to add file '%s'", file_path);
+    }
+  }
+
+  fts_close(fts);
+}
+
+llvm::IntrusiveRefCntPtr<FileSystem> createCommonVFS(const std::string& header_dir,
+                                                     const std::string& dependency_dir,
+                                                     bool add_versioning_header) {
+  auto vfs = std::make_unique<InMemoryFileSystem>();
+  addDirectoryToVFS(vfs.get(), header_dir);
+  if (!dependency_dir.empty()) {
+    addDirectoryToVFS(vfs.get(), dependency_dir);
+  }
+
+  if (add_versioning_header) {
+    const char* top = getenv("ANDROID_BUILD_TOP");
+    if (!top) {
+      errx(1, "-i passed, but ANDROID_BUILD_TOP is unset");
+    }
+
+    std::string header_path = std::string(top) + "/bionic/libc/include/android/versioning.h";
+    auto buffer_opt = llvm::MemoryBuffer::getFile(header_path);
+    if (!buffer_opt) {
+      err(1, "failed to open %s", header_path.c_str());
+    }
+    vfs->addFile("android/versioning.h", 0, std::move(buffer_opt.get()));
+  }
+
+  return llvm::IntrusiveRefCntPtr<FileSystem>(vfs.release());
+}
diff --git a/tools/versioner/src/VFS.h b/tools/versioner/src/VFS.h
new file mode 100644
index 0000000..e2ab002
--- /dev/null
+++ b/tools/versioner/src/VFS.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+
+#include <llvm/ADT/IntrusiveRefCntPtr.h>
+#include <clang/Basic/VirtualFileSystem.h>
+
+llvm::IntrusiveRefCntPtr<clang::vfs::FileSystem> createCommonVFS(const std::string& header_dir,
+                                                                 const std::string& dependency_dir,
+                                                                 bool add_versioning_header);
diff --git a/tools/versioner/src/versioner.cpp b/tools/versioner/src/versioner.cpp
index 2f0656c..86349e1 100644
--- a/tools/versioner/src/versioner.cpp
+++ b/tools/versioner/src/versioner.cpp
@@ -23,6 +23,7 @@
 #include <unistd.h>
 
 #include <atomic>
+#include <functional>
 #include <iostream>
 #include <map>
 #include <memory>
@@ -33,105 +34,32 @@
 #include <unordered_map>
 #include <vector>
 
-#include <clang/Frontend/TextDiagnosticPrinter.h>
-#include <clang/Tooling/Tooling.h>
 #include <llvm/ADT/StringRef.h>
 
+#include <android-base/parseint.h>
+
 #include "Arch.h"
 #include "DeclarationDatabase.h"
+#include "Driver.h"
 #include "Preprocessor.h"
 #include "SymbolDatabase.h"
 #include "Utils.h"
+#include "VFS.h"
+
 #include "versioner.h"
 
 using namespace std::string_literals;
-using namespace clang;
-using namespace clang::tooling;
 
+bool add_include;
 bool verbose;
-static bool add_include;
-
-class HeaderCompilationDatabase : public CompilationDatabase {
-  CompilationType type;
-  std::string cwd;
-  std::vector<std::string> headers;
-  std::vector<std::string> include_dirs;
-
- public:
-  HeaderCompilationDatabase(CompilationType type, std::string cwd, std::vector<std::string> headers,
-                            std::vector<std::string> include_dirs)
-      : type(type),
-        cwd(std::move(cwd)),
-        headers(std::move(headers)),
-        include_dirs(std::move(include_dirs)) {
-  }
-
-  CompileCommand generateCompileCommand(const std::string& filename) const {
-    std::vector<std::string> command = { "clang-tool", filename, "-nostdlibinc" };
-    for (const auto& dir : include_dirs) {
-      command.push_back("-isystem");
-      command.push_back(dir);
-    }
-    command.push_back("-std=c11");
-    command.push_back("-DANDROID");
-    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("-Wundef");
-    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");
-    command.push_back(arch_targets[type.arch]);
-
-    if (add_include) {
-      const char* top = getenv("ANDROID_BUILD_TOP");
-      std::string header_path = to_string(top) + "/bionic/libc/include/android/versioning.h";
-      command.push_back("-include");
-      command.push_back(std::move(header_path));
-    }
-
-    command.push_back("-D_FILE_OFFSET_BITS="s + std::to_string(type.file_offset_bits));
-
-    return CompileCommand(cwd, filename, command);
-  }
-
-  std::vector<CompileCommand> getAllCompileCommands() const override {
-    std::vector<CompileCommand> commands;
-    for (const std::string& file : headers) {
-      commands.push_back(generateCompileCommand(file));
-    }
-    return commands;
-  }
-
-  std::vector<CompileCommand> getCompileCommands(StringRef file) const override {
-    std::vector<CompileCommand> commands;
-    commands.push_back(generateCompileCommand(file));
-    return commands;
-  }
-
-  std::vector<std::string> getAllFiles() const override {
-    return headers;
-  }
-};
-
-struct CompilationRequirements {
-  std::vector<std::string> headers;
-  std::vector<std::string> dependencies;
-};
+static int max_thread_count = 48;
 
 static CompilationRequirements collectRequirements(const Arch& arch, const std::string& header_dir,
                                                    const std::string& dependency_dir) {
   std::vector<std::string> headers = collectFiles(header_dir);
-
   std::vector<std::string> dependencies = { header_dir };
   if (!dependency_dir.empty()) {
-    auto collect_children = [&dependencies](const std::string& dir_path) {
+    auto collect_children = [&dependencies, &dependency_dir](const std::string& dir_path) {
       DIR* dir = opendir(dir_path.c_str());
       if (!dir) {
         err(1, "failed to open dependency directory '%s'", dir_path.c_str());
@@ -207,70 +135,60 @@
 
 static std::unique_ptr<HeaderDatabase> compileHeaders(const std::set<CompilationType>& types,
                                                       const std::string& header_dir,
-                                                      const std::string& dependency_dir,
-                                                      bool* failed) {
-  constexpr size_t thread_count = 8;
-  size_t threads_created = 0;
-  std::mutex mutex;
-  std::vector<std::thread> threads(thread_count);
+                                                      const std::string& dependency_dir) {
+  if (types.empty()) {
+    errx(1, "compileHeaders received no CompilationTypes");
+  }
+
+  auto vfs = createCommonVFS(header_dir, dependency_dir, add_include);
+
+  size_t thread_count = max_thread_count;
+  std::vector<std::thread> threads;
 
   std::map<CompilationType, HeaderDatabase> header_databases;
   std::unordered_map<Arch, CompilationRequirements> requirements;
 
-  std::string cwd = getWorkingDir();
-  bool errors = false;
-
+  auto result = std::make_unique<HeaderDatabase>();
   for (const auto& type : types) {
     if (requirements.count(type.arch) == 0) {
       requirements[type.arch] = collectRequirements(type.arch, header_dir, dependency_dir);
     }
   }
 
-  auto result = std::make_unique<HeaderDatabase>();
-  for (const auto& type : types) {
-    size_t thread_id = threads_created++;
-    if (thread_id >= thread_count) {
-      thread_id = thread_id % thread_count;
-      threads[thread_id].join();
+  initializeTargetCC1FlagCache(vfs, types, requirements);
+
+  std::vector<std::pair<CompilationType, const std::string&>> jobs;
+  for (CompilationType type : types) {
+    CompilationRequirements& req = requirements[type.arch];
+    for (const std::string& header : req.headers) {
+      jobs.emplace_back(type, header);
+    }
+  }
+
+  thread_count = std::min(thread_count, jobs.size());
+
+  if (thread_count == 1) {
+    for (const auto& job : jobs) {
+      compileHeader(vfs, result.get(), job.first, job.second);
+    }
+  } else {
+    // Spawn threads.
+    for (size_t i = 0; i < thread_count; ++i) {
+      threads.emplace_back([&jobs, &result, &header_dir, vfs, thread_count, i]() {
+        size_t index = i;
+        while (index < jobs.size()) {
+          const auto& job = jobs[index];
+          compileHeader(vfs, result.get(), job.first, job.second);
+          index += thread_count;
+        }
+      });
     }
 
-    threads[thread_id] = std::thread(
-        [&](CompilationType type) {
-          const auto& req = requirements[type.arch];
-
-          HeaderCompilationDatabase compilation_database(type, cwd, req.headers, req.dependencies);
-
-          ClangTool tool(compilation_database, req.headers);
-
-          clang::DiagnosticOptions diagnostic_options;
-          std::vector<std::unique_ptr<ASTUnit>> asts;
-          tool.buildASTs(asts);
-          for (const auto& ast : asts) {
-            clang::DiagnosticsEngine& diagnostics_engine = ast->getDiagnostics();
-            if (diagnostics_engine.getNumWarnings() || diagnostics_engine.hasErrorOccurred()) {
-              std::unique_lock<std::mutex> l(mutex);
-              errors = true;
-              printf("versioner: compilation failure for %s in %s\n", to_string(type).c_str(),
-                     ast->getOriginalSourceFileName().str().c_str());
-            }
-
-            result->parseAST(type, ast.get());
-          }
-        },
-        type);
-  }
-
-  if (threads_created < thread_count) {
-    threads.resize(threads_created);
-  }
-
-  for (auto& thread : threads) {
-    thread.join();
-  }
-
-  if (errors) {
-    printf("versioner: compilation generated warnings or errors\n");
-    *failed = errors;
+    // Reap them.
+    for (auto& thread : threads) {
+      thread.join();
+    }
+    threads.clear();
   }
 
   return result;
@@ -492,6 +410,7 @@
     fprintf(stderr, "\n");
     fprintf(stderr, "Miscellaneous:\n");
     fprintf(stderr, "  -d\t\tdump function availability\n");
+    fprintf(stderr, "  -j THREADS\tmaximum number of threads to use\n");
     fprintf(stderr, "  -h\t\tdisplay this message\n");
     exit(0);
   }
@@ -503,12 +422,12 @@
   std::string platform_dir;
   std::set<Arch> selected_architectures;
   std::set<int> selected_levels;
-  bool dump = false;
   std::string preprocessor_output_path;
   bool force = false;
+  bool dump = false;
 
   int c;
-  while ((c = getopt(argc, argv, "a:r:p:vo:fdhi")) != -1) {
+  while ((c = getopt(argc, argv, "a:r:p:vo:fdj:hi")) != -1) {
     default_args = false;
     switch (c) {
       case 'a': {
@@ -575,6 +494,12 @@
         dump = true;
         break;
 
+      case 'j':
+        if (!android::base::ParseInt<int>(optarg, &max_thread_count, 1)) {
+          usage();
+        }
+        break;
+
       case 'h':
         usage(true);
         break;
@@ -647,10 +572,10 @@
     symbol_database = parsePlatforms(compilation_types, platform_dir);
   }
 
-  bool failed = false;
   std::unique_ptr<HeaderDatabase> declaration_database =
-      compileHeaders(compilation_types, header_dir, dependency_dir, &failed);
+      compileHeaders(compilation_types, header_dir, dependency_dir);
 
+  bool failed = false;
   if (dump) {
     declaration_database->dump(header_dir + "/");
   } else {
diff --git a/tools/versioner/src/versioner.h b/tools/versioner/src/versioner.h
index 9547f2c..97b1509 100644
--- a/tools/versioner/src/versioner.h
+++ b/tools/versioner/src/versioner.h
@@ -23,6 +23,7 @@
 #include <unordered_set>
 
 extern bool verbose;
+extern bool add_include;
 
 #define D(...)             \
   do {                     \
diff --git a/tools/versioner/tests/compilation_error/run.sh b/tools/versioner/tests/compilation_error/run.sh
index a34fda8..7e8d489 100644
--- a/tools/versioner/tests/compilation_error/run.sh
+++ b/tools/versioner/tests/compilation_error/run.sh
@@ -1 +1 @@
-versioner headers -p platforms -r arm -a 9 -i
\ No newline at end of file
+versioner headers -p platforms -r arm -a 9 -i -j1