Merge "Fix various <sys/socket.h> prototypes."
diff --git a/libc/Android.bp b/libc/Android.bp
index c706935..6ba8ae6 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -7,7 +7,6 @@
     "bionic/getpriority.c",
     "bionic/initgroups.c",
     "bionic/isatty.c",
-    "bionic/memmem.c",
     "bionic/pututline.c",
     "bionic/sched_cpualloc.c",
     "bionic/sched_cpucount.c",
@@ -1245,6 +1244,7 @@
         "bionic/mbrtoc16.cpp",
         "bionic/mbrtoc32.cpp",
         "bionic/mbstate.cpp",
+        "bionic/memmem.cpp",
         "bionic/mempcpy.cpp",
         "bionic/mkdir.cpp",
         "bionic/mkfifo.cpp",
diff --git a/libc/bionic/memmem.c b/libc/bionic/memmem.c
deleted file mode 100644
index e72501b..0000000
--- a/libc/bionic/memmem.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-/*
- * This uses the "Not So Naive" algorithm, a very simple but
- * usually effective algorithm, see:
- * http://www-igm.univ-mlv.fr/~lecroq/string/
- */
-#include <string.h>
-
-void *memmem(const void *haystack, size_t n, const void *needle, size_t m)
-{
-    if (m > n || !m || !n)
-        return NULL;
-
-    if (__builtin_expect((m > 1), 1)) {
-        const unsigned char*  y = (const unsigned char*) haystack;
-        const unsigned char*  x = (const unsigned char*) needle;
-        size_t                j = 0;
-        size_t                k = 1, l = 2;
-
-        if (x[0] == x[1]) {
-            k = 2;
-            l = 1;
-        }
-        while (j <= n-m) {
-            if (x[1] != y[j+1]) {
-                j += k;
-            } else {
-                if (!memcmp(x+2, y+j+2, m-2) && x[0] == y[j])
-                    return (void*) &y[j];
-                j += l;
-            }
-        }
-    } else {
-        /* degenerate case */
-        return memchr(haystack, ((unsigned char*)needle)[0], n);
-    }
-    return NULL;
-}
diff --git a/libc/bionic/memmem.cpp b/libc/bionic/memmem.cpp
new file mode 100644
index 0000000..61d681f
--- /dev/null
+++ b/libc/bionic/memmem.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 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 <string.h>
+
+void* memmem(const void* void_haystack, size_t n, const void* void_needle, size_t m) {
+  const unsigned char* haystack = reinterpret_cast<const unsigned char*>(void_haystack);
+  const unsigned char* needle = reinterpret_cast<const unsigned char*>(void_needle);
+
+  if (n < m) return nullptr;
+
+  if (m == 0) return const_cast<void*>(void_haystack);
+  if (m == 1) return memchr(haystack, needle[0], n);
+
+  // This uses the "Not So Naive" algorithm, a very simple but usually effective algorithm.
+  // http://www-igm.univ-mlv.fr/~lecroq/string/
+  const unsigned char* y = haystack;
+  const unsigned char* x = needle;
+  size_t j = 0;
+  size_t k = 1, l = 2;
+
+  if (x[0] == x[1]) {
+    k = 2;
+    l = 1;
+  }
+  while (j <= n-m) {
+    if (x[1] != y[j+1]) {
+      j += k;
+    } else {
+      if (!memcmp(x+2, y+j+2, m-2) && x[0] == y[j]) return const_cast<unsigned char*>(&y[j]);
+      j += l;
+    }
+  }
+  return nullptr;
+}
diff --git a/libc/include/sys/mman.h b/libc/include/sys/mman.h
index 934963f..79f1faf 100644
--- a/libc/include/sys/mman.h
+++ b/libc/include/sys/mman.h
@@ -57,8 +57,8 @@
 void* mmap64(void*, size_t, int, int, int, off64_t) __INTRODUCED_IN(21);
 
 int munmap(void*, size_t);
-int msync(const void*, size_t, int);
-int mprotect(const void*, size_t, int);
+int msync(void*, size_t, int);
+int mprotect(void*, size_t, int);
 void* mremap(void*, size_t, size_t, int, ...);
 
 int mlockall(int) __INTRODUCED_IN(17);
diff --git a/tests/Android.build.prebuilt.mk b/tests/Android.build.prebuilt.mk
index 2d12557..09c2366 100644
--- a/tests/Android.build.prebuilt.mk
+++ b/tests/Android.build.prebuilt.mk
@@ -21,6 +21,8 @@
 LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/prebuilt-elf-files
 LOCAL_MODULE_CLASS := EXECUTABLES
 
+LOCAL_MODULE_TARGET_ARCH := arm arm64 x86 x86_64
+
 LOCAL_SRC_FILES_arm := prebuilt-elf-files/arm/$(bionic_tests_module)
 LOCAL_SRC_FILES_arm64 := prebuilt-elf-files/arm64/$(bionic_tests_module)
 LOCAL_SRC_FILES_x86 := prebuilt-elf-files/x86/$(bionic_tests_module)
