Add option to parse version script during linking.

1) Add option to parse version script file specifying exported symbols and
variables during link time.
2) Added test version script file.
3) Minor style cleanup.
4) Remove HandleVTable as it is no longer needed.
5) header-abi-diff has a mode to enable advice-only mode. This means no
failures on abi extensions / incompatiblity. Only report generation.
5) Seperate out functionality into util lib.

Test: 1) header-abi-dumper -o example1.dump tests/input/example1.cpp -I
tests -- -x c++ -std=c++11 -I . -I
~/android/aosp/external/clang/lib/Headers

2) header-abi-linker -o test.lsdump example1.dump -v
tests/input/test_version_script.map -arch arm64 -api 24,
test.lsdump has only the symbols specified by test_version_script.amp

Change-Id: Ib539db4a886abe4917e09cd8643ab35ffecb0df1
diff --git a/vndk/tools/header-checker/Android.bp b/vndk/tools/header-checker/Android.bp
index 8c97386..70fd232 100644
--- a/vndk/tools/header-checker/Android.bp
+++ b/vndk/tools/header-checker/Android.bp
@@ -89,6 +89,7 @@
 
     shared_libs: [
         "libprotobuf-cpp-full",
+        "libheader-abi-util",
     ],
 }
 
@@ -129,6 +130,10 @@
         "libheader-checker-proto",
     ],
 
+		shared_libs: [
+        "libheader-abi-util",
+    ],
+
     target: {
         linux: {
             host_ldlibs: [
@@ -208,3 +213,25 @@
         },
     },
 }
+
+cc_library_shared {
+    name: "libheader-abi-util",
+    defaults: [
+        "header-checker-defaults",
+    ],
+    host_supported: true,
+    export_include_dirs: ["header-abi-util/include"],
+    local_include_dirs: ["header-abi-util/include"],
+
+    srcs: [
+        "header-abi-util/src/*.cpp"
+    ],
+    static_libs: [
+        "libLLVMSupport",
+    ],
+    cflags: [
+        "-Wcast-qual",
+        "-Wno-long-long",
+        "-Wno-unused-parameter",
+    ],
+}
diff --git a/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.cpp b/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.cpp
index c886315..8cb0bc7 100644
--- a/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.cpp
+++ b/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.cpp
@@ -84,7 +84,7 @@
 
 template <>
 bool Diff<EnumFieldDecl>(const EnumFieldDecl &old_element,
-                                const EnumFieldDecl &new_element) {
+                         const EnumFieldDecl &new_element) {
   // Can be specialized for future changes in the format.
   return DiffBasicTypeAbi(old_element.basic_abi().type_abi(),
                           new_element.basic_abi().type_abi()) ||
diff --git a/vndk/tools/header-checker/header-abi-diff/src/header_abi_diff.cpp b/vndk/tools/header-checker/header-abi-diff/src/header_abi_diff.cpp
index 226e0f4..111ef61 100644
--- a/vndk/tools/header-checker/header-abi-diff/src/header_abi_diff.cpp
+++ b/vndk/tools/header-checker/header-abi-diff/src/header_abi_diff.cpp
@@ -32,6 +32,10 @@
     "old", llvm::cl::desc("<old dump>"), llvm::cl::Required,
     llvm::cl::cat(header_checker_category));
 
+static llvm::cl::opt<bool> advice_only(
+    "advice-only", llvm::cl::desc("Advisory mode only"), llvm::cl::Optional,
+    llvm::cl::cat(header_checker_category));
+
 int main(int argc, const char **argv) {
   GOOGLE_PROTOBUF_VERIFY_VERSION;
   llvm::cl::ParseCommandLineOptions(argc, argv, "header-checker");
@@ -53,6 +57,9 @@
                  << " Please check compatiblity report at : "
                  << compatibility_report << "\n"
                  << "*****************************************************\n";
+    if (!advice_only) {
+      return extension_or_incompatible;
+    }
   }
