Merge "bionic: Allocate a shadow call stack for each thread."
diff --git a/libc/bionic/grp_pwd_file.h b/libc/bionic/grp_pwd_file.h
index 29d75f4..9004c4e 100644
--- a/libc/bionic/grp_pwd_file.h
+++ b/libc/bionic/grp_pwd_file.h
@@ -45,7 +45,7 @@
   bool FindByName(const char* name, Line* line);
   void Unmap();
 
-  DISALLOW_COPY_AND_ASSIGN(MmapFile);
+  BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(MmapFile);
 
  private:
   enum class FileStatus {
@@ -78,7 +78,7 @@
     mmap_file_.Unmap();
   }
 
-  DISALLOW_COPY_AND_ASSIGN(PasswdFile);
+  BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(PasswdFile);
 
  private:
   MmapFile mmap_file_;
@@ -94,7 +94,7 @@
     mmap_file_.Unmap();
   }
 
-  DISALLOW_COPY_AND_ASSIGN(GroupFile);
+  BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(GroupFile);
 
  private:
   MmapFile mmap_file_;
diff --git a/libc/bionic/locale.cpp b/libc/bionic/locale.cpp
index 2a5bcab..8358fb0 100644
--- a/libc/bionic/locale.cpp
+++ b/libc/bionic/locale.cpp
@@ -66,7 +66,7 @@
     }
   }
 
-  DISALLOW_COPY_AND_ASSIGN(__locale_t);
+  BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(__locale_t);
 };
 
 size_t __ctype_get_mb_cur_max() {
diff --git a/libc/bionic/malloc_info.cpp b/libc/bionic/malloc_info.cpp
index 99caedb..9c8a4bf 100644
--- a/libc/bionic/malloc_info.cpp
+++ b/libc/bionic/malloc_info.cpp
@@ -53,7 +53,7 @@
   FILE* fp;
   const char* name;
 
-  DISALLOW_COPY_AND_ASSIGN(Elem);
+  BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(Elem);
 };
 
 int malloc_info(int options, FILE* fp) {
diff --git a/libc/bionic/pthread_atfork.cpp b/libc/bionic/pthread_atfork.cpp
index 84e511c..fb12a3b 100644
--- a/libc/bionic/pthread_atfork.cpp
+++ b/libc/bionic/pthread_atfork.cpp
@@ -107,7 +107,7 @@
   atfork_t* first_;
   atfork_t* last_;
 
-  DISALLOW_COPY_AND_ASSIGN(atfork_list_t);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(atfork_list_t);
 };
 
 static pthread_mutex_t g_atfork_list_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
@@ -180,4 +180,3 @@
   });
   pthread_mutex_unlock(&g_atfork_list_mutex);
 }
-
diff --git a/libc/bionic/pthread_internal.cpp b/libc/bionic/pthread_internal.cpp
index 829194c..92786fe 100644
--- a/libc/bionic/pthread_internal.cpp
+++ b/libc/bionic/pthread_internal.cpp
@@ -54,7 +54,7 @@
 
  private:
   pthread_rwlock_t* rwlock_;
-  DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedRWLock);
+  BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedRWLock);
 };
 
 typedef ScopedRWLock<true> ScopedWriteLock;
diff --git a/libc/bionic/pthread_internal.h b/libc/bionic/pthread_internal.h
index b7173a3..9d55eba 100644
--- a/libc/bionic/pthread_internal.h
+++ b/libc/bionic/pthread_internal.h
@@ -135,10 +135,6 @@
 
   thread_local_dtor* thread_local_dtors;
 
-  void* tls[BIONIC_TLS_SLOTS];
-
-  pthread_key_data_t key_data[BIONIC_PTHREAD_KEY_COUNT];
-
   /*
    * The dynamic linker implements dlerror(3), which makes it hard for us to implement this
    * per-thread buffer by simply using malloc(3) and free(3).
@@ -147,6 +143,12 @@
   char dlerror_buffer[__BIONIC_DLERROR_BUFFER_SIZE];
 
   bionic_tls* bionic_tls;
+
+  pthread_key_data_t key_data[BIONIC_PTHREAD_KEY_COUNT];
+
+  // The thread pointer (__get_tls()) points at this field. This field must come last so that
+  // an executable's TLS segment can be allocated at a fixed offset after the thread pointer.
+  void* tls[BIONIC_TLS_SLOTS];
 };
 
 __LIBC_HIDDEN__ int __init_thread(pthread_internal_t* thread);
diff --git a/libc/bionic/scandir.cpp b/libc/bionic/scandir.cpp
index e55be42..0b39049 100644
--- a/libc/bionic/scandir.cpp
+++ b/libc/bionic/scandir.cpp
@@ -90,7 +90,7 @@
     return copy;
   }
 
-  DISALLOW_COPY_AND_ASSIGN(ScandirResult);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(ScandirResult);
 };
 
 int scandirat(int parent_fd, const char* dir_name, dirent*** name_list,
diff --git a/libc/bionic/system_property_set.cpp b/libc/bionic/system_property_set.cpp
index a70a376..bc3ba76 100644
--- a/libc/bionic/system_property_set.cpp
+++ b/libc/bionic/system_property_set.cpp
@@ -170,7 +170,7 @@
   uint32_t uint_buf_[kUintBufSize];
   size_t uint_buf_index_;
 
-  DISALLOW_IMPLICIT_CONSTRUCTORS(SocketWriter);
+  BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(SocketWriter);
 };
 
 struct prop_msg {
diff --git a/libc/include/android/versioning.h b/libc/include/android/versioning.h
index cab1156..6e4b8ab 100644
--- a/libc/include/android/versioning.h
+++ b/libc/include/android/versioning.h
@@ -17,6 +17,10 @@
 #ifndef ANDROID_VERSIONING_H
 #define ANDROID_VERSIONING_H
 
+#ifndef __STRING
+#define __STRING(x) #x
+#endif
+
 #define __INTRODUCED_IN(api_level) __attribute__((annotate("introduced_in=" __STRING(api_level))))
 #define __INTRODUCED_IN_FUTURE __attribute__((annotate("introduced_in_future")))
 #define __DEPRECATED_IN(api_level) __attribute__((annotate("deprecated_in=" __STRING(api_level))))
diff --git a/libc/include/sys/select.h b/libc/include/sys/select.h
index e58df21..9be444c 100644
--- a/libc/include/sys/select.h
+++ b/libc/include/sys/select.h
@@ -80,7 +80,7 @@
 
 int select(int __fd_count, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, struct timeval* __timeout);
 int pselect(int __fd_count, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, const struct timespec* __timeout, const sigset_t* __mask);
-int pselect64(int __fd_count, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, const struct timespec* __timeout, const sigset64_t* __mask);
+int pselect64(int __fd_count, fd_set* __read_fds, fd_set* __write_fds, fd_set* __exception_fds, const struct timespec* __timeout, const sigset64_t* __mask) __INTRODUCED_IN(28);
 
 __END_DECLS
 
diff --git a/libc/kernel/tools/cpp.py b/libc/kernel/tools/cpp.py
old mode 100644
new mode 100755
index 2400f5d..336a9c8
--- a/libc/kernel/tools/cpp.py
+++ b/libc/kernel/tools/cpp.py
@@ -6,6 +6,7 @@
 import os
 import re
 import site
+import unittest
 import utils
 
 top = os.getenv('ANDROID_BUILD_TOP')
@@ -309,75 +310,50 @@
 
 # Unit testing
 #
-class CppTokenizerTester(object):
-    """A class used to test CppTokenizer classes."""
+class CppTokenizerTests(unittest.TestCase):
+    """CppTokenizer tests."""
 
-    def __init__(self, tokenizer=None):
-        self._tokenizer = tokenizer
-        self._token = None
+    def get_tokens(self, token_string, line_col=False):
+        tokens = CppStringTokenizer(token_string)
+        token_list = []
+        while True:
+            token = tokens.nextToken()
+            if not token:
+                break
+            if line_col:
+                token_list.append((token.id, token.location.line,
+                                   token.location.column))
+            else:
+                token_list.append(token.id)
+        return token_list
 
-    def setTokenizer(self, tokenizer):
-        self._tokenizer = tokenizer
+    def test_hash(self):
+        self.assertEqual(self.get_tokens("#an/example  && (01923_xy)"),
+                         ["#", "an", "/", "example", tokLOGICAND, tokLPAREN,
+                          "01923_xy", tokRPAREN])
 
-    def expect(self, id):
-        self._token = self._tokenizer.nextToken()
-        if self._token is None:
-            tokid = ''
-        else:
-            tokid = self._token.id
-        if tokid == id:
-            return
-        raise BadExpectedToken("###  BAD TOKEN: '%s' expecting '%s'" % (
-            tokid, id))
+    def test_parens(self):
+        self.assertEqual(self.get_tokens("FOO(BAR) && defined(BAZ)"),
+                         ["FOO", tokLPAREN, "BAR", tokRPAREN, tokLOGICAND,
+                          "defined", tokLPAREN, "BAZ", tokRPAREN])
 
-    def expectToken(self, id, line, col):
-        self.expect(id)
-        if self._token.location.line != line:
-            raise BadExpectedToken(
-                "###  BAD LINENO: token '%s' got '%d' expecting '%d'" % (
-                    id, self._token.lineno, line))
-        if self._token.location.column != col:
-            raise BadExpectedToken("###  BAD COLNO: '%d' expecting '%d'" % (
-                self._token.colno, col))
+    def test_comment(self):
+        self.assertEqual(self.get_tokens("/*\n#\n*/"), [])
 
-    def expectTokens(self, tokens):
-        for id, line, col in tokens:
-            self.expectToken(id, line, col)
+    def test_line_cross(self):
+        self.assertEqual(self.get_tokens("first\nsecond"), ["first", "second"])
 
-    def expectList(self, list_):
-        for item in list_:
-            self.expect(item)
+    def test_line_cross_line_col(self):
+        self.assertEqual(self.get_tokens("first second\n  third", True),
+                         [("first", 1, 1), ("second", 1, 7), ("third", 2, 3)])
 
+    def test_comment_line_col(self):
+        self.assertEqual(self.get_tokens("boo /* what the\nhell */", True),
+                         [("boo", 1, 1)])
 