diff --git a/tests/Android.mk b/tests/Android.mk
index 482c828..e1efbbe 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -35,6 +35,12 @@
 bionic_tests_module := libtest_invalid-empty_shdr_table.so
 include $(LOCAL_PATH)/Android.build.prebuilt.mk
 
+bionic_tests_module := libtest_invalid-zero_shdr_table_offset.so
+include $(LOCAL_PATH)/Android.build.prebuilt.mk
+
+bionic_tests_module := libtest_invalid-zero_shdr_table_content.so
+include $(LOCAL_PATH)/Android.build.prebuilt.mk
+
 ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
 build_host := true
 else
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 68827fa..ecc2a12 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -1144,7 +1144,7 @@
 }
 
 // Bionic specific tests
-#if defined(__BIONIC__)
+#if defined(__BIONIC__) && !defined(__mips__)
 
 #if defined(__LP64__)
 #define NATIVE_TESTS_PATH "/nativetest64"
@@ -1194,4 +1194,20 @@
   ASSERT_STREQ(expected_dlerror.c_str(), dlerror());
 }
 
+TEST(dlfcn, dlopen_invalid_zero_shdr_table_offset) {
+  std::string libpath = std::string(getenv("ANDROID_DATA")) + PREBUILT_ELF_PATH + "/libtest_invalid-zero_shdr_table_offset.so";
+  void* handle = dlopen(libpath.c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle == nullptr);
+  std::string expected_dlerror = std::string("dlopen failed: \"") + libpath + "\" has invalid shdr offset/size: 0/";
+  ASSERT_SUBSTR(expected_dlerror.c_str(), dlerror());
+}
+
+TEST(dlfcn, dlopen_invalid_zero_shdr_table_content) {
+  std::string libpath = std::string(getenv("ANDROID_DATA")) + PREBUILT_ELF_PATH + "/libtest_invalid-zero_shdr_table_content.so";
+  void* handle = dlopen(libpath.c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle == nullptr);
+  std::string expected_dlerror = std::string("dlopen failed: \"") + libpath + "\" .dynamic section header was not found";
+  ASSERT_SUBSTR(expected_dlerror.c_str(), dlerror());
+}
+
 #endif
diff --git a/tests/prebuilt-elf-files/arm/libtest_invalid-zero_shdr_table_content.so b/tests/prebuilt-elf-files/arm/libtest_invalid-zero_shdr_table_content.so
new file mode 100755
index 0000000..829c7c2
--- /dev/null
+++ b/tests/prebuilt-elf-files/arm/libtest_invalid-zero_shdr_table_content.so
Binary files differ
diff --git a/tests/prebuilt-elf-files/arm/libtest_invalid-zero_shdr_table_offset.so b/tests/prebuilt-elf-files/arm/libtest_invalid-zero_shdr_table_offset.so
new file mode 100755
index 0000000..347cf8b
--- /dev/null
+++ b/tests/prebuilt-elf-files/arm/libtest_invalid-zero_shdr_table_offset.so
Binary files differ
diff --git a/tests/prebuilt-elf-files/arm64/libtest_invalid-zero_shdr_table_content.so b/tests/prebuilt-elf-files/arm64/libtest_invalid-zero_shdr_table_content.so
new file mode 100755
index 0000000..819a032
--- /dev/null
+++ b/tests/prebuilt-elf-files/arm64/libtest_invalid-zero_shdr_table_content.so
Binary files differ
diff --git a/tests/prebuilt-elf-files/arm64/libtest_invalid-zero_shdr_table_offset.so b/tests/prebuilt-elf-files/arm64/libtest_invalid-zero_shdr_table_offset.so
new file mode 100755
index 0000000..0a07e1c
--- /dev/null
+++ b/tests/prebuilt-elf-files/arm64/libtest_invalid-zero_shdr_table_offset.so
Binary files differ
diff --git a/tests/prebuilt-elf-files/x86/libtest_invalid-zero_shdr_table_content.so b/tests/prebuilt-elf-files/x86/libtest_invalid-zero_shdr_table_content.so
new file mode 100755
index 0000000..9bb21ed
--- /dev/null
+++ b/tests/prebuilt-elf-files/x86/libtest_invalid-zero_shdr_table_content.so
Binary files differ
diff --git a/tests/prebuilt-elf-files/x86/libtest_invalid-zero_shdr_table_offset.so b/tests/prebuilt-elf-files/x86/libtest_invalid-zero_shdr_table_offset.so
new file mode 100755
index 0000000..ca814ea
--- /dev/null
+++ b/tests/prebuilt-elf-files/x86/libtest_invalid-zero_shdr_table_offset.so
Binary files differ
diff --git a/tests/prebuilt-elf-files/x86_64/libtest_invalid-zero_shdr_table_content.so b/tests/prebuilt-elf-files/x86_64/libtest_invalid-zero_shdr_table_content.so
new file mode 100755
index 0000000..5ad764c
--- /dev/null
+++ b/tests/prebuilt-elf-files/x86_64/libtest_invalid-zero_shdr_table_content.so
Binary files differ
diff --git a/tests/prebuilt-elf-files/x86_64/libtest_invalid-zero_shdr_table_offset.so b/tests/prebuilt-elf-files/x86_64/libtest_invalid-zero_shdr_table_offset.so
new file mode 100755
index 0000000..1fae589
--- /dev/null
+++ b/tests/prebuilt-elf-files/x86_64/libtest_invalid-zero_shdr_table_offset.so
Binary files differ
diff --git a/tests/string_test.cpp b/tests/string_test.cpp
index 763d65c..385fe33 100644
--- a/tests/string_test.cpp
+++ b/tests/string_test.cpp
@@ -1455,3 +1455,32 @@
   }
   RunSingleBufferAlignTest(MEDIUM, DoMemcpySameTest);
 }