-  return extension_or_incompatible;
+  return 0;
 }
diff --git a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp
index a8d5ae1..6e34be8 100644
--- a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp
+++ b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp
@@ -126,13 +126,18 @@
   return true;
 }
 
+static bool AreHeadersExported(const std::set<std::string> &exported_headers) {
+  return exported_headers.empty();
+}
+
 // We don't need to recurse into Declarations which are not exported.
 bool HeaderASTVisitor::TraverseDecl(clang::Decl *decl) {
   if (!decl) {
     return true;
   }
   std::string source_file = ABIWrapper::GetDeclSourceFile(decl, cip_);
-  if ((decl != tu_decl_) &&
+  // If no exported headers are specified we assume the whole AST is exported.
+  if ((decl != tu_decl_) && !AreHeadersExported(exported_headers_) &&
       (exported_headers_.find(source_file) == exported_headers_.end())) {
     return true;
   }
@@ -167,10 +172,6 @@
   }
 }
 
-void HeaderASTConsumer::HandleVTable(clang::CXXRecordDecl *crd) {
-  llvm::errs() << "HandleVTable: " << crd->getName() << "\n";
-}
-
 void HeaderASTPPCallbacks::MacroDefined(const clang::Token &macro_name_tok,
                                         const clang::MacroDirective *) {
   assert(macro_name_tok.getLength() != 0);
diff --git a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h
index f8de1dc..038918b 100644
--- a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h
+++ b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h
@@ -76,8 +76,6 @@
 
   void HandleTranslationUnit(clang::ASTContext &ctx) override;
 
-  void HandleVTable(clang::CXXRecordDecl *crd) override;
-
  private:
   std::string file_name_;
   clang::CompilerInstance *cip_;
diff --git a/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.cpp b/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.cpp
index 00abf66..69d6491 100644
--- a/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.cpp
+++ b/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.cpp
@@ -12,9 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "frontend_action.h"
 
 #include "ast_processing.h"
+#include "frontend_action.h"
+#include <header_abi_util.h>
 
 #include <clang/AST/ASTConsumer.h>
 #include <clang/Frontend/CompilerInstance.h>
@@ -35,7 +36,7 @@
   pp.addPPCallbacks(llvm::make_unique<HeaderASTPPCallbacks>());
   std::set<std::string> exported_headers;
   for (auto &&dir_name : export_header_dirs_) {
-    if (!CollectExportedHeaderSet(dir_name, &exported_headers)) {
+    if (!abi_util::CollectExportedHeaderSet(dir_name, &exported_headers)) {
          return nullptr;
     }
   }
@@ -44,55 +45,3 @@
                                               &ci, dump_name_,
                                               exported_headers);
 }
-
-static bool ShouldSkipFile(llvm::StringRef &file_name) {
-  return (file_name.empty() || file_name.startswith(".") ||
-          file_name.endswith(".swp") || file_name.endswith(".swo") ||
-          file_name.endswith("#") || file_name.endswith(".cpp") ||
-          file_name.endswith(".cc") || file_name.endswith(".c"));
-}
-
-bool HeaderCheckerFrontendAction::CollectExportedHeaderSet(
-    const std::string &dir_name,
-    std::set<std::string> *exported_headers) {
-  std::error_code ec;
-  llvm::sys::fs::recursive_directory_iterator walker(dir_name, ec);
-  // Default construction - end of directory.
-  llvm::sys::fs::recursive_directory_iterator end;
-  llvm::sys::fs::file_status status;
-  for ( ; walker != end; walker.increment(ec)) {
-    if (ec) {
-      llvm::errs() << "Failed to walk dir : " << dir_name << "\n";
-      return false;
-    }
-
-    const std::string &file_path = walker->path();
-
-    llvm::StringRef file_name(llvm::sys::path::filename(file_path));
-    // Ignore swap files and hidden files / dirs. Do not recurse into them too.
-    // We should also not look at source files. Many projects include source
-    // files in their exports.
-    if (ShouldSkipFile(file_name)) {
-      walker.no_push();
-      continue;
-    }
-
-    if (walker->status(status)) {
-      llvm::errs() << "Failed to stat file : " << file_path << "\n";
-      return false;
-    }
-
-    if (!llvm::sys::fs::is_regular_file(status)) {
-      // Ignore non regular files. eg: soft links.
-      continue;
-    }
-
-    llvm::SmallString<128> abs_path(file_path);
-    if (llvm::sys::fs::make_absolute(abs_path)) {
-      llvm::errs() << "Failed to get absolute path for : " << file_name << "\n";
-      return false;
-    }
-    exported_headers->insert(abs_path.str());
-  }
-  return true;
-}
diff --git a/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.h b/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.h
index 40a3b3b..85afe23 100644
--- a/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.h
+++ b/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.h
@@ -41,11 +41,6 @@
  protected:
   std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
       clang::CompilerInstance &ci, llvm::StringRef header_file) override;