-def test_CppTokenizer():
-    tester = CppTokenizerTester()
-
-    tester.setTokenizer(CppStringTokenizer("#an/example  && (01923_xy)"))
-    tester.expectList(["#", "an", "/", "example", tokLOGICAND, tokLPAREN,
-                       "01923_xy", tokRPAREN])
-
-    tester.setTokenizer(CppStringTokenizer("FOO(BAR) && defined(BAZ)"))
-    tester.expectList(["FOO", tokLPAREN, "BAR", tokRPAREN, tokLOGICAND,
-                       "defined", tokLPAREN, "BAZ", tokRPAREN])
-
-    tester.setTokenizer(CppStringTokenizer("/*\n#\n*/"))
-    tester.expectList([])
-
-    tester.setTokenizer(CppStringTokenizer("first\nsecond"))
-    tester.expectList(["first", "second"])
-
-    tester.setTokenizer(CppStringTokenizer("first second\n  third"))
-    tester.expectTokens([("first", 1, 1),
-                         ("second", 1, 7),
-                         ("third", 2, 3)])
-
-    tester.setTokenizer(CppStringTokenizer("boo /* what the\nhell */"))
-    tester.expectTokens([("boo", 1, 1)])
-
-    tester.setTokenizer(CppStringTokenizer("an \\\n example"))
-    tester.expectTokens([("an", 1, 1),
-                         ("example", 2, 2)])
-    return True
+    def test_escapes(self):
+        self.assertEqual(self.get_tokens("an \\\n example", True),
+                         [("an", 1, 1), ("example", 2, 2)])
 
 
 ################################################################################
@@ -829,134 +805,137 @@
             macros = {}
         self.expr = self.optimize_node(self.expr, macros)
 
+class CppExprTest(unittest.TestCase):
+    """CppExpr unit tests."""
 
-def test_cpp_expr(expr, expected):
-    e = CppExpr(CppStringTokenizer(expr).tokens)
-    s1 = repr(e)
-    if s1 != expected:
-        print ("[FAIL]: expression '%s' generates '%s', should be "
-               "'%s'" % (expr, s1, expected))
-        global failure_count
-        failure_count += 1
+    def get_expr(self, expr):
+        return repr(CppExpr(CppStringTokenizer(expr).tokens))
 
+    def test_cpp_expr(self):
+        self.assertEqual(self.get_expr("0"), "(int 0)")
+        self.assertEqual(self.get_expr("1"), "(int 1)")
+        self.assertEqual(self.get_expr("-5"), "(int -5)")
+        self.assertEqual(self.get_expr("+1"), "(int 1)")
+        self.assertEqual(self.get_expr("0U"), "(int 0)")
+        self.assertEqual(self.get_expr("015"), "(oct 015)")
+        self.assertEqual(self.get_expr("015l"), "(oct 015)")
+        self.assertEqual(self.get_expr("0x3e"), "(hex 0x3e)")
+        self.assertEqual(self.get_expr("(0)"), "(int 0)")
+        self.assertEqual(self.get_expr("1 && 1"), "(&& (int 1) (int 1))")
+        self.assertEqual(self.get_expr("1 && 0"), "(&& (int 1) (int 0))")
+        self.assertEqual(self.get_expr("EXAMPLE"), "(ident EXAMPLE)")
+        self.assertEqual(self.get_expr("EXAMPLE - 3"),
+                         "(- (ident EXAMPLE) (int 3))")
+        self.assertEqual(self.get_expr("defined(EXAMPLE)"),
+                         "(defined EXAMPLE)")
+        self.assertEqual(self.get_expr("defined ( EXAMPLE ) "),
+                         "(defined EXAMPLE)")
+        self.assertEqual(self.get_expr("!defined(EXAMPLE)"),
+                         "(! (defined EXAMPLE))")
+        self.assertEqual(self.get_expr("defined(ABC) || defined(BINGO)"),
+                         "(|| (defined ABC) (defined BINGO))")
+        self.assertEqual(self.get_expr("FOO(BAR,5)"), "(call FOO [BAR,5])")
+        self.assertEqual(self.get_expr("A == 1 || defined(B)"),
+                         "(|| (== (ident A) (int 1)) (defined B))")
 
-def test_cpp_expr_optim(expr, expected, macros=None):
-    if macros is None:
-        macros = {}
-    e = CppExpr(CppStringTokenizer(expr).tokens)
-    e.optimize(macros)
-    s1 = repr(e)
-    if s1 != expected:
-        print ("[FAIL]: optimized expression '%s' generates '%s' with "
-               "macros %s, should be '%s'" % (expr, s1, macros, expected))
-        global failure_count
-        failure_count += 1
+    def get_expr_optimize(self, expr, macros=None):
+        if macros is None:
+            macros = {}
+        e = CppExpr(CppStringTokenizer(expr).tokens)
+        e.optimize(macros)
+        return repr(e)
 
-
-def test_cpp_expr_source(expr, expected):
-    e = CppExpr(CppStringTokenizer(expr).tokens)
-    s1 = str(e)
-    if s1 != expected:
-        print ("[FAIL]: source expression '%s' generates '%s', should "
-               "be '%s'" % (expr, s1, expected))
-        global failure_count
-        failure_count += 1
-
-
-def test_CppExpr():
-    test_cpp_expr("0", "(int 0)")
-    test_cpp_expr("1", "(int 1)")
-    test_cpp_expr("-5", "(int -5)")
-    test_cpp_expr("+1", "(int 1)")
-    test_cpp_expr("0U", "(int 0)")
-    test_cpp_expr("015", "(oct 015)")
-    test_cpp_expr("015l", "(oct 015)")
-    test_cpp_expr("0x3e", "(hex 0x3e)")
-    test_cpp_expr("(0)", "(int 0)")
-    test_cpp_expr("1 && 1", "(&& (int 1) (int 1))")
-    test_cpp_expr("1 && 0", "(&& (int 1) (int 0))")
-    test_cpp_expr("EXAMPLE", "(ident EXAMPLE)")
-    test_cpp_expr("EXAMPLE - 3", "(- (ident EXAMPLE) (int 3))")
-    test_cpp_expr("defined(EXAMPLE)", "(defined EXAMPLE)")
-    test_cpp_expr("defined ( EXAMPLE ) ", "(defined EXAMPLE)")
-    test_cpp_expr("!defined(EXAMPLE)", "(! (defined EXAMPLE))")
-    test_cpp_expr("defined(ABC) || defined(BINGO)",
-                  "(|| (defined ABC) (defined BINGO))")
-    test_cpp_expr("FOO(BAR,5)", "(call FOO [BAR,5])")
-    test_cpp_expr("A == 1 || defined(B)",
-                  "(|| (== (ident A) (int 1)) (defined B))")
-
-    test_cpp_expr_optim("0", "(int 0)")
-    test_cpp_expr_optim("1", "(int 1)")
-    test_cpp_expr_optim("1 && 1", "(int 1)")
-    test_cpp_expr_optim("1 && +1", "(int 1)")
-    test_cpp_expr_optim("0x1 && 01", "(oct 01)")
-    test_cpp_expr_optim("1 && 0", "(int 0)")
-    test_cpp_expr_optim("0 && 1", "(int 0)")
-    test_cpp_expr_optim("0 && 0", "(int 0)")
-    test_cpp_expr_optim("1 || 1", "(int 1)")
-    test_cpp_expr_optim("1 || 0", "(int 1)")
-    test_cpp_expr_optim("0 || 1", "(int 1)")
-    test_cpp_expr_optim("0 || 0", "(int 0)")
-    test_cpp_expr_optim("A", "(ident A)")
-    test_cpp_expr_optim("A", "(int 1)", {"A": 1})
-    test_cpp_expr_optim("A || B", "(int 1)", {"A": 1})
-    test_cpp_expr_optim("A || B", "(int 1)", {"B": 1})
-    test_cpp_expr_optim("A && B", "(ident B)", {"A": 1})
-    test_cpp_expr_optim("A && B", "(ident A)", {"B": 1})
-    test_cpp_expr_optim("A && B", "(&& (ident A) (ident B))")
-    test_cpp_expr_optim("EXAMPLE", "(ident EXAMPLE)")
-    test_cpp_expr_optim("EXAMPLE - 3", "(- (ident EXAMPLE) (int 3))")
-    test_cpp_expr_optim("defined(EXAMPLE)", "(defined EXAMPLE)")
-    test_cpp_expr_optim("defined(EXAMPLE)", "(defined XOWOE)",
-                        {"EXAMPLE": "XOWOE"})
-    test_cpp_expr_optim("defined(EXAMPLE)", "(int 0)",
-                        {"EXAMPLE": kCppUndefinedMacro})
-    test_cpp_expr_optim("!defined(EXAMPLE)", "(! (defined EXAMPLE))")
-    test_cpp_expr_optim("!defined(EXAMPLE)", "(! (defined XOWOE))",
-                        {"EXAMPLE": "XOWOE"})
-    test_cpp_expr_optim("!defined(EXAMPLE)", "(int 1)",
-                        {"EXAMPLE": kCppUndefinedMacro})
-    test_cpp_expr_optim("defined(A) || defined(B)",
+    def test_cpp_expr_optimize(self):
+        self.assertEqual(self.get_expr_optimize("0"), "(int 0)")
+        self.assertEqual(self.get_expr_optimize("1"), "(int 1)")
+        self.assertEqual(self.get_expr_optimize("1 && 1"), "(int 1)")
+        self.assertEqual(self.get_expr_optimize("1 && +1"), "(int 1)")
+        self.assertEqual(self.get_expr_optimize("0x1 && 01"), "(oct 01)")
+        self.assertEqual(self.get_expr_optimize("1 && 0"), "(int 0)")
+        self.assertEqual(self.get_expr_optimize("0 && 1"), "(int 0)")
+        self.assertEqual(self.get_expr_optimize("0 && 0"), "(int 0)")
+        self.assertEqual(self.get_expr_optimize("1 || 1"), "(int 1)")
+        self.assertEqual(self.get_expr_optimize("1 || 0"), "(int 1)")
+        self.assertEqual(self.get_expr_optimize("0 || 1"), "(int 1)")
+        self.assertEqual(self.get_expr_optimize("0 || 0"), "(int 0)")
+        self.assertEqual(self.get_expr_optimize("A"), "(ident A)")
+        self.assertEqual(self.get_expr_optimize("A", {"A": 1}), "(int 1)")
+        self.assertEqual(self.get_expr_optimize("A || B", {"A": 1}), "(int 1)")
+        self.assertEqual(self.get_expr_optimize("A || B", {"B": 1}), "(int 1)")
+        self.assertEqual(self.get_expr_optimize("A && B", {"A": 1}), "(ident B)")
+        self.assertEqual(self.get_expr_optimize("A && B", {"B": 1}), "(ident A)")
+        self.assertEqual(self.get_expr_optimize("A && B"), "(&& (ident A) (ident B))")
+        self.assertEqual(self.get_expr_optimize("EXAMPLE"), "(ident EXAMPLE)")
+        self.assertEqual(self.get_expr_optimize("EXAMPLE - 3"), "(- (ident EXAMPLE) (int 3))")
+        self.assertEqual(self.get_expr_optimize("defined(EXAMPLE)"), "(defined EXAMPLE)")
+        self.assertEqual(self.get_expr_optimize("defined(EXAMPLE)",
+                                                {"EXAMPLE": "XOWOE"}),
+                         "(defined XOWOE)")
+        self.assertEqual(self.get_expr_optimize("defined(EXAMPLE)",
+                                                {"EXAMPLE": kCppUndefinedMacro}),
+                         "(int 0)")
+        self.assertEqual(self.get_expr_optimize("!defined(EXAMPLE)"), "(! (defined EXAMPLE))")
+        self.assertEqual(self.get_expr_optimize("!defined(EXAMPLE)",
+                                                {"EXAMPLE": "XOWOE"}),
+                         "(! (defined XOWOE))")
+        self.assertEqual(self.get_expr_optimize("!defined(EXAMPLE)",
+                                                {"EXAMPLE": kCppUndefinedMacro}),
+                         "(int 1)")
+        self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)"),
                         "(|| (defined A) (defined B))")