+
+TEST(STRING_TEST, memmem_strstr_empty_needle) {
+  const char* some_haystack = "haystack";
+  const char* empty_haystack = "";
+
+  ASSERT_EQ(some_haystack, memmem(some_haystack, 8, "", 0));
+  ASSERT_EQ(empty_haystack, memmem(empty_haystack, 0, "", 0));
+
+  ASSERT_EQ(some_haystack, strstr(some_haystack, ""));
+  ASSERT_EQ(empty_haystack, strstr(empty_haystack, ""));
+}
+
+TEST(STRING_TEST, memmem_smoke) {
+  const char haystack[] = "big\0daddy\0giant\0haystacks";
+  ASSERT_EQ(haystack, memmem(haystack, sizeof(haystack), "", 0));
+  ASSERT_EQ(haystack + 3, memmem(haystack, sizeof(haystack), "", 1));
+  ASSERT_EQ(haystack + 0, memmem(haystack, sizeof(haystack), "b", 1));
+  ASSERT_EQ(haystack + 1, memmem(haystack, sizeof(haystack), "i", 1));
+  ASSERT_EQ(haystack + 4, memmem(haystack, sizeof(haystack), "da", 2));
+  ASSERT_EQ(haystack + 8, memmem(haystack, sizeof(haystack), "y\0g", 3));
+}
+
+TEST(STRING_TEST, strstr_smoke) {
+  const char* haystack = "big daddy/giant haystacks";
+  ASSERT_EQ(haystack, strstr(haystack, ""));
+  ASSERT_EQ(haystack + 0, strstr(haystack, "b"));
+  ASSERT_EQ(haystack + 1, strstr(haystack, "i"));
+  ASSERT_EQ(haystack + 4, strstr(haystack, "da"));
+}
diff --git a/tools/update_headers.sh b/tools/update_headers.sh
new file mode 100755
index 0000000..345b657
--- /dev/null
+++ b/tools/update_headers.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+cd $DIR
+
+which versioner >/dev/null 2>&1
+if [ $? -ne 0 ]; then
+  >&2 echo "versioner not in path; run mma in $DIR/versioner"
+  exit 1
+fi
+
+VERSION=$(git rev-parse --short HEAD)
+git diff-index --quiet HEAD
+DIRTY=$?
+git branch -r --contains HEAD | grep -q aosp/master
+SUBMITTED=$?
+
+if [ $DIRTY -ne 0 ]; then
+  >&2 echo "Warning: bionic has uncommitted changes"
+  VERSION="${VERSION}-dirty"
+elif [ $SUBMITTED -ne 0 ]; then
+  >&2 echo "Warning: current HEAD does not exist in aosp/master"
+  VERSION="${VERSION}-unsubmitted"
+fi
+
+PREBUILTS_DIR=$ANDROID_BUILD_TOP/prebuilts/ndk
+BRANCH_NAME=$(git -C $PREBUILTS_DIR symbolic-ref --short -q HEAD)
+if [ $? -ne 0 ]; then
+  BRANCH_NAME=update-bionic-headers-$VERSION
+  echo "prebuilts/ndk has detached head; creating branch $BRANCH_NAME"
+  repo start $BRANCH_NAME $PREBUILTS_DIR
+else
+  echo "prebuilts/ndk already on branch $BRANCH_NAME"
+fi
+
+HEADERS_INSTALL=$PREBUILTS_DIR/headers
+if [ -d "$HEADERS_INSTALL" ]; then
+  git -C $PREBUILTS_DIR rm -r --ignore-unmatch $HEADERS_INSTALL
+  rm -r $HEADERS_INSTALL
+fi
+
+versioner -p versioner/platforms versioner/current versioner/dependencies \
+  -o $HEADERS_INSTALL
+
+git -C $PREBUILTS_DIR add $HEADERS_INSTALL
+git -C $PREBUILTS_DIR commit -m "Update bionic headers to $VERSION."
diff --git a/tools/versioner/run_tests.py b/tools/versioner/run_tests.py
index 9bac3e2..f3bb6db 100755
--- a/tools/versioner/run_tests.py
+++ b/tools/versioner/run_tests.py
@@ -53,13 +53,33 @@
     print("{} {}".format(prefix_pass, test_name))
     return True
 
+
+def usage():
+    print("Usage: run_tests.py [-f]")
+    print("    -f\t\tdon't run slow tests")
+    sys.exit(0)
+
+
 root_dir = os.path.dirname(os.path.realpath(__file__))
 test_dir = os.path.join(root_dir, "tests")
 tests = os.listdir(test_dir)