-
- private:
-  bool CollectExportedHeaderSet(
-      const std::string &dir_name,
-      std::set<std::string> *eh);
 };
 
 #endif  // FRONTEND_ACTION_H_
diff --git a/vndk/tools/header-checker/header-abi-dumper/src/header_checker.cpp b/vndk/tools/header-checker/header-abi-dumper/src/header_checker.cpp
index f224309..00045e5 100644
--- a/vndk/tools/header-checker/header-abi-dumper/src/header_checker.cpp
+++ b/vndk/tools/header-checker/header-abi-dumper/src/header_checker.cpp
@@ -41,7 +41,7 @@
     llvm::cl::cat(header_checker_category));
 
 static llvm::cl::list<std::string> exported_header_dirs(
-    "I", llvm::cl::desc("<export_include_dirs>"), llvm::cl::OneOrMore,
+    "I", llvm::cl::desc("<export_include_dirs>"), llvm::cl::ZeroOrMore,
     llvm::cl::cat(header_checker_category));
 
 // Hide irrelevant command line options defined in LLVM libraries.
diff --git a/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp b/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp
index 9644e08..b67e647 100644
--- a/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp
+++ b/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp
@@ -18,10 +18,11 @@
 #include "proto/abi_dump.pb.h"
 #pragma clang diagnostic pop
 
+#include <header_abi_util.h>
+
 #include <llvm/Support/CommandLine.h>
 #include <llvm/Support/raw_ostream.h>
 
-
 #include <google/protobuf/text_format.h>
 #include <google/protobuf/io/zero_copy_stream_impl.h>
 
@@ -33,23 +34,45 @@
 
 #include <stdlib.h>
 