-    test_cpp_expr_optim("defined(A) || defined(B)", "(int 1)", {"A": "1"})
-    test_cpp_expr_optim("defined(A) || defined(B)", "(int 1)", {"B": "1"})
-    test_cpp_expr_optim("defined(A) || defined(B)", "(defined A)",
-                        {"B": kCppUndefinedMacro})
-    test_cpp_expr_optim("defined(A) || defined(B)", "(int 0)",
-                        {"A": kCppUndefinedMacro, "B": kCppUndefinedMacro})
-    test_cpp_expr_optim("defined(A) && defined(B)",
-                        "(&& (defined A) (defined B))")
-    test_cpp_expr_optim("defined(A) && defined(B)",
-                        "(defined B)", {"A": "1"})
-    test_cpp_expr_optim("defined(A) && defined(B)",
-                        "(defined A)", {"B": "1"})
-    test_cpp_expr_optim("defined(A) && defined(B)", "(int 0)",
-                        {"B": kCppUndefinedMacro})
-    test_cpp_expr_optim("defined(A) && defined(B)",
-                        "(int 0)", {"A": kCppUndefinedMacro})
-    test_cpp_expr_optim("A == 1 || defined(B)",
-                        "(|| (== (ident A) (int 1)) (defined B))")
-    test_cpp_expr_optim(
-        "defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)",
-        "(|| (! (defined __GLIBC__)) (< (ident __GLIBC__) (int 2)))",
-        {"__KERNEL__": kCppUndefinedMacro})
+        self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)",
+                                                {"A": "1"}),
+                         "(int 1)")
+        self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)",
+                                                {"B": "1"}),
+                         "(int 1)")
+        self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)",
+                                                {"B": kCppUndefinedMacro}),
+                         "(defined A)")
+        self.assertEqual(self.get_expr_optimize("defined(A) || defined(B)",
+                                                {"A": kCppUndefinedMacro,
+                                                 "B": kCppUndefinedMacro}),
+                         "(int 0)")
+        self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)"),
+                         "(&& (defined A) (defined B))")
+        self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)",
+                                                {"A": "1"}),
+                         "(defined B)")
+        self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)",
+                                                {"B": "1"}),
+                         "(defined A)")
+        self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)",
+                                                {"B": kCppUndefinedMacro}),
+                        "(int 0)")
+        self.assertEqual(self.get_expr_optimize("defined(A) && defined(B)",
+                                                {"A": kCppUndefinedMacro}),
+                        "(int 0)")
+        self.assertEqual(self.get_expr_optimize("A == 1 || defined(B)"),
+                         "(|| (== (ident A) (int 1)) (defined B))")
+        self.assertEqual(self.get_expr_optimize(
+              "defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)",
+              {"__KERNEL__": kCppUndefinedMacro}),
+              "(|| (! (defined __GLIBC__)) (< (ident __GLIBC__) (int 2)))")
 
-    test_cpp_expr_source("0", "0")
-    test_cpp_expr_source("1", "1")
-    test_cpp_expr_source("1 && 1", "1 && 1")
-    test_cpp_expr_source("1 && 0", "1 && 0")
-    test_cpp_expr_source("0 && 1", "0 && 1")
-    test_cpp_expr_source("0 && 0", "0 && 0")
-    test_cpp_expr_source("1 || 1", "1 || 1")
-    test_cpp_expr_source("1 || 0", "1 || 0")
-    test_cpp_expr_source("0 || 1", "0 || 1")
-    test_cpp_expr_source("0 || 0", "0 || 0")
-    test_cpp_expr_source("EXAMPLE", "EXAMPLE")
-    test_cpp_expr_source("EXAMPLE - 3", "EXAMPLE - 3")
-    test_cpp_expr_source("defined(EXAMPLE)", "defined(EXAMPLE)")
-    test_cpp_expr_source("defined EXAMPLE", "defined(EXAMPLE)")
-    test_cpp_expr_source("A == 1 || defined(B)", "A == 1 || defined(B)")
+    def get_expr_string(self, expr):
+        return str(CppExpr(CppStringTokenizer(expr).tokens))
+
+    def test_cpp_expr_string(self):
+        self.assertEqual(self.get_expr_string("0"), "0")
+        self.assertEqual(self.get_expr_string("1"), "1")
+        self.assertEqual(self.get_expr_string("1 && 1"), "1 && 1")
+        self.assertEqual(self.get_expr_string("1 && 0"), "1 && 0")
+        self.assertEqual(self.get_expr_string("0 && 1"), "0 && 1")
+        self.assertEqual(self.get_expr_string("0 && 0"), "0 && 0")
+        self.assertEqual(self.get_expr_string("1 || 1"), "1 || 1")
+        self.assertEqual(self.get_expr_string("1 || 0"), "1 || 0")
+        self.assertEqual(self.get_expr_string("0 || 1"), "0 || 1")
+        self.assertEqual(self.get_expr_string("0 || 0"), "0 || 0")
+        self.assertEqual(self.get_expr_string("EXAMPLE"), "EXAMPLE")
+        self.assertEqual(self.get_expr_string("EXAMPLE - 3"), "EXAMPLE - 3")
+        self.assertEqual(self.get_expr_string("defined(EXAMPLE)"), "defined(EXAMPLE)")
+        self.assertEqual(self.get_expr_string("defined EXAMPLE"), "defined(EXAMPLE)")
+        self.assertEqual(self.get_expr_string("A == 1 || defined(B)"), "A == 1 || defined(B)")
 
 
 ################################################################################
@@ -1605,31 +1584,30 @@
         return self.getBlocks(CppFileTokenizer(path))
 
 
-def test_block_parsing(lines, expected):
-    """Helper method to test the correctness of BlockParser.parse."""
-    blocks = BlockParser().parse(CppStringTokenizer('\n'.join(lines)))
-    if len(blocks) != len(expected):
-        raise BadExpectedToken("BlockParser.parse() returned '%s' expecting "
-                               "'%s'" % (str(blocks), repr(expected)))
-    for n in range(len(blocks)):
-        if str(blocks[n]) != expected[n]:
-            raise BadExpectedToken("BlockParser.parse()[%d] is '%s', "
-                                   "expecting '%s'" % (n, str(blocks[n]),
-                                                       expected[n]))
+class BlockParserTests(unittest.TestCase):
+    """BlockParser unit tests."""
 
+    def get_blocks(self, lines):
+        blocks = BlockParser().parse(CppStringTokenizer('\n'.join(lines)))
+        return map(lambda a: str(a), blocks)
 
-def test_BlockParser():
-    test_block_parsing(["#error hello"], ["#error hello"])
-    test_block_parsing(["foo", "", "bar"], ["foo bar"])
+    def test_hash(self):
+        self.assertEqual(self.get_blocks(["#error hello"]), ["#error hello"])
 
-    # We currently cannot handle the following case with libclang properly.
-    # Fortunately it doesn't appear in current headers.
-    # test_block_parsing(["foo", "  #  ", "bar"], ["foo", "bar"])
+    def test_empty_line(self):
+        self.assertEqual(self.get_blocks(["foo", "", "bar"]), ["foo bar"])
 
-    test_block_parsing(["foo",
-                        "  #  /* ahah */ if defined(__KERNEL__) /* more */",
-                        "bar", "#endif"],
-                       ["foo", "#ifdef __KERNEL__", "bar", "#endif"])
+    def test_hash_with_space(self):
+        # We currently cannot handle the following case with libclang properly.
+        # Fortunately it doesn't appear in current headers.
+        #self.assertEqual(self.get_blocks(["foo", "  #  ", "bar"]), ["foo", "bar"])
+        pass
+
+    def test_with_comment(self):
+        self.assertEqual(self.get_blocks(["foo",
+                                          "  #  /* ahah */ if defined(__KERNEL__) /* more */",
+                                          "bar", "#endif"]),
+                         ["foo", "#ifdef __KERNEL__", "bar", "#endif"])
 
 
 ################################################################################
@@ -1684,6 +1662,7 @@
 
         if r == 0:
             # if 0 => skip everything until the corresponding #endif
+            start_dir = blocks[j].directive
             j = find_matching_endif(blocks, j + 1)
             if j >= n:
                 # unterminated #if 0, finish here
@@ -1692,18 +1671,27 @@
             if dir_ == "endif":
                 logging.debug("remove 'if 0' .. 'endif' (lines %d to %d)",
                               blocks[i].lineno, blocks[j].lineno)
+                if start_dir == "elif":
+                    # Put an endif since we started with an elif.
+                    result += blocks[j:j+1]
                 i = j + 1
             elif dir_ == "else":
                 # convert 'else' into 'if 1'
                 logging.debug("convert 'if 0' .. 'else' into 'if 1' (lines %d "
                               "to %d)", blocks[i].lineno, blocks[j-1].lineno)
-                blocks[j].directive = "if"
+                if start_dir == "elif":
+                    blocks[j].directive = "elif"
+                else:
+                    blocks[j].directive = "if"
                 blocks[j].expr = CppExpr(CppStringTokenizer("1").tokens)
                 i = j
             elif dir_ == "elif":
                 # convert 'elif' into 'if'
                 logging.debug("convert 'if 0' .. 'elif' into 'if'")
-                blocks[j].directive = "if"
+                if start_dir == "elif":
+                    blocks[j].directive = "elif"
+                else:
+                    blocks[j].directive = "if"
                 i = j
             continue
 
@@ -1715,18 +1703,33 @@
             result += blocks[j+1:k]
             break
 
+        start_dir = blocks[j].directive
         dir_ = blocks[k].directive
         if dir_ == "endif":
             logging.debug("convert 'if 1' .. 'endif' (lines %d to %d)",
                           blocks[j].lineno, blocks[k].lineno)
+            if start_dir == "elif":
+                # Add the elif in to the results and convert it to an elif 1.
+                blocks[j].tokens = CppStringTokenizer("1").tokens
+                result += blocks[j:j+1]
             result += optimize_if01(blocks[j+1:k])
+            if start_dir == "elif":
+                # Add the endif in to the results.
+                result += blocks[k:k+1]
             i = k + 1
         elif dir_ == "else":
             # convert 'else' into 'if 0'
             logging.debug("convert 'if 1' .. 'else' (lines %d to %d)",
                           blocks[j].lineno, blocks[k].lineno)
+            if start_dir == "elif":
+                # Add the elif in to the results and convert it to an elif 1.
+                blocks[j].tokens = CppStringTokenizer("1").tokens
+                result += blocks[j:j+1]
             result += optimize_if01(blocks[j+1:k])