+run_slow = True
+
+if len(sys.argv) > 2:
+    usage()
+elif len(sys.argv) == 2:
+    if sys.argv[1] != "-f":
+        usage()
+    run_slow = False
 
 success = True
 for test in sorted(tests):
-    if not run_test(test, os.path.join(test_dir, test)):
+    if test.startswith("slow") and not run_slow:
+        continue
+    path = os.path.join(test_dir, test)
+    if not os.path.isdir(path):
+        continue
+    if not run_test(test, path):
         success = False
 
 sys.exit(0 if success else 1)
diff --git a/tools/versioner/src/DeclarationDatabase.cpp b/tools/versioner/src/DeclarationDatabase.cpp
index 8e8d84f..88f7b55 100644
--- a/tools/versioner/src/DeclarationDatabase.cpp
+++ b/tools/versioner/src/DeclarationDatabase.cpp
@@ -288,7 +288,7 @@
 
 std::string to_string(const CompilationType& type) {
   std::stringstream ss;
-  ss << to_string(type.arch) << "-" << type.api_level;
+  ss << to_string(type.arch) << "-" << type.api_level << " [fob = " << type.file_offset_bits << "]";
   return ss.str();
 }
 
diff --git a/tools/versioner/src/DeclarationDatabase.h b/tools/versioner/src/DeclarationDatabase.h
index 8fe12ea..b130ca9 100644
--- a/tools/versioner/src/DeclarationDatabase.h
+++ b/tools/versioner/src/DeclarationDatabase.h
@@ -43,10 +43,11 @@
 struct CompilationType {
   Arch arch;
   int api_level;
+  int file_offset_bits;
 
  private:
   auto tie() const {
-    return std::tie(arch, api_level);
+    return std::tie(arch, api_level, file_offset_bits);
   }
 
  public:
diff --git a/tools/versioner/src/Preprocessor.cpp b/tools/versioner/src/Preprocessor.cpp
index 33531bd..8d0b943 100644
--- a/tools/versioner/src/Preprocessor.cpp
+++ b/tools/versioner/src/Preprocessor.cpp
@@ -93,12 +93,12 @@
   }
 
   DeclarationAvailability result = decl_av;
-  if (result.global_availability.introduced < global_min_api_visible) {
+  if (result.global_availability.introduced <= global_min_api_visible) {
     result.global_availability.introduced = 0;
   }
 
   for (Arch arch : supported_archs) {
-    if (result.arch_availability[arch].introduced < arch_visibility[arch]) {
+    if (result.arch_availability[arch].introduced <= arch_visibility[arch]) {
       result.arch_availability[arch].introduced = 0;
     }
   }
@@ -459,7 +459,7 @@
     }
   }
 
-  // Copy over any unchanged files directly.
+  // Copy over the original headers before preprocessing.
   char* fts_paths[2] = { const_cast<char*>(src_dir.c_str()), nullptr };
   FTS* fts = fts_open(fts_paths, FTS_LOGICAL, nullptr);
   while (FTSENT* ent = fts_read(fts)) {
@@ -473,18 +473,14 @@
     }
 
     std::string rel_path = path.substr(src_dir.length() + 1);
-    if (guards.count(rel_path) == 0) {
-      std::string dst_path = dst_dir + "/" + rel_path;
-      llvm::StringRef parent_path = llvm::sys::path::parent_path(dst_path);
-      if (llvm::sys::fs::create_directories(parent_path)) {
-        errx(1, "failed to ensure existence of directory '%s'", parent_path.str().c_str());
-      }
-      if (llvm::sys::fs::copy_file(path, dst_path)) {
-        errx(1, "failed to copy '%s/%s' to '%s'", src_dir.c_str(), path.str().c_str(),
-             dst_path.c_str());
-      }
-
-      printf("Copied unmodified header %s\n", dst_path.c_str());
+    std::string dst_path = dst_dir + "/" + rel_path;
+    llvm::StringRef parent_path = llvm::sys::path::parent_path(dst_path);
+    if (llvm::sys::fs::create_directories(parent_path)) {
+      errx(1, "failed to ensure existence of directory '%s'", parent_path.str().c_str());
+    }
+    if (llvm::sys::fs::copy_file(path, dst_path)) {
+      errx(1, "failed to copy '%s/%s' to '%s'", src_dir.c_str(), path.str().c_str(),
+           dst_path.c_str());
     }
   }
   fts_close(fts);
diff --git a/tools/versioner/src/versioner.cpp b/tools/versioner/src/versioner.cpp
index b18c3c2..432fd66 100644
--- a/tools/versioner/src/versioner.cpp
+++ b/tools/versioner/src/versioner.cpp
@@ -89,6 +89,8 @@
       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);
   }
 
@@ -184,8 +186,13 @@
       if (api_level < min_api) {
         continue;
       }
-      CompilationType type = { .arch = arch, .api_level = api_level };
-      result.insert(type);
+
+      for (int file_offset_bits : { 32, 64 }) {
+        CompilationType type = {
+          .arch = arch, .api_level = api_level, .file_offset_bits = file_offset_bits
+        };
+        result.insert(type);
+      }
     }
   }
   return result;