-static llvm::cl::OptionCategory header_checker_category(
+static llvm::cl::OptionCategory header_linker_category(
     "header-abi-linker options");
 
 static llvm::cl::list<std::string> dump_files(
     llvm::cl::Positional, llvm::cl::desc("<dump-files>"), llvm::cl::Required,
-    llvm::cl::cat(header_checker_category), llvm::cl::OneOrMore);
+    llvm::cl::cat(header_linker_category), llvm::cl::OneOrMore);
 
 static llvm::cl::opt<std::string> linked_dump(
     "o", llvm::cl::desc("<linked dump>"), llvm::cl::Required,
-    llvm::cl::cat(header_checker_category));
+    llvm::cl::cat(header_linker_category));
+
+static llvm::cl::list<std::string> exported_header_dirs(
+    "I", llvm::cl::desc("<export_include_dirs>"), llvm::cl::ZeroOrMore,
+    llvm::cl::cat(header_linker_category));
+
+static llvm::cl::opt<std::string> version_script(
+    "v", llvm::cl::desc("<version_script>"), llvm::cl::Optional,
+    llvm::cl::cat(header_linker_category));
+
+static llvm::cl::opt<std::string> api(
+    "api", llvm::cl::desc("<api>"), llvm::cl::Optional,
+    llvm::cl::cat(header_linker_category));
+
+static llvm::cl::opt<std::string> arch(
+    "arch", llvm::cl::desc("<arch>"), llvm::cl::Optional,
+    llvm::cl::cat(header_linker_category));
 
 class HeaderAbiLinker {
  public:
   HeaderAbiLinker(
-      const std::vector<std::string> &files,
-      const std::string &linked_dump)
-    : dump_files_(files), out_dump_name_(linked_dump) {};
+      const std::vector<std::string> &dump_files,
+      const std::vector<std::string> &exported_header_dirs,
+      const std::string &version_script,
+      const std::string &linked_dump,
+      const std::string &arch,
+      const std::string &api)
+    : dump_files_(dump_files), exported_header_dirs_(exported_header_dirs),
+    version_script_(version_script), out_dump_name_(linked_dump), arch_(arch),
+    api_(api) {};
 
   bool LinkAndDump();
 
@@ -67,14 +90,21 @@
                       abi_dump::TranslationUnit *linked_tu);
 
   template <typename T>
-  static inline bool LinkDecl(
-    google::protobuf::RepeatedPtrField<T> *dst,
-    std::set<std::string> *link_set,
-    const google::protobuf::RepeatedPtrField<T> &src);
+  inline bool LinkDecl(google::protobuf::RepeatedPtrField<T> *dst,
+                       std::set<std::string> *link_set,
+                       const google::protobuf::RepeatedPtrField<T> &src,
+                       bool use_version_script);
+
+  bool ParseVersionScriptFiles();
 
  private:
   const std::vector<std::string> &dump_files_;
+  const std::vector<std::string> &exported_header_dirs_;
+  const std::string &version_script_;
   const std::string &out_dump_name_;
+  const std::string &arch_;
+  const std::string &api_;
+  std::set<std::string> exported_headers_;
   std::set<std::string> record_decl_set_;
   std::set<std::string> function_decl_set_;
   std::set<std::string> enum_decl_set_;
@@ -85,6 +115,16 @@
   abi_dump::TranslationUnit linked_tu;
   std::ofstream text_output(out_dump_name_);
   google::protobuf::io::OstreamOutputStream text_os(&text_output);
+  if (!version_script_.empty() && !ParseVersionScriptFiles()) {
+    llvm::errs() << "Failed to parse stub files for exported symbols\n";
+    return false;
+  }
+  for (auto &&dir : exported_header_dirs_) {
+    if (!abi_util::CollectExportedHeaderSet(dir, &exported_headers_)) {
+      llvm::errs() << "Couldn't collect exported headers\n";
+      return false;
+    }
+  }
   for (auto &&i : dump_files_) {
     abi_dump::TranslationUnit dump_tu;
     std::ifstream input(i);
@@ -106,18 +146,51 @@
   return true;
 }
 