-            blocks[k].directive = "if"
+            if start_dir == "elif":
+                blocks[k].directive = "elif"
+            else:
+                blocks[k].directive = "if"
             blocks[k].expr = CppExpr(CppStringTokenizer("0").tokens)
             i = k
         elif dir_ == "elif":
@@ -1738,85 +1741,203 @@
             i = k
     return result
 
+class OptimizerTests(unittest.TestCase):
+    def parse(self, text, macros=None):
+        out = utils.StringOutput()
+        blocks = BlockParser().parse(CppStringTokenizer(text))
+        blocks.replaceTokens(kernel_token_replacements)
+        blocks.optimizeAll(macros)
+        blocks.write(out)
+        return out.get()
 
-def test_optimizeAll():
-    text = """\
+    def test_if1(self):
+        text = """\
 #if 1
-#define  GOOD_1
+#define  GOOD
 #endif
-#if 0
-#define  BAD_2
-#define  BAD_3
-#endif
+"""
+        expected = """\
+#define GOOD
+"""
+        self.assertEqual(self.parse(text), expected)
 
+    def test_if0(self):
+        text = """\
+#if 0
+#define  SHOULD_SKIP1
+#define  SHOULD_SKIP2
+#endif
+"""
+        expected = ""
+        self.assertEqual(self.parse(text), expected)
+
+    def test_if1_else(self):
+        text = """\
 #if 1
-#define  GOOD_2
+#define  GOOD
 #else
-#define  BAD_4
+#define  BAD
 #endif
+"""
+        expected = """\
+#define GOOD
+"""
+        self.assertEqual(self.parse(text), expected)
 
+    def test_if0_else(self):
+        text = """\
 #if 0
-#define  BAD_5
+#define  BAD
 #else
-#define  GOOD_3
+#define  GOOD
 #endif
+"""
+        expected = """\
+#define GOOD
+"""
+        self.assertEqual(self.parse(text), expected)
 
+    def test_if_elif1(self):
+        text = """\
+#if defined(something)
+#define EXISTS
+#elif 1
+#define GOOD
+#endif
+"""
+        expected = """\
+#ifdef something
+#define EXISTS
+#elif 1
+#define GOOD
+#endif
+"""
+        self.assertEqual(self.parse(text), expected)
+
+    def test_if_elif1_macro(self):
+        text = """\
+#if defined(something)
+#define EXISTS
+#elif defined(WILL_BE_ONE)
+#define GOOD
+#endif
+"""
+        expected = """\
+#ifdef something
+#define EXISTS
+#elif 1
+#define GOOD
+#endif
+"""
+        self.assertEqual(self.parse(text, {"WILL_BE_ONE": "1"}), expected)
+
+
+    def test_if_elif1_else(self):
+        text = """\
+#if defined(something)
+#define EXISTS
+#elif 1
+#define GOOD
+#else
+#define BAD
+#endif
+"""
+        expected = """\
+#ifdef something
+#define EXISTS
+#elif 1
+#define GOOD
+#endif
+"""
+        self.assertEqual(self.parse(text), expected)
+
+    def test_if_elif1_else_macro(self):
+        text = """\
+#if defined(something)
+#define EXISTS
+#elif defined(WILL_BE_ONE)
+#define GOOD
+#else
+#define BAD
+#endif
+"""
+        expected = """\
+#ifdef something
+#define EXISTS
+#elif 1
+#define GOOD
+#endif
+"""
+        self.assertEqual(self.parse(text, {"WILL_BE_ONE": "1"}), expected)
+
+
+    def test_if_elif1_else_macro(self):
+        text = """\
+#if defined(something)
+#define EXISTS
+#elif defined(WILL_BE_ONE)
+#define GOOD
+#else
+#define BAD
+#endif
+"""
+        expected = """\
+#ifdef something
+#define EXISTS
+#elif 1
+#define GOOD
+#endif
+"""
+        self.assertEqual(self.parse(text, {"WILL_BE_ONE": "1"}), expected)
+
+    def test_macro_set_to_undefined_single(self):
+        text = """\
 #if defined(__KERNEL__)
 #define BAD_KERNEL
 #endif
+"""
+        expected = ""
+        macros = {"__KERNEL__": kCppUndefinedMacro}
+        self.assertEqual(self.parse(text, macros), expected)
 
+    def test_macro_set_to_undefined_if(self):
+        text = """\
 #if defined(__KERNEL__) || !defined(__GLIBC__) || (__GLIBC__ < 2)
-#define X
+#define CHECK
 #endif
+"""
+        expected = """\
+#if !defined(__GLIBC__) || __GLIBC__ < 2
+#define CHECK
+#endif
+"""
+        macros = {"__KERNEL__": kCppUndefinedMacro}
+        self.assertEqual(self.parse(text, macros), expected)
 
+    def test_endif_comment_removed(self):
+        text = """\
 #ifndef SIGRTMAX
 #define SIGRTMAX 123
 #endif /* SIGRTMAX */
-
-#if 0
-#if 1
-#define  BAD_6
-#endif
-#endif\
 """
-
-    expected = """\
-#define GOOD_1
-#define GOOD_2
-#define GOOD_3
-#if !defined(__GLIBC__) || __GLIBC__ < 2
-#define X
-#endif
+        expected = """\
 #ifndef __SIGRTMAX
 #define __SIGRTMAX 123
 #endif
 """
+        self.assertEqual(self.parse(text), expected)
 
-    out = utils.StringOutput()
-    blocks = BlockParser().parse(CppStringTokenizer(text))
-    blocks.replaceTokens(kernel_token_replacements)
-    blocks.optimizeAll({"__KERNEL__": kCppUndefinedMacro})
-    blocks.write(out)
-    if out.get() != expected:
-        print "[FAIL]: macro optimization failed\n"
-        print "<<<< expecting '",
-        print expected,
-        print "'\n>>>> result '",
-        print out.get(),
-        print "'\n----"
-        global failure_count
-        failure_count += 1
+    def test_multilevel_if0(self):
+        text = """\
+#if 0
+#if 1
+#define  BAD_6
+#endif
+#endif
+"""
+        expected = ""
+        self.assertEqual(self.parse(text), expected)
 
 
-def runUnitTests():
-    """Always run all unit tests for this program."""
-    test_CppTokenizer()
-    test_CppExpr()
-    test_optimizeAll()
-    test_BlockParser()
-
-
-failure_count = 0
-runUnitTests()
-if failure_count != 0:
-    utils.panic("Unit tests failed in cpp.py.\n")
+if __name__ == '__main__':
+    unittest.main()
diff --git a/libc/malloc_debug/DebugData.h b/libc/malloc_debug/DebugData.h
index f7cf8ab..3a36299 100644
--- a/libc/malloc_debug/DebugData.h
+++ b/libc/malloc_debug/DebugData.h
@@ -94,7 +94,7 @@
 
   Config config_;
 
-  DISALLOW_COPY_AND_ASSIGN(DebugData);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(DebugData);
 };
 
 extern DebugData* g_debug;
diff --git a/libc/malloc_debug/GuardData.h b/libc/malloc_debug/GuardData.h
index 7b21671..b6ec889 100644
--- a/libc/malloc_debug/GuardData.h
+++ b/libc/malloc_debug/GuardData.h
@@ -56,7 +56,7 @@
 
   virtual const char* GetTypeName() = 0;
 
-  DISALLOW_COPY_AND_ASSIGN(GuardData);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(GuardData);
 };
 
 class FrontGuardData : public GuardData {
@@ -75,7 +75,7 @@
 
   size_t offset_ = 0;
 
-  DISALLOW_COPY_AND_ASSIGN(FrontGuardData);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(FrontGuardData);
 };
 
 class RearGuardData : public GuardData {
@@ -90,5 +90,5 @@
  private:
   const char* GetTypeName() override { return "REAR"; }
 
-  DISALLOW_COPY_AND_ASSIGN(RearGuardData);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(RearGuardData);
 };
diff --git a/libc/malloc_debug/MapData.h b/libc/malloc_debug/MapData.h
index d8398bd..b9b697c 100644
--- a/libc/malloc_debug/MapData.h
+++ b/libc/malloc_debug/MapData.h
@@ -68,5 +68,5 @@
   std::mutex m_;
   std::set<MapEntry*, compare_entries> entries_;
 
-  DISALLOW_COPY_AND_ASSIGN(MapData);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(MapData);
 };
diff --git a/libc/malloc_debug/OptionData.h b/libc/malloc_debug/OptionData.h
index 3fa8e14..8fb13a4 100644
--- a/libc/malloc_debug/OptionData.h
+++ b/libc/malloc_debug/OptionData.h
@@ -39,5 +39,5 @@
  protected:
   DebugData* debug_;
 
-  DISALLOW_COPY_AND_ASSIGN(OptionData);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(OptionData);
 };
diff --git a/libc/malloc_debug/PointerData.h b/libc/malloc_debug/PointerData.h
index 62d4186..b05a763 100644
--- a/libc/malloc_debug/PointerData.h
+++ b/libc/malloc_debug/PointerData.h
@@ -184,5 +184,5 @@
   static std::mutex free_pointer_mutex_;
   static std::deque<FreePointerInfoType> free_pointers_;
 
-  DISALLOW_COPY_AND_ASSIGN(PointerData);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(PointerData);
 };
diff --git a/libc/malloc_debug/RecordData.h b/libc/malloc_debug/RecordData.h
index 3e5ca02..a015882 100644
--- a/libc/malloc_debug/RecordData.h
+++ b/libc/malloc_debug/RecordData.h
@@ -49,7 +49,7 @@
   pid_t tid_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(RecordEntry);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(RecordEntry);
 };
 
 class ThreadCompleteEntry : public RecordEntry {
@@ -60,7 +60,7 @@
   std::string GetString() const override;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(ThreadCompleteEntry);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(ThreadCompleteEntry);
 };
 
 class AllocEntry : public RecordEntry {
@@ -72,7 +72,7 @@
   void* pointer_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(AllocEntry);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(AllocEntry);
 };
 
 class MallocEntry : public AllocEntry {
@@ -86,7 +86,7 @@
   size_t size_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(MallocEntry);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(MallocEntry);
 };
 
 class FreeEntry : public AllocEntry {
@@ -97,7 +97,7 @@
   std::string GetString() const override;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(FreeEntry);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(FreeEntry);
 };
 
 class CallocEntry : public MallocEntry {
@@ -111,7 +111,7 @@
   size_t nmemb_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(CallocEntry);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(CallocEntry);
 };
 
 class ReallocEntry : public MallocEntry {
@@ -125,7 +125,7 @@
   void* old_pointer_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(ReallocEntry);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(ReallocEntry);
 };
 
 // aligned_alloc, posix_memalign, memalign, pvalloc, valloc all recorded with this class.
@@ -140,7 +140,7 @@
   size_t alignment_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(MemalignEntry);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(MemalignEntry);
 };
 
 class Config;