diff --git a/tools/versioner/tests/.gitignore b/tools/versioner/tests/.gitignore
new file mode 100644
index 0000000..89f9ac0
--- /dev/null
+++ b/tools/versioner/tests/.gitignore
@@ -0,0 +1 @@
+out/
diff --git a/tools/versioner/tests/compilation_error/expected_fail b/tools/versioner/tests/compilation_error/expected_fail
index 62a643f..f18b625 100644
--- a/tools/versioner/tests/compilation_error/expected_fail
+++ b/tools/versioner/tests/compilation_error/expected_fail
@@ -1,2 +1 @@
-versioner: compilation failure for arm-9 in /android/aosp/bionic/tools/versioner/tests/compilation_error/headers/foo.h
 versioner: compilation generated warnings or errors
diff --git a/tools/versioner/tests/missing_api/expected_fail b/tools/versioner/tests/missing_api/expected_fail
index 65a25f2..18e7845 100644
--- a/tools/versioner/tests/missing_api/expected_fail
+++ b/tools/versioner/tests/missing_api/expected_fail
@@ -1,4 +1,3 @@
-foo: declaration marked available but symbol missing in [arm-12]
   foo: introduced = 9
     extern declaration @ headers/foo.h:1:1
       introduced = 9
diff --git a/tools/versioner/tests/missing_arch/expected_fail b/tools/versioner/tests/missing_arch/expected_fail
index ed8ab79..82c2b28 100644
--- a/tools/versioner/tests/missing_arch/expected_fail
+++ b/tools/versioner/tests/missing_arch/expected_fail
@@ -1,4 +1,3 @@
-foo: declaration marked available but symbol missing in [x86-9]
   foo: no availability
     extern declaration @ headers/foo.h:1:1
       no availability
diff --git a/tools/versioner/tests/preprocessor/expected/foo.h b/tools/versioner/tests/preprocessor/expected/foo.h
index 3e05a1e..b83dd07 100644
--- a/tools/versioner/tests/preprocessor/expected/foo.h
+++ b/tools/versioner/tests/preprocessor/expected/foo.h
@@ -3,44 +3,64 @@
 int also_always_available() __INTRODUCED_IN(9);
 
 
-#if __ANDROID_API__ >= 10
-int needs_guard() __INTRODUCED_IN(10);
-#endif /* __ANDROID_API__ >= 10 */
+#if __ANDROID_API__ >= 13
+int needs_guard() __INTRODUCED_IN(13);
+#endif /* __ANDROID_API__ >= 13 */
 
 
-#if __ANDROID_API__ >= 10
-int already_guarded() __INTRODUCED_IN(10);
+#if __ANDROID_API__ >= 12
+
+#if __ANDROID_API__ >= 13
+int needs_guard_2() __INTRODUCED_IN(13);
+#endif /* __ANDROID_API__ >= 13 */
+
+#endif
+
+#if __ANDROID_API__ >= 13
+int already_guarded() __INTRODUCED_IN(13);
+#endif
+
+#if __ANDROID_API__ > 13
+int already_guarded_2() __INTRODUCED_IN(13);
 #endif
 
 #if defined(__arm__)
 
-#if __ANDROID_API__ >= 11
-int specific_arch() __INTRODUCED_IN(11);
-#endif /* __ANDROID_API__ >= 11 */
+#if __ANDROID_API__ >= 14
+int specific_arch() __INTRODUCED_IN(14);
+#endif /* __ANDROID_API__ >= 14 */
 
+
+#if __ANDROID_API__ >= 14
+int specific_arch_already_guarded() __INTRODUCED_IN(14);
+#endif
+
+#if __ANDROID_API__ > 14
+int specific_arch_already_guarded_2() __INTRODUCED_IN(14);
+#endif
 #endif
 
 #if defined(__arm__) || defined(__i386__)
 
-#if __ANDROID_API__ >= 11
-int multiple_archs() __INTRODUCED_IN(11);
-#endif /* __ANDROID_API__ >= 11 */
+#if __ANDROID_API__ >= 14
+int multiple_archs() __INTRODUCED_IN(14);
+#endif /* __ANDROID_API__ >= 14 */
 
 #endif
 
 // __INTRODUCED_IN_64(21) should be ignored.
 
-#if (defined(__LP64__)) || (defined(__arm__) && __ANDROID_API__ >= 10) || (defined(__mips__) && !defined(__LP64__) && __ANDROID_API__ >= 11) || (defined(__i386__) && __ANDROID_API__ >= 10)
-int multiple_introduced_1() __INTRODUCED_IN_ARM(10) __INTRODUCED_IN_MIPS(11) __INTRODUCED_IN_X86(10)
+#if (defined(__LP64__)) || (defined(__arm__) && __ANDROID_API__ >= 13) || (defined(__mips__) && !defined(__LP64__) && __ANDROID_API__ >= 14) || (defined(__i386__) && __ANDROID_API__ >= 13)
+int multiple_introduced_1() __INTRODUCED_IN_ARM(13) __INTRODUCED_IN_MIPS(14) __INTRODUCED_IN_X86(13)
     __INTRODUCED_IN_64(21);