+static std::string GetSymbol(const abi_dump::RecordDecl &element) {
+  return element.mangled_record_name();
+}
+
+static std::string GetSymbol(const abi_dump::FunctionDecl &element) {
+  return element.mangled_function_name();
+}
+
+static std::string GetSymbol(const abi_dump::EnumDecl &element) {
+  return element.basic_abi().linker_set_key();
+}
+
+static std::string GetSymbol(const abi_dump::GlobalVarDecl &element) {
+  return element.basic_abi().linker_set_key();
+}
+
 template <typename T>
 inline bool HeaderAbiLinker::LinkDecl(
     google::protobuf::RepeatedPtrField<T> *dst,
     std::set<std::string> *link_set,
-    const google::protobuf::RepeatedPtrField<T> &src) {
+    const google::protobuf::RepeatedPtrField<T> &src,
+    bool use_version_script) {
   assert(dst != nullptr);
   assert(link_set != nullptr);
   for (auto &&element : src) {
-    // The element already exists in the linked dump. Skip.
-    if (!link_set->insert(element.basic_abi().linker_set_key()).second) {
+    // If exported headers are available, filter out unexported abi.
+    if (!exported_headers_.empty() &&
+        exported_headers_.find(element.source_file()) ==
+        exported_headers_.end()) {
       continue;
     }
+    // Check for the existence of the element in linked dump / symbol file.
+    if (!use_version_script) {
+        if (!link_set->insert(element.basic_abi().linker_set_key()).second) {
+        continue;
+        }
+    } else {
+      std::set<std::string>::iterator it =
+          link_set->find(GetSymbol(element));
+      if (it == link_set->end()) {
+        continue;
+      } else {
+        link_set->erase(*it); // Avoid multiple instances of the same symbol.
+      }
+    }
     T *added_element = dst->Add();
     if (!added_element) {
       llvm::errs() << "Failed to add element to linked dump\n";
@@ -131,38 +204,57 @@
 bool HeaderAbiLinker::LinkRecords(const abi_dump::TranslationUnit &dump_tu,
                                   abi_dump::TranslationUnit *linked_tu) {
   assert(linked_tu != nullptr);
+  if (!version_script_.empty()) {
+    // version scripts do not have record tags.
+    return true;
+  }
   return LinkDecl(linked_tu->mutable_records(), &record_decl_set_,
-                  dump_tu.records());
+                  dump_tu.records(), false);
 }
 
 bool HeaderAbiLinker::LinkFunctions(const abi_dump::TranslationUnit &dump_tu,
                                     abi_dump::TranslationUnit *linked_tu) {
   assert(linked_tu != nullptr);
   return LinkDecl(linked_tu->mutable_functions(), &function_decl_set_,
-                  dump_tu.functions());
+                  dump_tu.functions(), (!version_script_.empty()));
 }
 
 bool HeaderAbiLinker::LinkEnums(const abi_dump::TranslationUnit &dump_tu,
                                 abi_dump::TranslationUnit *linked_tu) {
   assert(linked_tu != nullptr);
+  if (!version_script.empty()) {
+    // version scripts do not have enum tags.
+    return true;
+  }
   return LinkDecl(linked_tu->mutable_enums(), &enum_decl_set_,
-                  dump_tu.enums());
+                  dump_tu.enums(), false);
 }
 
 bool HeaderAbiLinker::LinkGlobalVars(const abi_dump::TranslationUnit &dump_tu,
                                      abi_dump::TranslationUnit *linked_tu) {
   assert(linked_tu != nullptr);
   return LinkDecl(linked_tu->mutable_global_vars(), &globvar_decl_set_,
-                  dump_tu.global_vars());
+                  dump_tu.global_vars(), (!version_script.empty()));
+}
+
+bool HeaderAbiLinker::ParseVersionScriptFiles() {
+  abi_util::VersionScriptParser version_script_parser(version_script_, arch_,
+                                                      api_);
+  if (!version_script_parser.Parse()) {
+    return false;
+  }
+  function_decl_set_ = version_script_parser.GetFunctions();
+  globvar_decl_set_ = version_script_parser.GetGlobVars();
+  return true;
 }
 
 int main(int argc, const char **argv) {
   GOOGLE_PROTOBUF_VERIFY_VERSION;
-  llvm::cl::ParseCommandLineOptions(argc, argv, "header-checker");
-  HeaderAbiLinker Linker(dump_files, linked_dump);
+  llvm::cl::ParseCommandLineOptions(argc, argv, "header-linker");
+  HeaderAbiLinker Linker(dump_files, exported_header_dirs,
+                         version_script, linked_dump, arch, api);
   if (!Linker.LinkAndDump()) {
     return -1;
   }
-
   return 0;
 }