@@ -170,5 +170,5 @@
   std::atomic_bool dump_;
   std::string dump_file_;
 
-  DISALLOW_COPY_AND_ASSIGN(RecordData);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(RecordData);
 };
diff --git a/libc/malloc_debug/debug_disable.h b/libc/malloc_debug/debug_disable.h
index 0049595..f9c3149 100644
--- a/libc/malloc_debug/debug_disable.h
+++ b/libc/malloc_debug/debug_disable.h
@@ -57,5 +57,5 @@
  private:
   bool disabled_;
 
-  DISALLOW_COPY_AND_ASSIGN(ScopedDisableDebugCalls);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(ScopedDisableDebugCalls);
 };
diff --git a/libc/private/ErrnoRestorer.h b/libc/private/ErrnoRestorer.h
index f467393..52e115a 100644
--- a/libc/private/ErrnoRestorer.h
+++ b/libc/private/ErrnoRestorer.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ERRNO_RESTORER_H
-#define ERRNO_RESTORER_H
+#pragma once
 
 #include <errno.h>
 
@@ -37,7 +36,5 @@
  private:
   int saved_errno_;
 
-  DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(ErrnoRestorer);
 };
-
-#endif // ERRNO_RESTORER_H
diff --git a/libc/private/KernelArgumentBlock.h b/libc/private/KernelArgumentBlock.h
index e05ceb9..886dd32 100644
--- a/libc/private/KernelArgumentBlock.h
+++ b/libc/private/KernelArgumentBlock.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef KERNEL_ARGUMENT_BLOCK_H
-#define KERNEL_ARGUMENT_BLOCK_H
+#pragma once
 
 #include <elf.h>
 #include <link.h>
@@ -71,7 +70,5 @@
   libc_shared_globals* shared_globals;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(KernelArgumentBlock);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(KernelArgumentBlock);
 };
-
-#endif // KERNEL_ARGUMENT_BLOCK_H
diff --git a/libc/private/ScopedPthreadMutexLocker.h b/libc/private/ScopedPthreadMutexLocker.h
index 58462e3..1c1e4a7 100644
--- a/libc/private/ScopedPthreadMutexLocker.h
+++ b/libc/private/ScopedPthreadMutexLocker.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef SCOPED_PTHREAD_MUTEX_LOCKER_H
-#define SCOPED_PTHREAD_MUTEX_LOCKER_H
+#pragma once
 
 #include <pthread.h>
 
@@ -34,7 +33,5 @@
  private:
   pthread_mutex_t* mu_;
 
-  DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedPthreadMutexLocker);
+  BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(ScopedPthreadMutexLocker);
 };
-
-#endif // SCOPED_PTHREAD_MUTEX_LOCKER_H
diff --git a/libc/private/ScopedReaddir.h b/libc/private/ScopedReaddir.h
index 1a59e1a..dc22309 100644
--- a/libc/private/ScopedReaddir.h
+++ b/libc/private/ScopedReaddir.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef SCOPED_READDIR_H
-#define SCOPED_READDIR_H
+#pragma once
 
 #include <dirent.h>
 
@@ -47,7 +46,5 @@
  private:
   DIR* dir_;
 
-  DISALLOW_COPY_AND_ASSIGN(ScopedReaddir);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(ScopedReaddir);
 };
-
-#endif // SCOPED_READDIR_H
diff --git a/libc/private/ScopedSignalBlocker.h b/libc/private/ScopedSignalBlocker.h
index d1cf629..10aacb3 100644
--- a/libc/private/ScopedSignalBlocker.h
+++ b/libc/private/ScopedSignalBlocker.h
@@ -46,5 +46,5 @@
 
   sigset64_t old_set_;
 
-  DISALLOW_COPY_AND_ASSIGN(ScopedSignalBlocker);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(ScopedSignalBlocker);
 };
diff --git a/libc/private/WriteProtected.h b/libc/private/WriteProtected.h
index 7a6b098..69a6822 100644
--- a/libc/private/WriteProtected.h
+++ b/libc/private/WriteProtected.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _PRIVATE_WRITEPROTECTED_H
-#define _PRIVATE_WRITEPROTECTED_H
+#pragma once
 
 #include <errno.h>
 #include <string.h>
@@ -33,7 +32,7 @@
   char padding[PAGE_SIZE];
 
   WriteProtectedContents() = default;
-  DISALLOW_COPY_AND_ASSIGN(WriteProtectedContents);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(WriteProtectedContents);
 } __attribute__((aligned(PAGE_SIZE)));
 
 // Write protected wrapper class that aligns its contents to a page boundary,
@@ -49,7 +48,7 @@
 
  public:
   WriteProtected() = default;
-  DISALLOW_COPY_AND_ASSIGN(WriteProtected);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(WriteProtected);
 
   void initialize() {
     // Not strictly necessary, but this will hopefully segfault if we initialize
@@ -82,5 +81,3 @@
     }
   }
 };
-
-#endif
diff --git a/libc/private/bionic_lock.h b/libc/private/bionic_lock.h
index 54168d3..eebfeff 100644
--- a/libc/private/bionic_lock.h
+++ b/libc/private/bionic_lock.h
@@ -25,8 +25,8 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#ifndef _BIONIC_LOCK_H
-#define _BIONIC_LOCK_H
+
+#pragma once
 
 #include <stdatomic.h>
 #include "private/bionic_futex.h"
@@ -85,10 +85,8 @@
     lock_.unlock();
   }
 
-  DISALLOW_COPY_AND_ASSIGN(LockGuard);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(LockGuard);
 
  private:
   Lock& lock_;
 };
-
-#endif  // _BIONIC_LOCK_H
diff --git a/libc/private/bionic_macros.h b/libc/private/bionic_macros.h
index 0a36cdb..4800e3a 100644
--- a/libc/private/bionic_macros.h
+++ b/libc/private/bionic_macros.h
@@ -14,31 +14,17 @@
  * limitations under the License.
  */
 
-#ifndef _BIONIC_MACROS_H_
-#define _BIONIC_MACROS_H_
+#pragma once
 
 #include <stdint.h>
 
-// Frameworks OpenGL code currently leaks this header and allows
-// collisions with other declarations, e.g., from libnativehelper.
-// TODO: Remove once cleaned up. b/18334516
-#if !defined(DISALLOW_COPY_AND_ASSIGN)
-// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions.
-// It goes in the private: declarations in a class.
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
-  TypeName(const TypeName&) = delete;      \
+#define BIONIC_DISALLOW_COPY_AND_ASSIGN(TypeName) \
+  TypeName(const TypeName&) = delete;             \
   void operator=(const TypeName&) = delete
-#endif  // !defined(DISALLOW_COPY_AND_ASSIGN)
 
-// A macro to disallow all the implicit constructors, namely the
-// default constructor, copy constructor and operator= functions.
-//
-// This should be used in the private: declarations for a class
-// that wants to prevent anyone from instantiating it. This is
-// especially useful for classes containing only static methods.
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
-  TypeName() = delete;                           \
-  DISALLOW_COPY_AND_ASSIGN(TypeName)
+#define BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+  TypeName() = delete;                                  \
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(TypeName)
 
 #define BIONIC_ROUND_UP_POWER_OF_2(value) \
   ((sizeof(value) == 8) \
@@ -101,5 +87,3 @@
 #else
 #define __BIONIC_FALLTHROUGH
 #endif
-
-#endif // _BIONIC_MACROS_H_
diff --git a/libc/private/bionic_systrace.h b/libc/private/bionic_systrace.h
index 304fb80..86d2a08 100644
--- a/libc/private/bionic_systrace.h
+++ b/libc/private/bionic_systrace.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef BIONIC_SYSTRACE_H
-#define BIONIC_SYSTRACE_H
+#pragma once
 
 #include "bionic_macros.h"
 
@@ -31,10 +30,8 @@
   void End();
  private:
   bool called_end_;
-  DISALLOW_COPY_AND_ASSIGN(ScopedTrace);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(ScopedTrace);
 };
 
 void bionic_trace_begin(const char* message);
 void bionic_trace_end();
-
-#endif
diff --git a/libc/system_properties/include/system_properties/context_node.h b/libc/system_properties/include/system_properties/context_node.h
index 35d0e92..20f4013 100644
--- a/libc/system_properties/include/system_properties/context_node.h
+++ b/libc/system_properties/include/system_properties/context_node.h
@@ -42,7 +42,7 @@
     Unmap();
   }
 
-  DISALLOW_COPY_AND_ASSIGN(ContextNode);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(ContextNode);
 
   bool Open(bool access_rw, bool* fsetxattr_failed);
   bool CheckAccessAndOpen();
diff --git a/libc/system_properties/include/system_properties/prop_area.h b/libc/system_properties/include/system_properties/prop_area.h
index 2c25337..a69f90e 100644
--- a/libc/system_properties/include/system_properties/prop_area.h
+++ b/libc/system_properties/include/system_properties/prop_area.h
@@ -86,7 +86,7 @@
   }
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(prop_bt);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(prop_bt);
 };
 
 class prop_area {
@@ -158,5 +158,5 @@
   uint32_t reserved_[28];
   char data_[0];
 
-  DISALLOW_COPY_AND_ASSIGN(prop_area);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(prop_area);
 };
diff --git a/libc/system_properties/include/system_properties/prop_info.h b/libc/system_properties/include/system_properties/prop_info.h
index a127550..27b29c8 100644
--- a/libc/system_properties/include/system_properties/prop_info.h
+++ b/libc/system_properties/include/system_properties/prop_info.h
@@ -83,7 +83,7 @@
   prop_info(const char* name, uint32_t namelen, uint32_t long_offset);
 
  private:
-  DISALLOW_IMPLICIT_CONSTRUCTORS(prop_info);
+  BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(prop_info);
 };
 
 static_assert(sizeof(prop_info) == 96, "sizeof struct prop_info must be 96 bytes");
diff --git a/libc/system_properties/include/system_properties/system_properties.h b/libc/system_properties/include/system_properties/system_properties.h
index 52ffcaf..cad29cc 100644
--- a/libc/system_properties/include/system_properties/system_properties.h
+++ b/libc/system_properties/include/system_properties/system_properties.h
@@ -52,7 +52,7 @@
   explicit SystemProperties(bool initialized) : initialized_(initialized) {
   }
 
-  DISALLOW_COPY_AND_ASSIGN(SystemProperties);
+  BIONIC_DISALLOW_COPY_AND_ASSIGN(SystemProperties);
 
   bool Init(const char* filename);
   bool AreaInit(const char* filename, bool* fsetxattr_failed);
diff --git a/linker/Android.bp b/linker/Android.bp
index 697c260..779cd3f 100644
--- a/linker/Android.bp
+++ b/linker/Android.bp
@@ -15,7 +15,7 @@
     // We need to access Bionic private headers in the linker.
     include_dirs: ["bionic/libc"],
 