-#endif /* (defined(__LP64__)) || (defined(__arm__) && __ANDROID_API__ >= 10) || (defined(__mips__) && !defined(__LP64__) && __ANDROID_API__ >= 11) || (defined(__i386__) && __ANDROID_API__ >= 10) */
+#endif /* (defined(__LP64__)) || (defined(__arm__) && __ANDROID_API__ >= 13) || (defined(__mips__) && !defined(__LP64__) && __ANDROID_API__ >= 14) || (defined(__i386__) && __ANDROID_API__ >= 13) */
 
 
 
-#if (defined(__LP64__) && __ANDROID_API__ >= 22) || (defined(__arm__) && __ANDROID_API__ >= 10) || (defined(__mips__) && !defined(__LP64__) && __ANDROID_API__ >= 11) || (defined(__i386__) && __ANDROID_API__ >= 10)
-int multiple_introduced_2() __INTRODUCED_IN_ARM(10) __INTRODUCED_IN_MIPS(11) __INTRODUCED_IN_X86(10)
+#if (defined(__LP64__) && __ANDROID_API__ >= 22) || (defined(__arm__) && __ANDROID_API__ >= 13) || (defined(__mips__) && !defined(__LP64__) && __ANDROID_API__ >= 14) || (defined(__i386__) && __ANDROID_API__ >= 13)
+int multiple_introduced_2() __INTRODUCED_IN_ARM(13) __INTRODUCED_IN_MIPS(14) __INTRODUCED_IN_X86(13)
     __INTRODUCED_IN_64(22);
-#endif /* (defined(__LP64__) && __ANDROID_API__ >= 22) || (defined(__arm__) && __ANDROID_API__ >= 10) || (defined(__mips__) && !defined(__LP64__) && __ANDROID_API__ >= 11) || (defined(__i386__) && __ANDROID_API__ >= 10) */
+#endif /* (defined(__LP64__) && __ANDROID_API__ >= 22) || (defined(__arm__) && __ANDROID_API__ >= 13) || (defined(__mips__) && !defined(__LP64__) && __ANDROID_API__ >= 14) || (defined(__i386__) && __ANDROID_API__ >= 13) */
 
 
 
diff --git a/tools/versioner/tests/preprocessor/headers/foo.h b/tools/versioner/tests/preprocessor/headers/foo.h
index ae5b847..7eba47f 100644
--- a/tools/versioner/tests/preprocessor/headers/foo.h
+++ b/tools/versioner/tests/preprocessor/headers/foo.h
@@ -2,25 +2,41 @@
 
 int also_always_available() __INTRODUCED_IN(9);
 
-int needs_guard() __INTRODUCED_IN(10);
+int needs_guard() __INTRODUCED_IN(13);
 
-#if __ANDROID_API__ >= 10
-int already_guarded() __INTRODUCED_IN(10);
+#if __ANDROID_API__ >= 12
+int needs_guard_2() __INTRODUCED_IN(13);
+#endif
+
+#if __ANDROID_API__ >= 13
+int already_guarded() __INTRODUCED_IN(13);
+#endif
+
+#if __ANDROID_API__ > 13
+int already_guarded_2() __INTRODUCED_IN(13);
 #endif
 
 #if defined(__arm__)
-int specific_arch() __INTRODUCED_IN(11);
+int specific_arch() __INTRODUCED_IN(14);
+
+#if __ANDROID_API__ >= 14
+int specific_arch_already_guarded() __INTRODUCED_IN(14);
+#endif
+
+#if __ANDROID_API__ > 14
+int specific_arch_already_guarded_2() __INTRODUCED_IN(14);
+#endif
 #endif
 
 #if defined(__arm__) || defined(__i386__)
-int multiple_archs() __INTRODUCED_IN(11);
+int multiple_archs() __INTRODUCED_IN(14);
 #endif
 
 // __INTRODUCED_IN_64(21) should be ignored.
-int multiple_introduced_1() __INTRODUCED_IN_ARM(10) __INTRODUCED_IN_MIPS(11) __INTRODUCED_IN_X86(10)
+int multiple_introduced_1() __INTRODUCED_IN_ARM(13) __INTRODUCED_IN_MIPS(14) __INTRODUCED_IN_X86(13)
     __INTRODUCED_IN_64(21);
 
-int multiple_introduced_2() __INTRODUCED_IN_ARM(10) __INTRODUCED_IN_MIPS(11) __INTRODUCED_IN_X86(10)
+int multiple_introduced_2() __INTRODUCED_IN_ARM(13) __INTRODUCED_IN_MIPS(14) __INTRODUCED_IN_X86(13)
     __INTRODUCED_IN_64(22);
 
 int group_lp32() __INTRODUCED_IN_ARM(12) __INTRODUCED_IN_X86(12) __INTRODUCED_IN_MIPS(12);