diff --git a/vndk/tools/header-checker/header-abi-util/include/header_abi_util.h b/vndk/tools/header-checker/header-abi-util/include/header_abi_util.h
new file mode 100644
index 0000000..789e310
--- /dev/null
+++ b/vndk/tools/header-checker/header-abi-util/include/header_abi_util.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2017 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 <set>
+#include <string>
+#include <vector>
+
+namespace abi_util {
+
+bool CollectExportedHeaderSet(const std::string &dir_name,
+                              std::set<std::string> *exported_headers);
+class VersionScriptParser {
+ public:
+
+  enum LineScope {
+    global,
+    local,
+  };
+
+  VersionScriptParser(const std::string &version_script,
+                      const std::string &arch,
+                      const std::string &api);
+  bool Parse();
+
+  const std::set<std::string> &GetFunctions();
+
+  const std::set<std::string> &GetGlobVars();
+
+ private:
+
+  bool ParseInnerBlock(std::ifstream &symbol_ifstream);
+
+  LineScope GetLineScope(std::string &line, LineScope scope);
+
+  bool ParseSymbolLine(const std::string &line);
+
+  bool SymbolInArchAndApiVersion(const std::string &line,
+                                 const std::string &arch, int api);
+
+  bool SymbolExported(const std::string &line, const std::string &arch,
+                      int api);
+
+  int ApiStrToInt(const std::string &api);
+
+ private:
+  const std::string &version_script_;
+  const std::string &arch_;
+  std::set<std::string> functions_;
+  std::set<std::string> globvars_;
+  int api_;
+};
+
+} // namespace abi_util
diff --git a/vndk/tools/header-checker/header-abi-util/src/collect_exported_headers.cpp b/vndk/tools/header-checker/header-abi-util/src/collect_exported_headers.cpp
new file mode 100644
index 0000000..13a7072
--- /dev/null
+++ b/vndk/tools/header-checker/header-abi-util/src/collect_exported_headers.cpp
@@ -0,0 +1,84 @@
+// Copyright (C) 2017 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 <header_abi_util.h>
+
+#include <llvm/Support/raw_ostream.h>
+#include <llvm/Support/FileSystem.h>
+#include <llvm/Support/Path.h>
+
+#include <set>
+#include <string>
+#include <vector>
+
+namespace abi_util {
+
+static bool ShouldSkipFile(llvm::StringRef &file_name) {
+ // Ignore swap files and hidden files / dirs. Do not recurse into them too.
+  // We should also not look at source files. Many projects include source
+  // files in their exports.
+  if (file_name.empty() || file_name.startswith(".") ||
+      file_name.endswith(".swp") || file_name.endswith(".swo") ||
+      file_name.endswith("#") || file_name.endswith(".cpp") ||
+      file_name.endswith(".cc") || file_name.endswith(".c")) {
+    return true;
+  }
+  return false;
+}
+
+bool CollectExportedHeaderSet(const std::string &dir_name,
+                              std::set<std::string> *exported_headers) {
+  std::error_code ec;
+  llvm::sys::fs::recursive_directory_iterator walker(dir_name, ec);
+  // Default construction - end of directory.
+  llvm::sys::fs::recursive_directory_iterator end;
+  llvm::sys::fs::file_status status;
+  for ( ; walker != end; walker.increment(ec)) {
+    if (ec) {
+      llvm::errs() << "Failed to walk dir : " << dir_name << "\n";
+      return false;
+    }
+
+    const std::string &file_path = walker->path();
+
+    llvm::StringRef file_name(llvm::sys::path::filename(file_path));
+    // Ignore swap files and hidden files / dirs. Do not recurse into them too.
+    // We should also not look at source files. Many projects include source
+    // files in their exports.
+    if (ShouldSkipFile(file_name)) {
+      walker.no_push();
+      continue;
+    }
+
+    if (walker->status(status)) {
+      llvm::errs() << "Failed to stat file : " << file_path << "\n";
+      return false;
+    }
+
+    if (!llvm::sys::fs::is_regular_file(status)) {
+      // Ignore non regular files. eg: soft links.
+      continue;
+    }
+
+    llvm::SmallString<128> abs_path(file_path);
+    if (llvm::sys::fs::make_absolute(abs_path)) {
+      llvm::errs() << "Failed to get absolute path for : " << file_name << "\n";
+      return false;
+    }
+    exported_headers->insert(abs_path.str());
+  }
+  return true;
+}
+
+} // namespace abi_util
diff --git a/vndk/tools/header-checker/header-abi-util/src/version_script_parser.cpp b/vndk/tools/header-checker/header-abi-util/src/version_script_parser.cpp
new file mode 100644
index 0000000..d651d07
--- /dev/null
+++ b/vndk/tools/header-checker/header-abi-util/src/version_script_parser.cpp
@@ -0,0 +1,189 @@
+// Copyright (C) 2017 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 <header_abi_util.h>
+
+#include <llvm/Support/raw_ostream.h>
+#include <llvm/Support/FileSystem.h>
+#include <llvm/Support/Path.h>
+
+#include <memory>
+#include <fstream>
+#include <iostream>
+#include <set>
+#include <unordered_set>
+#include <string>
+#include <vector>
+#include <regex>
+
+namespace abi_util {
+
+#define FUTURE_API 10000
+
+std::unordered_set<std::string> AllArches({"arm", "arm64", "x86", "x86_64",
+                                        "mips", "mips64"});
+
+static bool StringContains(const std::string &line,
+                           const std::string &substring) {
+  return (line.find(substring) != std::string::npos);
+}
+
+static bool LineSatisfiesArch(const std::string &line,
+                              const std::string arch) {
+  bool has_arch_tags = false;
+  for (auto &&possible_arch : AllArches) {
+    if (StringContains(line, possible_arch)) {
+      has_arch_tags = true;
+      break;
+    }
+  }
+  return (has_arch_tags && StringContains(line, arch)) || !has_arch_tags;
+}
+
+VersionScriptParser::VersionScriptParser(const std::string &version_script,
+                                         const std::string &arch,
+                                         const std::string &api) :
+  version_script_(version_script), arch_(arch), api_(ApiStrToInt(api)) { }
+
+int VersionScriptParser::ApiStrToInt(const std::string &api) {
+  // Follow what build/soong/cc/gen_stub_libs.py does.
+  if (api == "current") {
+    return FUTURE_API;
+  }
+  return std::stoi(api);
+}
+
+bool VersionScriptParser::SymbolInArchAndApiVersion(const std::string &line,
+                                                    const std::string &arch,
+                                                    int api) {
+  // If the tags do not have an "introduced" requirement, the symbol is
+  // exported.
+  if (!StringContains(line, "introduced") && LineSatisfiesArch(line, arch)) {
+    return true;
+  }
+  if (line == "future") {
+    return api == FUTURE_API;
+  }
+  const std::string regex_match_string1 = " *introduced-" + arch + "=([0-9]+)";
+  const std::string regex_match_string2 = " *introduced=([0-9]+)";
+  std::smatch matcher1;
+  std::smatch matcher2;
+  std::regex match_clause1(regex_match_string1);
+  std::regex match_clause2(regex_match_string2);
+  int matched_api = -1;
+  if (std::regex_search(line, matcher1, match_clause1)) {
+    matched_api = std::stoi(matcher1.str(1));
+  } else if ((std::regex_search(line, matcher2, match_clause2)) &&
+    LineSatisfiesArch(line, arch)) {
+    matched_api = std::stoi(matcher2.str(1));
+  }
+  if ( matched_api > 0 && api >= matched_api) {
+    return true;
+  }
+  return false;
+}
+
+bool VersionScriptParser::SymbolExported(const std::string &line,
+                                         const std::string &arch, int api) {
+  // Empty line means that the symbol is exported
+  if (line.empty() || SymbolInArchAndApiVersion(line, arch, api)) {
+    return true;
+  }
+  return false;
+}
+
+bool VersionScriptParser::ParseSymbolLine(const std::string &line) {
+  //The symbol lies before the ; and the tags are after ;
+  std::string::size_type pos = line.find(";");
+  if (pos == std::string::npos) {
+    llvm::errs() << "Couldn't find end of symbol" << line <<"\n";
+    return false;
+  }
+  std::string symbol = line.substr(0, pos);
+  std::string::size_type last_space = symbol.find_last_of(' ');
+  symbol = symbol.substr(last_space + 1, pos);
+  std::string tags = line.substr(pos + 1);
+  if (SymbolExported(tags, arch_, api_)) {
+    if (StringContains(tags, "var")) {
+      globvars_.insert(symbol);
+    } else {
+      functions_.insert(symbol);
+    }
+  }
+  return true;
+}
+
+typedef VersionScriptParser::LineScope LineScope;
+
+LineScope VersionScriptParser::GetLineScope(std::string &line,
+                                            LineScope scope) {
+  if (StringContains(line, "local:")) {
+    scope = LineScope::local;
+  }
+  return scope;
+}
+
+bool VersionScriptParser::ParseInnerBlock(std::ifstream &symbol_ifstream) {
+  std::string line = "";
+  LineScope scope = LineScope::global;
+
+  while (std::getline(symbol_ifstream, line)) {
+    if (line.find("}") != std::string::npos) {
+      break;
+    }
+    if (line.c_str()[0] == '#') {
+      continue;
+    }
+    scope = GetLineScope(line, scope);
+    if (scope != LineScope::global || StringContains(line, "global:")) {
+      continue;
+    }
+    ParseSymbolLine(line);
+  }
+  return true;
+}
+
+const std::set<std::string> &VersionScriptParser::GetFunctions() {
+  return functions_;
+}
+
+const std::set<std::string> &VersionScriptParser::GetGlobVars() {
+  return globvars_;
+}
+
+bool VersionScriptParser::Parse() {
+  std::ifstream symbol_ifstream(version_script_);
+  if (!symbol_ifstream.is_open()) {
+    llvm::errs() << "Failed to open version script file\n";
+    return false;
+  }
+  std::string line = "";
+
+  while (std::getline(symbol_ifstream, line)) {
+    // Skip comment lines.
+    if (line.c_str()[0] == '#') {
+      continue;
+    }
+    if (StringContains(line, "{")) {
+
+      if ((StringContains(line, "PRIVATE"))) {
+        continue;
+      }
+      ParseInnerBlock(symbol_ifstream);
+    }
+  }
+  return true;
+}
+
+} // namespace abi_util
diff --git a/vndk/tools/header-checker/tests/input/test_version_script.map b/vndk/tools/header-checker/tests/input/test_version_script.map
new file mode 100644
index 0000000..92b1391
--- /dev/null
+++ b/vndk/tools/header-checker/tests/input/test_version_script.map
@@ -0,0 +1,18 @@
+LIBC {
+  global:
+    _Z10HiddenBase;
+    _Z6fooray; # introduced=23
+    _ZN5test35BeginET_T0_i; # introduced-arm=17 introduced-arm64=21 introduced-mips=17 introduced-mips64=21 introduced-x86=17 introduced-x86_64=21
+    _ZN5Stack4pushET_; # introduced-arm=17 introduced-arm64=21 introduced-mips=17 introduced-mips64=21 introduced-x86=17 introduced-x86_64=21
+    _ZN5test310double_byeE; # var introduced=23
+		local:
+    *;
+};
+
+LIBC_PRIVATE {
+  global:
+};
+
+LIBC_DEPRECATED {
+  global:
+};