-    static_libs: ["libasync_safe"],
+    static_libs: ["libasync_safe", "libbase"],
 }
 
 // This is used for bionic on (host) Linux to bootstrap our linker embedded into
diff --git a/linker/linked_list.h b/linker/linked_list.h
index 7f70a2c..5473ca0 100644
--- a/linker/linked_list.h
+++ b/linker/linked_list.h
@@ -28,7 +28,7 @@
 
 #pragma once
 
-#include "private/bionic_macros.h"
+#include <android-base/macros.h>
 
 template<typename T>
 struct LinkedListEntry {
diff --git a/linker/linker.cpp b/linker/linker.cpp
index f085863..e866c3d 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1086,6 +1086,7 @@
     fd = open_library_on_paths(zip_archive_cache, name, file_offset, needed_by->get_dt_runpath(), realpath);
     // Check if the library is accessible
     if (fd != -1 && !ns->is_accessible(*realpath)) {
+      close(fd);
       fd = -1;
     }
   }
diff --git a/linker/linker_block_allocator.h b/linker/linker_block_allocator.h
index b501659..bd44fc8 100644
--- a/linker/linker_block_allocator.h
+++ b/linker/linker_block_allocator.h
@@ -30,7 +30,8 @@
 
 #include <stdlib.h>
 #include <limits.h>
-#include "private/bionic_macros.h"
+
+#include <android-base/macros.h>
 
 struct LinkerBlockAllocatorPage;
 
diff --git a/linker/linker_common_types.h b/linker/linker_common_types.h
index ffa4066..ae78aa9 100644
--- a/linker/linker_common_types.h
+++ b/linker/linker_common_types.h
@@ -31,6 +31,8 @@
 #include <android/dlext.h>
 #include "linked_list.h"
 
+#include <android-base/macros.h>
+
 // TODO(dimitry): move this to linker_defines.h? Unless it is removed by
 // consequent refactoring steps.
 
diff --git a/linker/linker_config.h b/linker/linker_config.h
index e117aea..24c44f4 100644
--- a/linker/linker_config.h
+++ b/linker/linker_config.h
@@ -32,13 +32,14 @@
 
 #include <stdlib.h>
 #include <limits.h>
-#include "private/bionic_macros.h"
 
 #include <memory>
 #include <string>
 #include <vector>
 #include <unordered_map>
 
+#include <android-base/macros.h>
+
 class NamespaceLinkConfig {
  public:
   NamespaceLinkConfig() = default;
diff --git a/linker/linker_logger.h b/linker/linker_logger.h
index 9ce438e..1828799 100644
--- a/linker/linker_logger.h
+++ b/linker/linker_logger.h
@@ -30,9 +30,11 @@
 
 #include <stdlib.h>
 #include <limits.h>
-#include "private/bionic_macros.h"
+
 #include "private/bionic_systrace.h"
 
+#include <android-base/macros.h>
+
 #define LD_LOG(type, x...) \
   { \
     g_linker_logger.Log(type, x); \
diff --git a/linker/linker_mapped_file_fragment.h b/linker/linker_mapped_file_fragment.h
index f7872bd..91e094f 100644
--- a/linker/linker_mapped_file_fragment.h
+++ b/linker/linker_mapped_file_fragment.h
@@ -30,7 +30,7 @@
 
 #include <unistd.h>
 
-#include "private/bionic_macros.h"
+#include <android-base/macros.h>
 
 class MappedFileFragment {
  public:
diff --git a/tests/TemporaryFile.h b/tests/TemporaryFile.h
index 8af92d4..7853781 100644
--- a/tests/TemporaryFile.h
+++ b/tests/TemporaryFile.h
@@ -17,7 +17,7 @@
 #include <fcntl.h>
 #include <unistd.h>
 
-#include "private/bionic_macros.h"
+#include <android-base/macros.h>
 
 template <typename T = int (*)(char*)>
 class GenericTemporaryFile {
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index e68f1ff..84ce531 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -34,12 +34,12 @@
 #include <future>
 #include <vector>
 
+#include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/scopeguard.h>
 #include <android-base/strings.h>
 
 #include "private/bionic_constants.h"
-#include "private/bionic_macros.h"
 #include "BionicDeathTest.h"
 #include "SignalUtils.h"
 #include "utils.h"
diff --git a/tools/versioner/platforms/crtbegin.map.txt b/tools/versioner/platforms/crtbegin.map.txt
new file mode 100644
index 0000000..2c08da6
--- /dev/null
+++ b/tools/versioner/platforms/crtbegin.map.txt
@@ -0,0 +1,7 @@
+# This file lists the libc functions are included in `crtbegin.o` and not
+# exported by `libc.so`.
+
+CRTBEGIN {
+  global:
+    atexit;  # arm64 x86 x86_64 mips mips64
+};
diff --git a/tools/versioner/platforms/libc.map.txt b/tools/versioner/platforms/libc.map.txt
new file mode 120000
index 0000000..8527b2e
--- /dev/null
+++ b/tools/versioner/platforms/libc.map.txt
@@ -0,0 +1 @@
+../../../libc/libc.map.txt
\ No newline at end of file
diff --git a/tools/versioner/src/Android.bp b/tools/versioner/src/Android.bp
index f138cc3..c3721ef 100644
--- a/tools/versioner/src/Android.bp
+++ b/tools/versioner/src/Android.bp
@@ -14,6 +14,7 @@
         "Driver.cpp",
         "Preprocessor.cpp",
         "SymbolDatabase.cpp",
+        "SymbolFileParser.cpp",
         "Utils.cpp",
         "VFS.cpp",
     ],
diff --git a/tools/versioner/src/Arch.cpp b/tools/versioner/src/Arch.cpp
index 8d1d41e..4cd9e07 100644
--- a/tools/versioner/src/Arch.cpp
+++ b/tools/versioner/src/Arch.cpp
@@ -44,20 +44,19 @@
   errx(1, "unknown arch '%zu'", size_t(arch));
 }
 
-Arch arch_from_string(const std::string& name) {
-  if (name == "arm") {
-    return Arch::arm;
-  } else if (name == "arm64") {
-    return Arch::arm64;
-  } else if (name == "mips") {
-    return Arch::mips;
-  } else if (name == "mips64") {
-    return Arch::mips64;
-  } else if (name == "x86") {
-    return Arch::x86;
-  } else if (name == "x86_64") {
-    return Arch::x86_64;
-  }
+static const std::unordered_map<std::string, Arch> arch_name_map{
+  {"arm", Arch::arm},
+  {"arm64", Arch::arm64},
+  {"mips", Arch::mips},
+  {"mips64", Arch::mips64},
+  {"x86", Arch::x86},
+  {"x86_64", Arch::x86_64},
+};
 
-  errx(1, "unknown architecture '%s'", name.c_str());
+std::optional<Arch> arch_from_string(const std::string& name) {
+  auto it = arch_name_map.find(name);
+  if (it == arch_name_map.end()) {
+    return std::nullopt;
+  }
+  return std::make_optional(it->second);
 }
diff --git a/tools/versioner/src/Arch.h b/tools/versioner/src/Arch.h
index ab05d5c..bac9ec4 100644
--- a/tools/versioner/src/Arch.h
+++ b/tools/versioner/src/Arch.h
@@ -20,8 +20,10 @@
 
 #include <array>
 #include <initializer_list>
+#include <optional>
 #include <set>
 #include <string>
+#include <unordered_map>
 
 enum class Arch : size_t {
   arm = 0,
@@ -33,7 +35,7 @@
 };
 
 std::string to_string(const Arch& arch);
-Arch arch_from_string(const std::string& name);
+std::optional<Arch> arch_from_string(const std::string& name);
 
 template <typename T>
 class ArchMapIterator;
@@ -136,7 +138,7 @@
   { Arch::x86_64, "x86_64-linux-android" },
 };
 
-static const std::set<int> default_levels = { 14, 15, 16, 17, 18, 19, 21, 23, 24, 25, 26, 27 };
+static const std::set<int> default_levels = { 14, 15, 16, 17, 18, 19, 21, 23, 24, 25, 26, 27, 28 };
 
 static const ArchMap<int> arch_min_api = {
   { Arch::arm, 9 },
@@ -146,3 +148,23 @@
   { Arch::x86, 9 },
   { Arch::x86_64, 21 },
 };
+
+static constexpr int future_api = 10000;
+
+static const std::unordered_map<std::string, int> api_codename_map{
+  {"G", 9},
+  {"I", 14},
+  {"J", 16},
+  {"J-MR1", 17},
+  {"J-MR2", 18},
+  {"K", 19},
+  {"L", 21},
+  {"L-MR1", 22},
+  {"M", 23},
+  {"N", 24},
+  {"N-MR1", 25},
+  {"O", 26},
+  {"O-MR1", 27},
+  {"P", 28},
+  {"Q", 9001},
+};
diff --git a/tools/versioner/src/SymbolDatabase.cpp b/tools/versioner/src/SymbolDatabase.cpp
index 5b8ed5a..c483c0f 100644
--- a/tools/versioner/src/SymbolDatabase.cpp
+++ b/tools/versioner/src/SymbolDatabase.cpp
@@ -16,6 +16,8 @@
 
 #include "SymbolDatabase.h"
 
+#include "SymbolFileParser.h"
+
 #include <err.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -61,3 +63,53 @@
 
   return result;
 }
+
+static std::map<std::string, NdkSymbolType> parsePlatform(const CompilationType& type,
+                                                          const std::string& platform_dir) {
+  static const std::pair<const char*, bool> wanted_files[] = {
+    {"crtbegin.map.txt", false},
+    {"libc.map.txt", true},
+  };
+
+  std::map<std::string, NdkSymbolType> result;
+
+  for (auto&& [filename, required] : wanted_files) {
+    std::string path = platform_dir + "/" + filename;
+
+    std::optional<SymbolMap> symbols = parseSymbolFile(path, type);
+    if (!symbols) {
+      if (required) {
+        errx(1, "error: failed to load: %s", path.c_str());
+      }
+      continue;
+    }
+
+    for (auto&& [symbol_name, symbol_type] : *symbols) {
+      if (symbol_name.empty()) {
+        continue;
+      }
+
+      if (result.count(symbol_name) != 0) {
+        if (strict) {
+          printf("duplicated symbol '%s' in '%s'\n", symbol_name.c_str(), path.c_str());
+        }
+      }
+
+      result[symbol_name] = symbol_type;
+    }
+  }
+
+  return result;
+}
+
+std::optional<NdkSymbolDatabase> parsePlatforms(const std::set<CompilationType>& types,
+                                                const std::string& platform_dir) {
+  NdkSymbolDatabase result;
+  for (const CompilationType& type : types) {
+    std::map<std::string, NdkSymbolType> symbols = parsePlatform(type, platform_dir);
+    for (const auto& it : symbols) {
+      result[it.first][type] = it.second;
+    }
+  }
+  return std::make_optional(std::move(result));
+}
diff --git a/tools/versioner/src/SymbolDatabase.h b/tools/versioner/src/SymbolDatabase.h
index c5b89d7..dbbba4f 100644
--- a/tools/versioner/src/SymbolDatabase.h
+++ b/tools/versioner/src/SymbolDatabase.h
@@ -17,6 +17,7 @@
 #pragma once
 
 #include <map>
+#include <optional>
 #include <set>
 #include <string>
 #include <unordered_set>
@@ -32,3 +33,5 @@
 };
 
 using NdkSymbolDatabase = std::map<std::string, std::map<CompilationType, NdkSymbolType>>;