diff --git a/tools/versioner/tests/preprocessor/out/foo.h b/tools/versioner/tests/preprocessor/out/foo.h
deleted file mode 100644
index 3e05a1e..0000000
--- a/tools/versioner/tests/preprocessor/out/foo.h
+++ /dev/null
@@ -1,50 +0,0 @@
-int always_available();
-
-int also_always_available() __INTRODUCED_IN(9);
-
-
-#if __ANDROID_API__ >= 10
-int needs_guard() __INTRODUCED_IN(10);
-#endif /* __ANDROID_API__ >= 10 */
-
-
-#if __ANDROID_API__ >= 10
-int already_guarded() __INTRODUCED_IN(10);
-#endif
-
-#if defined(__arm__)
-
-#if __ANDROID_API__ >= 11
-int specific_arch() __INTRODUCED_IN(11);
-#endif /* __ANDROID_API__ >= 11 */
-
-#endif
-
-#if defined(__arm__) || defined(__i386__)
-
-#if __ANDROID_API__ >= 11
-int multiple_archs() __INTRODUCED_IN(11);
-#endif /* __ANDROID_API__ >= 11 */
-
-#endif
-
-// __INTRODUCED_IN_64(21) should be ignored.
-
-#if (defined(__LP64__)) || (defined(__arm__) && __ANDROID_API__ >= 10) || (defined(__mips__) && !defined(__LP64__) && __ANDROID_API__ >= 11) || (defined(__i386__) && __ANDROID_API__ >= 10)
-int multiple_introduced_1() __INTRODUCED_IN_ARM(10) __INTRODUCED_IN_MIPS(11) __INTRODUCED_IN_X86(10)
-    __INTRODUCED_IN_64(21);
-#endif /* (defined(__LP64__)) || (defined(__arm__) && __ANDROID_API__ >= 10) || (defined(__mips__) && !defined(__LP64__) && __ANDROID_API__ >= 11) || (defined(__i386__) && __ANDROID_API__ >= 10) */
-
-
-
-#if (defined(__LP64__) && __ANDROID_API__ >= 22) || (defined(__arm__) && __ANDROID_API__ >= 10) || (defined(__mips__) && !defined(__LP64__) && __ANDROID_API__ >= 11) || (defined(__i386__) && __ANDROID_API__ >= 10)
-int multiple_introduced_2() __INTRODUCED_IN_ARM(10) __INTRODUCED_IN_MIPS(11) __INTRODUCED_IN_X86(10)
-    __INTRODUCED_IN_64(22);
-#endif /* (defined(__LP64__) && __ANDROID_API__ >= 22) || (defined(__arm__) && __ANDROID_API__ >= 10) || (defined(__mips__) && !defined(__LP64__) && __ANDROID_API__ >= 11) || (defined(__i386__) && __ANDROID_API__ >= 10) */
-
-
-
-#if (!defined(__LP64__) && __ANDROID_API__ >= 12) || (defined(__LP64__))
-int group_lp32() __INTRODUCED_IN_ARM(12) __INTRODUCED_IN_X86(12) __INTRODUCED_IN_MIPS(12);
-#endif /* (!defined(__LP64__) && __ANDROID_API__ >= 12) || (defined(__LP64__)) */
-
diff --git a/tools/versioner/tests/preprocessor/run.sh b/tools/versioner/tests/preprocessor/run.sh
index 1b0aae2..60e7024 100644
--- a/tools/versioner/tests/preprocessor/run.sh
+++ b/tools/versioner/tests/preprocessor/run.sh
@@ -1,4 +1,29 @@
-rm -rf out
 set -e