+std::optional<NdkSymbolDatabase> parsePlatforms(const std::set<CompilationType>& types,
+                                                const std::string& platform_dir);
diff --git a/tools/versioner/src/SymbolFileParser.cpp b/tools/versioner/src/SymbolFileParser.cpp
new file mode 100644
index 0000000..33308d9
--- /dev/null
+++ b/tools/versioner/src/SymbolFileParser.cpp
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2018 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 "SymbolFileParser.h"
+
+#include "Arch.h"
+#include "CompilationType.h"
+
+#include <android-base/strings.h>
+
+#include <fstream>
+#include <ios>
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <err.h>
+
+namespace {
+
+using TagList = std::vector<std::string>;
+
+struct SymbolEnt {
+  std::string name;
+  TagList tags;
+};
+
+using SymbolList = std::vector<SymbolEnt>;
+
+struct Version {
+  std::string name;
+  std::string base;
+  SymbolList symbols;
+  TagList tags;
+};
+
+class SymbolFileParser {
+ public:
+  SymbolFileParser(const std::string& path, const CompilationType& type)
+    : file_path(path),
+      compilation_type(type),
+      api_level_arch_prefix("api-level-" + to_string(type.arch) + "="),
+      intro_arch_perfix("introduced-" + to_string(type.arch) + "="),
+      file(path, std::ios_base::in),
+      curr_line_num(0) {
+  }
+
+  // Parse the version script and build a symbol map.
+  std::optional<SymbolMap> parse() {
+    if (!file) {
+      return std::nullopt;
+    }
+
+    SymbolMap symbol_map;
+    while (hasNextLine()) {
+      auto&& version = parseVersion();
+      if (!version) {
+        return std::nullopt;
+      }
+
+      if (isInArch(version->tags) && isInApi(version->tags)) {
+        for (auto&& [name, tags] : version->symbols) {
+          if (isInArch(tags) && isInApi(tags)) {
+            symbol_map[name] = getSymbolType(tags);
+          }
+        }
+      }
+    }
+    return std::make_optional(std::move(symbol_map));
+  }
+
+ private:
+  // Read a non-empty line from the input and split at the first '#' character.
+  bool hasNextLine() {
+    std::string line;
+    while (std::getline(file, line)) {
+      ++curr_line_num;
+
+      size_t hash_pos = line.find('#');
+      curr_line = android::base::Trim(line.substr(0, hash_pos));
+      if (!curr_line.empty()) {
+        if (hash_pos != std::string::npos) {
+          curr_tags = parseTags(line.substr(hash_pos + 1));
+        } else {
+          curr_tags.clear();
+        }
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // Tokenize the tags after the '#' character.
+  static std::vector<std::string> parseTags(const std::string& tags_line) {
+    std::vector<std::string> tags = android::base::Split(tags_line, " \t");
+    tags.erase(std::remove(tags.begin(), tags.end(), ""), tags.end());
+    return tags;
+  }
+
+  // Parse a version scope.
+  std::optional<Version> parseVersion() {
+    size_t start_line_num = curr_line_num;
+
+    std::string::size_type lparen_pos = curr_line.find('{');
+    if (lparen_pos == std::string::npos) {
+      errx(1, "%s:%zu: error: expected '{' cannot be found in this line",
+           file_path.c_str(), curr_line_num);
+    }
+
+    // Record the version name and version tags (before hasNextLine()).
+    std::string name = android::base::Trim(curr_line.substr(0, lparen_pos));
+    TagList tags = std::move(curr_tags);
+
+    // Read symbol lines.
+    SymbolList symbols;
+    bool global_scope = true;
+    bool cpp_scope = false;
+    while (hasNextLine()) {
+      size_t rparen_pos = curr_line.find('}');
+      if (rparen_pos != std::string::npos) {
+        size_t semicolon_pos = curr_line.find(';', rparen_pos + 1);
+        if (semicolon_pos == std::string::npos) {
+          errx(1, "%s:%zu: error: the line that ends a scope must end with ';'",
+               file_path.c_str(), curr_line_num);
+        }
+
+        if (cpp_scope) {
+          cpp_scope = false;
+          continue;
+        }
+
+        std::string base = android::base::Trim(
+          curr_line.substr(rparen_pos + 1, semicolon_pos - 1));
+
+        return std::make_optional(Version{std::move(name), std::move(base),
+                                          std::move(symbols), std::move(tags)});
+      }
+
+      if (android::base::StartsWith(curr_line, R"(extern "C++" {)")) {
+        cpp_scope = true;
+        continue;
+      }
+
+      if (cpp_scope) {
+        continue;
+      }
+
+      size_t colon_pos = curr_line.find(':');
+      if (colon_pos != std::string::npos) {
+        std::string visibility =
+          android::base::Trim(curr_line.substr(0, colon_pos));
+
+        if (visibility == "global") {
+          global_scope = true;
+        } else if (visibility == "local") {
+          global_scope = false;
+        } else {
+          errx(1, "%s:%zu: error: unknown version visibility: %s",
+               file_path.c_str(), curr_line_num, visibility.c_str());
+        }
+        continue;
+      }
+
+      if (global_scope) {
+        size_t semicolon_pos = curr_line.find(';');
+        if (semicolon_pos == std::string::npos) {
+          errx(1, "%s:%zu: error: symbol name line must end with ';'",
+               file_path.c_str(), curr_line_num);
+        }
+
+        std::string symbol_name =
+          android::base::Trim(curr_line.substr(0, semicolon_pos));
+
+        size_t asterisk_pos = symbol_name.find('*');
+        if (asterisk_pos != std::string::npos) {
+          errx(1, "%s:%zu: error: global symbol name must not have wildcards",
+               file_path.c_str(), curr_line_num);
+        }
+
+        symbols.push_back(SymbolEnt{std::move(symbol_name),
+                                    std::move(curr_tags)});
+      }
+    }
+
+    errx(1, "%s:%zu: error: scope started from %zu must be closed before EOF",
+         file_path.c_str(), curr_line_num, start_line_num);
+  }
+
+  static NdkSymbolType getSymbolType(const TagList& tags) {
+    for (auto&& tag : tags) {
+      if (tag == "var") {
+        return NdkSymbolType::variable;
+      }
+    }
+    return NdkSymbolType::function;
+  }
+
+  // isInArch() returns true if there is a matching arch-specific tag or there
+  // are no arch-specific tags.
+  bool isInArch(const TagList& tags) const {
+    bool has_arch_tags = false;
+    for (auto&& tag : tags) {
+      std::optional<Arch> arch = arch_from_string(tag);
+      if (!arch) {
+        continue;
+      }
+      if (*arch == compilation_type.arch) {
+        return true;
+      }
+      has_arch_tags = true;
+    }
+    return !has_arch_tags;
+  }
+
+  // isInApi() returns true if the specified API level is equal to the
+  // api-level tag, or the specified API level is greater than or equal to the
+  // introduced tag, or there are no api-level or introduced tags.
+  bool isInApi(const TagList& tags) const {
+    bool api_level_arch = false;
+    bool intro_arch = false;
+    std::string api_level;
+    std::string intro;
+
+    for (const std::string& tag : tags) {
+      // Check api-level tags.
+      if (android::base::StartsWith(tag, "api-level=") && !api_level_arch) {
+        api_level = tag;
+        continue;
+      }
+      if (android::base::StartsWith(tag, api_level_arch_prefix)) {
+        api_level = tag;
+        api_level_arch = true;
+        continue;
+      }
+
+      // Check introduced tags.
+      if (android::base::StartsWith(tag, "introduced=") && !intro_arch) {
+        intro = tag;
+        continue;
+      }
+      if (android::base::StartsWith(tag, intro_arch_perfix)) {
+        intro = tag;
+        intro_arch = true;
+        continue;
+      }
+
+      if (tag == "future") {
+        return compilation_type.api_level == future_api;
+      }
+    }
+
+    if (intro.empty() && api_level.empty()) {
+      return true;
+    }
+
+    if (!api_level.empty()) {
+      // If an api-level tag is specified, it must be an exact match (mainly
+      // for versioner unit tests).
+      return compilation_type.api_level == decodeApiLevelValue(api_level);
+    }
+
+    return compilation_type.api_level >= decodeApiLevelValue(intro);
+  }
+
+  // Extract and decode the integer API level from api-level or introduced tags.
+  static int decodeApiLevelValue(const std::string& tag) {
+    std::string api_level = tag.substr(tag.find('=') + 1);
+    auto it = api_codename_map.find(api_level);
+    if (it != api_codename_map.end()) {
+      return it->second;
+    }
+    return std::stoi(api_level);
+  }
+
+ private:
+  const std::string& file_path;
+  const CompilationType& compilation_type;
+  const std::string api_level_arch_prefix;
+  const std::string intro_arch_perfix;
+
+  std::ifstream file;
+  std::string curr_line;
+  std::vector<std::string> curr_tags;
+  size_t curr_line_num;
+};
+
+}  // anonymous namespace
+
+
+std::optional<SymbolMap> parseSymbolFile(const std::string& file_path,
+                                         const CompilationType& type) {
+  SymbolFileParser parser(file_path, type);
+  return parser.parse();
+}
diff --git a/tools/versioner/src/SymbolFileParser.h b/tools/versioner/src/SymbolFileParser.h
new file mode 100644
index 0000000..5cdbf2f
--- /dev/null
+++ b/tools/versioner/src/SymbolFileParser.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 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 <map>
+#include <string>
+#include <optional>
+
+#include "DeclarationDatabase.h"
+#include "SymbolDatabase.h"
+
+using SymbolMap = std::map<std::string, NdkSymbolType>;
+
+std::optional<SymbolMap> parseSymbolFile(const std::string &file,
+                                         const CompilationType& type);
diff --git a/tools/versioner/src/versioner.cpp b/tools/versioner/src/versioner.cpp
index af0c067..73eff0e 100644
--- a/tools/versioner/src/versioner.cpp
+++ b/tools/versioner/src/versioner.cpp
@@ -33,7 +33,6 @@
 #include <iostream>
 #include <map>
 #include <memory>
-#include <optional>
 #include <set>
 #include <sstream>
 #include <string>
@@ -519,8 +518,11 @@
       }
 
       case 'r': {
-        Arch arch = arch_from_string(optarg);
-        selected_architectures.insert(arch);
+        std::optional<Arch> arch = arch_from_string(optarg);
+        if (!arch) {
+          errx(1, "unknown architecture '%s'", optarg);
+        }
+        selected_architectures.insert(*arch);
         break;
       }
 
@@ -613,6 +615,9 @@
     std::string versioner_dir = to_string(top) + "/bionic/tools/versioner";
     location.header_path = versioner_dir + "/current";
     location.dependency_dir = versioner_dir + "/dependencies";
+    if (platform_dir.empty()) {
+      platform_dir = versioner_dir + "/platforms";
+    }
   } else {
     if (!android::base::Realpath(argv[optind], &location.header_path)) {
       err(1, "failed to get realpath for path '%s'", argv[optind]);
@@ -653,6 +658,12 @@
 
   compilation_types = generateCompilationTypes(selected_architectures, selected_levels);
 
+  // Do this before compiling so that we can early exit if the platforms don't match what we
+  // expect.
+  if (!platform_dir.empty()) {
+    symbol_database = parsePlatforms(compilation_types, platform_dir);
+  }
+
   auto start = std::chrono::high_resolution_clock::now();
   std::unique_ptr<HeaderDatabase> declaration_database =
       compileHeaders(compilation_types, location);
diff --git a/tools/versioner/tests/arch_specific/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/arch_specific/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/arch_specific/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/arch_specific/platforms/android-9/arch-x86/symbols/libc.so.functions.txt b/tools/versioner/tests/arch_specific/platforms/android-9/arch-x86/symbols/libc.so.functions.txt
deleted file mode 100644
index 3bd1f0e..0000000
--- a/tools/versioner/tests/arch_specific/platforms/android-9/arch-x86/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-foo
-bar
diff --git a/tools/versioner/tests/arch_specific/platforms/libc.map.txt b/tools/versioner/tests/arch_specific/platforms/libc.map.txt
new file mode 100644
index 0000000..5aa11ed
--- /dev/null
+++ b/tools/versioner/tests/arch_specific/platforms/libc.map.txt
@@ -0,0 +1,5 @@
+LIBC {
+  global:
+    foo;  # arm x86
+    bar;  # x86
+};
diff --git a/tools/versioner/tests/compilation_error/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/compilation_error/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/compilation_error/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/compilation_error/platforms/libc.map.txt b/tools/versioner/tests/compilation_error/platforms/libc.map.txt
new file mode 100644
index 0000000..6cc4a2e
--- /dev/null
+++ b/tools/versioner/tests/compilation_error/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;
+};
diff --git a/tools/versioner/tests/dependencies/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/dependencies/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/dependencies/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/dependencies/platforms/android-9/arch-x86/symbols/libc.so.functions.txt b/tools/versioner/tests/dependencies/platforms/android-9/arch-x86/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/dependencies/platforms/android-9/arch-x86/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/dependencies/platforms/libc.map.txt b/tools/versioner/tests/dependencies/platforms/libc.map.txt
new file mode 100644
index 0000000..6cc4a2e
--- /dev/null
+++ b/tools/versioner/tests/dependencies/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;
+};
diff --git a/tools/versioner/tests/extern_cpp/platforms/android-21/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/extern_cpp/platforms/android-21/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 8b13789..0000000
--- a/tools/versioner/tests/extern_cpp/platforms/android-21/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/tools/versioner/tests/extern_cpp/platforms/android-23/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/extern_cpp/platforms/android-23/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 36fe04f..0000000
--- a/tools/versioner/tests/extern_cpp/platforms/android-23/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-__gnu_basename
diff --git a/tools/versioner/tests/extern_cpp/platforms/libc.map.txt b/tools/versioner/tests/extern_cpp/platforms/libc.map.txt
new file mode 100644
index 0000000..297d1fb
--- /dev/null
+++ b/tools/versioner/tests/extern_cpp/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    __gnu_basename;  # introduced=23
+};
diff --git a/tools/versioner/tests/extern_cpp_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/extern_cpp_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/extern_cpp_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/extern_cpp_mismatch/platforms/libc.map.txt b/tools/versioner/tests/extern_cpp_mismatch/platforms/libc.map.txt
new file mode 100644
index 0000000..6cc4a2e
--- /dev/null
+++ b/tools/versioner/tests/extern_cpp_mismatch/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;
+};
diff --git a/tools/versioner/tests/future/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/future/platforms/libc.map.txt
similarity index 100%
rename from tools/versioner/tests/future/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
rename to tools/versioner/tests/future/platforms/libc.map.txt
diff --git a/tools/versioner/tests/future_arch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/future_arch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/future_arch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/future_arch/platforms/android-9/arch-x86/symbols/libc.so.functions.txt b/tools/versioner/tests/future_arch/platforms/android-9/arch-x86/symbols/libc.so.functions.txt
deleted file mode 100644
index e69de29..0000000
--- a/tools/versioner/tests/future_arch/platforms/android-9/arch-x86/symbols/libc.so.functions.txt
+++ /dev/null
diff --git a/tools/versioner/tests/future_arch/platforms/libc.map.txt b/tools/versioner/tests/future_arch/platforms/libc.map.txt
new file mode 100644
index 0000000..f56190e
--- /dev/null
+++ b/tools/versioner/tests/future_arch/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;  # arm
+};
diff --git a/tools/versioner/tests/inline/platforms/android-12/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/inline/platforms/android-12/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/inline/platforms/android-12/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/inline/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/inline/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index e69de29..0000000
--- a/tools/versioner/tests/inline/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
diff --git a/tools/versioner/tests/inline/platforms/libc.map.txt b/tools/versioner/tests/inline/platforms/libc.map.txt
new file mode 100644
index 0000000..4dced92
--- /dev/null
+++ b/tools/versioner/tests/inline/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;  # introduced=12
+};
diff --git a/tools/versioner/tests/missing_api/platforms/android-12/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/missing_api/platforms/android-12/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index e69de29..0000000
--- a/tools/versioner/tests/missing_api/platforms/android-12/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
diff --git a/tools/versioner/tests/missing_api/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/missing_api/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/missing_api/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/missing_api/platforms/libc.map.txt b/tools/versioner/tests/missing_api/platforms/libc.map.txt
new file mode 100644
index 0000000..3701a9d
--- /dev/null
+++ b/tools/versioner/tests/missing_api/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;  # api-level=9
+};
diff --git a/tools/versioner/tests/missing_arch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/missing_arch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/missing_arch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/missing_arch/platforms/android-9/arch-x86/symbols/libc.so.functions.txt b/tools/versioner/tests/missing_arch/platforms/android-9/arch-x86/symbols/libc.so.functions.txt
deleted file mode 100644
index e69de29..0000000
--- a/tools/versioner/tests/missing_arch/platforms/android-9/arch-x86/symbols/libc.so.functions.txt
+++ /dev/null
diff --git a/tools/versioner/tests/missing_arch/platforms/libc.map.txt b/tools/versioner/tests/missing_arch/platforms/libc.map.txt
new file mode 100644
index 0000000..f56190e
--- /dev/null
+++ b/tools/versioner/tests/missing_arch/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;  # arm
+};
diff --git a/tools/versioner/tests/multiple_decl/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/multiple_decl/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/multiple_decl/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/multiple_decl/platforms/libc.map.txt b/tools/versioner/tests/multiple_decl/platforms/libc.map.txt
new file mode 100644
index 0000000..6cc4a2e
--- /dev/null
+++ b/tools/versioner/tests/multiple_decl/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;
+};
diff --git a/tools/versioner/tests/multiple_decl_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/multiple_decl_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/multiple_decl_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/multiple_decl_mismatch/platforms/libc.map.txt b/tools/versioner/tests/multiple_decl_mismatch/platforms/libc.map.txt
new file mode 100644
index 0000000..6cc4a2e
--- /dev/null
+++ b/tools/versioner/tests/multiple_decl_mismatch/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;
+};
diff --git a/tools/versioner/tests/multiple_definition/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/multiple_definition/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/multiple_definition/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/multiple_definition/platforms/libc.map.txt b/tools/versioner/tests/multiple_definition/platforms/libc.map.txt
new file mode 100644
index 0000000..6cc4a2e
--- /dev/null
+++ b/tools/versioner/tests/multiple_definition/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;
+};
diff --git a/tools/versioner/tests/multiple_definition_ok/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/multiple_definition_ok/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/multiple_definition_ok/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/multiple_definition_ok/platforms/libc.map.txt b/tools/versioner/tests/multiple_definition_ok/platforms/libc.map.txt
new file mode 100644
index 0000000..6cc4a2e
--- /dev/null
+++ b/tools/versioner/tests/multiple_definition_ok/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;
+};
diff --git a/tools/versioner/tests/obsoleted/platforms/android-12/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/obsoleted/platforms/android-12/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index e69de29..0000000
--- a/tools/versioner/tests/obsoleted/platforms/android-12/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
diff --git a/tools/versioner/tests/obsoleted/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/obsoleted/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/obsoleted/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/obsoleted/platforms/libc.map.txt b/tools/versioner/tests/obsoleted/platforms/libc.map.txt
new file mode 100644
index 0000000..3701a9d
--- /dev/null
+++ b/tools/versioner/tests/obsoleted/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;  # api-level=9
+};
diff --git a/tools/versioner/tests/preprocessor/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/preprocessor/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/preprocessor/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/preprocessor_extern_cpp/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/preprocessor_extern_cpp/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/preprocessor_extern_cpp/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/preprocessor_merging/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/preprocessor_merging/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/preprocessor_merging/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/smoke/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/smoke/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/smoke/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/smoke/platforms/libc.map.txt b/tools/versioner/tests/smoke/platforms/libc.map.txt
new file mode 100644
index 0000000..6cc4a2e
--- /dev/null
+++ b/tools/versioner/tests/smoke/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;
+};
diff --git a/tools/versioner/tests/unnamed_bitfield/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/unnamed_bitfield/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/unnamed_bitfield/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/unnamed_bitfield/platforms/libc.map.txt b/tools/versioner/tests/unnamed_bitfield/platforms/libc.map.txt
new file mode 100644
index 0000000..6cc4a2e
--- /dev/null
+++ b/tools/versioner/tests/unnamed_bitfield/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;
+};
diff --git a/tools/versioner/tests/version_mismatch/platforms/android-12/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/version_mismatch/platforms/android-12/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/version_mismatch/platforms/android-12/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/version_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt b/tools/versioner/tests/version_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
deleted file mode 100644
index 257cc56..0000000
--- a/tools/versioner/tests/version_mismatch/platforms/android-9/arch-arm/symbols/libc.so.functions.txt
+++ /dev/null
@@ -1 +0,0 @@
-foo
diff --git a/tools/versioner/tests/version_mismatch/platforms/libc.map.txt b/tools/versioner/tests/version_mismatch/platforms/libc.map.txt
new file mode 100644
index 0000000..6cc4a2e
--- /dev/null
+++ b/tools/versioner/tests/version_mismatch/platforms/libc.map.txt
@@ -0,0 +1,4 @@
+LIBC {
+  global:
+    foo;
+};