-versioner headers -i -o out
-diff -q -w -B out expected
+
+function run_test {
+  SRC=$1
+  DST=$2
+  rm -rf $2
+  versioner $1 -i -o $2
+  diff -q -w -B $2 expected
+}
+
+run_test headers out
+run_test headers/ out
+run_test headers out/
+run_test headers/ out/
+
+run_test `pwd`/headers out
+run_test `pwd`/headers/ out
+run_test `pwd`/headers out/
+run_test `pwd`/headers/ out/
+
+run_test headers `pwd`/out
+run_test headers/ `pwd`/out
+run_test headers `pwd`/out/
+run_test headers/ `pwd`/out/
+
+run_test `pwd`/headers `pwd`/out
+run_test `pwd`/headers/ `pwd`/out
+run_test `pwd`/headers `pwd`/out/
+run_test `pwd`/headers/ `pwd`/out/
diff --git a/tools/versioner/tests/preprocessor_file_offset_bits/expected/foo.h b/tools/versioner/tests/preprocessor_file_offset_bits/expected/foo.h
new file mode 100644
index 0000000..7d31ec3
--- /dev/null
+++ b/tools/versioner/tests/preprocessor_file_offset_bits/expected/foo.h
@@ -0,0 +1,34 @@
+typedef int off_t;
+typedef int ssize_t;
+typedef unsigned size_t;
+
+#if !defined(__LP64__) && defined(_FILE_OFFSET_BITS)
+#if _FILE_OFFSET_BITS == 64
+#define __USE_FILE_OFFSET64 1
+#endif
+#endif
+
+#define __RENAME(x) __asm__(#x)
+
+#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ >= 21
+int truncate(const char* __path, off_t __length) __RENAME(truncate64) __INTRODUCED_IN(21);
+#else
+int truncate(const char* __path, off_t __length);
+#endif
+
+#if defined(__USE_FILE_OFFSET64)
+
+#if __ANDROID_API__ >= 12
+ssize_t pread(int __fd, void* __buf, size_t __count, off_t __offset) __RENAME(pread64)
+    __INTRODUCED_IN(12);
+#endif /* __ANDROID_API__ >= 12 */
+
+#else
+ssize_t pread(int __fd, void* __buf, size_t __count, off_t __offset);
+#endif
+
+#if defined(__USE_FILE_OFFSET64)
+off_t lseek(int __fd, off_t __offset, int __whence) __RENAME(lseek64);
+#else
+off_t lseek(int __fd, off_t __offset, int __whence);
+#endif
diff --git a/tools/versioner/tests/preprocessor_file_offset_bits/headers/foo.h b/tools/versioner/tests/preprocessor_file_offset_bits/headers/foo.h
new file mode 100644
index 0000000..868d1fc
--- /dev/null
+++ b/tools/versioner/tests/preprocessor_file_offset_bits/headers/foo.h
@@ -0,0 +1,30 @@
+typedef int off_t;
+typedef int ssize_t;
+typedef unsigned size_t;
+
+#if !defined(__LP64__) && defined(_FILE_OFFSET_BITS)
+#if _FILE_OFFSET_BITS == 64
+#define __USE_FILE_OFFSET64 1
+#endif
+#endif
+
+#define __RENAME(x) __asm__(#x)
+
+#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ >= 21
+int truncate(const char* __path, off_t __length) __RENAME(truncate64) __INTRODUCED_IN(21);
+#else
+int truncate(const char* __path, off_t __length);
+#endif
+
+#if defined(__USE_FILE_OFFSET64)
+ssize_t pread(int __fd, void* __buf, size_t __count, off_t __offset) __RENAME(pread64)
+    __INTRODUCED_IN(12);
+#else
+ssize_t pread(int __fd, void* __buf, size_t __count, off_t __offset);
+#endif
+
+#if defined(__USE_FILE_OFFSET64)
+off_t lseek(int __fd, off_t __offset, int __whence) __RENAME(lseek64);
+#else
+off_t lseek(int __fd, off_t __offset, int __whence);
+#endif
diff --git a/tools/versioner/tests/preprocessor_file_offset_bits/run.sh b/tools/versioner/tests/preprocessor_file_offset_bits/run.sh
new file mode 100644
index 0000000..c503867
--- /dev/null
+++ b/tools/versioner/tests/preprocessor_file_offset_bits/run.sh
@@ -0,0 +1,5 @@
+set -e
+
+rm -rf out
+versioner headers -i -o out
+diff -q -w -B out expected
diff --git a/tools/versioner/tests/preprocessor_idempotence/expected/foo.h b/tools/versioner/tests/preprocessor_idempotence/expected/foo.h
new file mode 100644
index 0000000..ed31e8b
--- /dev/null
+++ b/tools/versioner/tests/preprocessor_idempotence/expected/foo.h
@@ -0,0 +1,12 @@
+#if __ANDROID_API__ >= 10
+int foo() __INTRODUCED_IN(10);
+#endif
+
+#if __ANDROID_API__ >= 21
+int bar(int) __INTRODUCED_IN(21);
+#endif
+
+#if __ANDROID_API__ >= 10
+int multiple_1() __INTRODUCED_IN(10);
+int multiple_2() __INTRODUCED_IN(10);
+#endif
diff --git a/tools/versioner/tests/preprocessor_idempotence/headers/foo.h b/tools/versioner/tests/preprocessor_idempotence/headers/foo.h
new file mode 100644
index 0000000..ed31e8b
--- /dev/null
+++ b/tools/versioner/tests/preprocessor_idempotence/headers/foo.h
@@ -0,0 +1,12 @@
+#if __ANDROID_API__ >= 10
+int foo() __INTRODUCED_IN(10);
+#endif
+
+#if __ANDROID_API__ >= 21
+int bar(int) __INTRODUCED_IN(21);
+#endif
+
+#if __ANDROID_API__ >= 10
+int multiple_1() __INTRODUCED_IN(10);
+int multiple_2() __INTRODUCED_IN(10);
+#endif
diff --git a/tools/versioner/tests/preprocessor_idempotence/run.sh b/tools/versioner/tests/preprocessor_idempotence/run.sh
new file mode 100644
index 0000000..1b0aae2
--- /dev/null
+++ b/tools/versioner/tests/preprocessor_idempotence/run.sh
@@ -0,0 +1,4 @@
+rm -rf out
+set -e
+versioner headers -i -o out
+diff -q -w -B out expected
diff --git a/tools/versioner/tests/slow_preprocessor_idempotence/run.sh b/tools/versioner/tests/slow_preprocessor_idempotence/run.sh
new file mode 100644
index 0000000..6426156
--- /dev/null
+++ b/tools/versioner/tests/slow_preprocessor_idempotence/run.sh
@@ -0,0 +1,6 @@
+rm -rf out
+set -e
+mkdir out
+versioner -o out/initial
+versioner out/initial ../../dependencies -o out/second
+diff -qrwB out/initial out/second