[automerger skipped] Merge "Do not test ELF files that require special program interpreter" into pie-vts-dev
am: 106519795c -s ours
am skip reason: change_id I788aa564d132c3c62692a91ef5be7587a5ed4fae with SHA1 1d61b4b682 is in history

Change-Id: If94f33ae5497fa4d5e5b522adcf6a06b95d66d66
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..749ccda
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
diff --git a/abi/Android.mk b/abi/Android.mk
index 5a34733..98d81af 100644
--- a/abi/Android.mk
+++ b/abi/Android.mk
@@ -19,6 +19,5 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := VtsVndkAbi
-VTS_CONFIG_SRC_DIR := testcases/vndk/abi
 include test/vts/tools/build/Android.host_config.mk
 
diff --git a/abi/VtsVndkAbiTest.py b/abi/VtsVndkAbiTest.py
index 34f8ff5..89d12e2 100644
--- a/abi/VtsVndkAbiTest.py
+++ b/abi/VtsVndkAbiTest.py
@@ -15,6 +15,7 @@
 # limitations under the License.
 #
 
+import json
 import logging
 import os
 import shutil
@@ -22,34 +23,14 @@
 
 from vts.runners.host import asserts
 from vts.runners.host import base_test
-from vts.runners.host import const
 from vts.runners.host import keys
 from vts.runners.host import test_runner
+from vts.runners.host import utils
 from vts.testcases.vndk.golden import vndk_data
-from vts.utils.python.controllers import android_device
+from vts.utils.python.file import target_file_utils
 from vts.utils.python.library import elf_parser
-from vts.utils.python.library import vtable_parser
-
-
-def _IterateFiles(root_dir):
-    """A generator yielding relative and full paths in a directory.
-
-    Args:
-        root_dir: The directory to search.
-
-    Yields:
-        A tuple of (relative_path, full_path) for each regular file.
-        relative_path is the relative path to root_dir. full_path is the path
-        starting with root_dir.
-    """
-    for dir_path, dir_names, file_names in os.walk(root_dir):
-        if dir_path == root_dir:
-            rel_dir = ""
-        else:
-            rel_dir = os.path.relpath(dir_path, root_dir)
-        for file_name in file_names:
-            yield (os.path.join(rel_dir, file_name),
-                   os.path.join(dir_path, file_name))
+from vts.utils.python.library.vtable import vtable_dumper
+from vts.utils.python.vndk import vndk_utils
 
 
 class VtsVndkAbiTest(base_test.BaseTestClass):
@@ -61,12 +42,6 @@
         _vndk_version: String, the VNDK version supported by the device.
         data_file_path: The path to VTS data directory.
     """
-    _ODM_LIB_DIR_32 = "/odm/lib"
-    _ODM_LIB_DIR_64 = "/odm/lib64"
-    _VENDOR_LIB_DIR_32 = "/vendor/lib"
-    _VENDOR_LIB_DIR_64 = "/vendor/lib64"
-    _SYSTEM_LIB_DIR_32 = "/system/lib"
-    _SYSTEM_LIB_DIR_64 = "/system/lib64"
 
     def setUpClass(self):
         """Initializes data file path, device, and temporary directory."""
@@ -88,84 +63,170 @@
             target_dir: The directory to copy from device.
             host_dir: The directory to copy to host.
         """
-        test_cmd = "test -d " + target_dir
-        logging.info("adb shell %s", test_cmd)
-        result = self._dut.adb.shell(test_cmd, no_except=True)
-        if result[const.EXIT_CODE]:
+        if not target_file_utils.IsDirectory(target_dir, self._dut.shell):
             logging.info("%s doesn't exist. Create %s.", target_dir, host_dir)
-            os.mkdir(host_dir, 0750)
+            os.makedirs(host_dir)
             return
+        parent_dir = os.path.dirname(host_dir)
+        if parent_dir and not os.path.isdir(parent_dir):
+            os.makedirs(parent_dir)
         logging.info("adb pull %s %s", target_dir, host_dir)
         self._dut.adb.pull(target_dir, host_dir)
 
-    def _DiffSymbols(self, dump_path, lib_path):
+    def _ToHostPath(self, target_path):
+        """Maps target path to host path in self._temp_dir."""
+        return os.path.join(self._temp_dir, *target_path.strip("/").split("/"))
+
+    @staticmethod
+    def _LoadGlobalSymbolsFromDump(dump_obj):
+        """Loads global symbols from a dump object.
+
+        Args:
+            dump_obj: A dict, the dump in JSON format.
+
+        Returns:
+            A set of strings, the symbol names.
+        """
+        symbols = set()
+        for key in ("elf_functions", "elf_objects"):
+            symbols.update(
+                symbol.get("name", "") for symbol in dump_obj.get(key, []) if
+                symbol.get("binding", "global") == "global")
+        return symbols
+
+    def _DiffElfSymbols(self, dump_obj, parser):
         """Checks if a library includes all symbols in a dump.
 
         Args:
-            dump_path: The path to the dump file containing list of symbols.
-            lib_path: The path to the library.
+            dump_obj: A dict, the dump in JSON format.
+            parser: An elf_parser.ElfParser that loads the library.
 
         Returns:
             A list of strings, the global symbols that are in the dump but not
             in the library.
 
         Raises:
-            IOError if fails to load the dump.
             elf_parser.ElfError if fails to load the library.
         """
-        with open(dump_path, "r") as dump_file:
-            dump_symbols = set(line.strip() for line in dump_file
-                               if line.strip())
-        parser = elf_parser.ElfParser(lib_path)
-        try:
-            lib_symbols = parser.ListGlobalDynamicSymbols(include_weak=True)
-        finally:
-            parser.Close()
+        dump_symbols = self._LoadGlobalSymbolsFromDump(dump_obj)
+        lib_symbols = parser.ListGlobalDynamicSymbols(include_weak=True)
         return sorted(dump_symbols.difference(lib_symbols))
 
-    def _DiffVtables(self, dump_path, lib_path):
+    @staticmethod
+    def _DiffVtableComponent(offset, expected_symbol, vtable):
+        """Checks if a symbol is in a vtable entry.
+
+        Args:
+            offset: An integer, the offset of the expected symbol.
+            exepcted_symbol: A string, the name of the expected symbol.
+            vtable: A dict of {offset: [entry]} where offset is an integer and
+                    entry is an instance of vtable_dumper.VtableEntry.
+
+        Returns:
+            A list of strings, the actual possible symbols if expected_symbol
+            does not match the vtable entry.
+            None if expected_symbol matches the entry.
+        """
+        if offset not in vtable:
+            return []
+
+        entry = vtable[offset]
+        if not entry.names:
+            return [hex(entry.value).rstrip('L')]
+
+        if expected_symbol not in entry.names:
+            return entry.names
+
+    def _DiffVtableComponents(self, dump_obj, dumper):
         """Checks if a library includes all vtable entries in a dump.
 
         Args:
-            dump_path: The path to the dump file containing vtables.
-            lib_path: The path to the library.
+            dump_obj: A dict, the dump in JSON format.
+            dumper: An vtable_dumper.VtableDumper that loads the library.
 
         Returns:
-            A list of tuples (VTABLE, SYMBOL, EXPECTED_OFFSET, ACTUAL_OFFSET).
-            ACTUAL_OFFSET can be "missing" or numbers separated by comma.
+            A list of tuples (VTABLE, OFFSET, EXPECTED_SYMBOL, ACTUAL).
+            ACTUAL can be "missing", a list of symbol names, or an ELF virtual
+            address.
 
         Raises:
-            IOError if fails to load the dump.
-            vtable_parser.VtableError if fails to load the library.
+            vtable_dumper.VtableError if fails to dump vtable from the library.
         """
-        parser = vtable_parser.VtableParser(
-            os.path.join(self.data_file_path, "host"))
-        with open(dump_path, "r") as dump_file:
-            dump_vtables = parser.ParseVtablesFromString(dump_file.read())
+        function_kinds = [
+            "function_pointer",
+            "complete_dtor_pointer",
+            "deleting_dtor_pointer"
+        ]
+        non_function_kinds = [
+            "vcall_offset",
+            "vbase_offset",
+            "offset_to_top",
+            "rtti",
+            "unused_function_pointer"
+        ]
+        default_vtable_component_kind = "function_pointer"
 
-        lib_vtables = parser.ParseVtablesFromLibrary(lib_path)
-        # TODO(b/78316564): The dumper doesn't support SHT_ANDROID_RELA.
-        if not lib_vtables and self.run_as_compliance_test:
-            logging.warning("%s: Cannot dump vtables",
-                            os.path.relpath(lib_path, self._temp_dir))
-            return []
-        logging.debug("%s: %s", lib_path, lib_vtables)
-        diff = []
-        for vtable, dump_symbols in dump_vtables.iteritems():
-            lib_inv_vtable = dict()
-            if vtable in lib_vtables:
-                for off, sym in lib_vtables[vtable]:
-                    if sym not in lib_inv_vtable:
-                        lib_inv_vtable[sym] = [off]
-                    else:
-                        lib_inv_vtable[sym].append(off)
-            for off, sym in dump_symbols:
-                if sym not in lib_inv_vtable:
-                    diff.append((vtable, sym, str(off), "missing"))
-                elif off not in lib_inv_vtable[sym]:
-                    diff.append((vtable, sym, str(off),
-                                 ",".join(str(x) for x in lib_inv_vtable[sym])))
-        return diff
+        global_symbols = self._LoadGlobalSymbolsFromDump(dump_obj)
+
+        lib_vtables = {vtable.name: vtable
+                       for vtable in dumper.DumpVtables()}
+        logging.debug("\n\n".join(str(vtable)
+                                  for _, vtable in lib_vtables.iteritems()))
+
+        vtables_diff = []
+        for record_type in dump_obj.get("record_types", []):
+            type_name_symbol = record_type.get("unique_id", "")
+            vtable_symbol = type_name_symbol.replace("_ZTS", "_ZTV", 1)
+
+            # Skip if the vtable symbol isn't global.
+            if vtable_symbol not in global_symbols:
+                continue
+
+            # Collect vtable entries from library dump.
+            if vtable_symbol in lib_vtables:
+                lib_vtable = {entry.offset: entry
+                              for entry in lib_vtables[vtable_symbol].entries}
+            else:
+                lib_vtable = dict()
+
+            for index, entry in enumerate(record_type.get("vtable_components",
+                                                          [])):
+                entry_offset = index * int(self.abi_bitness) // 8
+                entry_kind = entry.get("kind", default_vtable_component_kind)
+                entry_symbol = entry.get("mangled_component_name", "")
+                entry_is_pure = entry.get("is_pure", False)
+
+                if entry_kind in non_function_kinds:
+                    continue
+
+                if entry_kind not in function_kinds:
+                    logging.warning("%s: Unexpected vtable entry kind %s",
+                                    vtable_symbol, entry_kind)
+
+                if entry_symbol not in global_symbols:
+                    # Itanium cxx abi doesn't specify pure virtual vtable
+                    # entry's behaviour. However we can still do some checks
+                    # based on compiler behaviour.
+                    # Even though we don't check weak symbols, we can still
+                    # issue a warning when a pure virtual function pointer
+                    # is missing.
+                    if entry_is_pure and entry_offset not in lib_vtable:
+                        logging.warning("%s: Expected pure virtual function"
+                                        "in %s offset %s",
+                                        vtable_symbol, vtable_symbol,
+                                        entry_offset)
+                    continue
+
+                diff_symbols = self._DiffVtableComponent(
+                    entry_offset, entry_symbol, lib_vtable)
+                if diff_symbols is None:
+                    continue
+
+                vtables_diff.append(
+                    (vtable_symbol, str(entry_offset), entry_symbol,
+                     (",".join(diff_symbols) if diff_symbols else "missing")))
+
+        return vtables_diff
 
     def _ScanLibDirs(self, dump_dir, lib_dirs, dump_version):
         """Compares dump files with libraries copied from device.
@@ -182,74 +243,70 @@
             An integer, number of incompatible libraries.
         """
         error_count = 0
-        symbol_dumps = dict()
-        vtable_dumps = dict()
+        dump_paths = dict()
         lib_paths = dict()
-        for dump_rel_path, dump_path in _IterateFiles(dump_dir):
-            if dump_rel_path.endswith("_symbol.dump"):
-                lib_name = dump_rel_path.rpartition("_symbol.dump")[0]
-                symbol_dumps[lib_name] = dump_path
-            elif dump_rel_path.endswith("_vtable.dump"):
-                lib_name = dump_rel_path.rpartition("_vtable.dump")[0]
-                vtable_dumps[lib_name] = dump_path
+        for parent_dir, dump_name in utils.iterate_files(dump_dir):
+            dump_path = os.path.join(parent_dir, dump_name)
+            if dump_path.endswith(".dump"):
+                lib_name = dump_name.rpartition(".dump")[0]
+                dump_paths[lib_name] = dump_path
             else:
                 logging.warning("Unknown dump: %s", dump_path)
-                continue
-            lib_paths[lib_name] = None
 
         for lib_dir in lib_dirs:
-            for lib_rel_path, lib_path in _IterateFiles(lib_dir):
-                try:
-                    vndk_dir = next(x for x in ("vndk", "vndk-sp") if
-                                    lib_rel_path.startswith(x + os.path.sep))
-                    lib_name = lib_rel_path.replace(
-                        vndk_dir, vndk_dir + "-" + dump_version, 1)
-                except StopIteration:
-                    lib_name = lib_rel_path
+            for parent_dir, lib_name in utils.iterate_files(lib_dir):
+                if lib_name not in lib_paths:
+                    lib_paths[lib_name] = os.path.join(parent_dir, lib_name)
 
-                if lib_name in lib_paths and not lib_paths[lib_name]:
-                    lib_paths[lib_name] = lib_path
-
-        for lib_name, lib_path in lib_paths.iteritems():
-            if not lib_path:
+        for lib_name, dump_path in dump_paths.iteritems():
+            if lib_name not in lib_paths:
                 logging.info("%s: Not found on target", lib_name)
                 continue
+            lib_path = lib_paths[lib_name]
             rel_path = os.path.relpath(lib_path, self._temp_dir)
 
             has_exception = False
             missing_symbols = []
             vtable_diff = []
-            # Compare symbols
-            if lib_name in symbol_dumps:
-                try:
-                    missing_symbols = self._DiffSymbols(
-                        symbol_dumps[lib_name], lib_path)
-                except (IOError, elf_parser.ElfError):
-                    logging.exception("%s: Cannot diff symbols", rel_path)
-                    has_exception = True
-            # Compare vtables
-            if lib_name in vtable_dumps:
-                try:
-                    vtable_diff = self._DiffVtables(
-                        vtable_dumps[lib_name], lib_path)
-                except (IOError, vtable_parser.VtableError):
-                    logging.exception("%s: Cannot diff vtables", rel_path)
-                    has_exception = True
+
+            try:
+                with open(dump_path, "r") as dump_file:
+                    dump_obj = json.load(dump_file)
+                with vtable_dumper.VtableDumper(lib_path) as dumper:
+                    missing_symbols = self._DiffElfSymbols(
+                        dump_obj, dumper)
+                    vtable_diff = self._DiffVtableComponents(
+                        dump_obj, dumper)
+            except (IOError,
+                    elf_parser.ElfError,
+                    vtable_dumper.VtableError) as e:
+                logging.exception("%s: Cannot diff ABI", rel_path)
+                has_exception = True
 
             if missing_symbols:
                 logging.error("%s: Missing Symbols:\n%s",
                               rel_path, "\n".join(missing_symbols))
             if vtable_diff:
                 logging.error("%s: Vtable Difference:\n"
-                              "vtable symbol expected actual\n%s",
+                              "vtable offset expected actual\n%s",
                               rel_path,
-                              "\n".join(" ".join(x) for x in vtable_diff))
-            if has_exception or missing_symbols or vtable_diff:
+                              "\n".join(" ".join(e) for e in vtable_diff))
+            if (has_exception or missing_symbols or vtable_diff):
                 error_count += 1
             else:
                 logging.info("%s: Pass", rel_path)
         return error_count
 
+    @staticmethod
+    def _GetLinkerSearchIndex(target_path):
+        """Returns the key for sorting linker search paths."""
+        index = 0
+        for prefix in ("/odm", "/vendor", "/system"):
+            if target_path.startswith(prefix):
+                return index
+            index += 1
+        return index
+
     def testAbiCompatibility(self):
         """Checks ABI compliance of VNDK libraries."""
         primary_abi = self._dut.getCpuAbiList()[0]
@@ -273,26 +330,20 @@
                 self._vndk_version, primary_abi, self.abi_bitness))
         logging.info("dump dir: %s", dump_dir)
 
-        odm_lib_dir = os.path.join(
-            self._temp_dir, "odm_lib_dir_" + self.abi_bitness)
-        vendor_lib_dir = os.path.join(
-            self._temp_dir, "vendor_lib_dir_" + self.abi_bitness)
-        system_lib_dir = os.path.join(
-            self._temp_dir, "system_lib_dir_" + self.abi_bitness)
-        logging.info("host lib dir: %s %s %s",
-                     odm_lib_dir, vendor_lib_dir, system_lib_dir)
-        self._PullOrCreateDir(
-            getattr(self, "_ODM_LIB_DIR_" + self.abi_bitness),
-            odm_lib_dir)
-        self._PullOrCreateDir(
-            getattr(self, "_VENDOR_LIB_DIR_" + self.abi_bitness),
-            vendor_lib_dir)
-        self._PullOrCreateDir(
-            getattr(self, "_SYSTEM_LIB_DIR_" + self.abi_bitness),
-            system_lib_dir)
+        target_vndk_dir = vndk_utils.GetVndkCoreDirectory(self.abi_bitness,
+                                                          self._vndk_version)
+        target_vndk_sp_dir = vndk_utils.GetVndkSpDirectory(self.abi_bitness,
+                                                           self._vndk_version)
+        target_dirs = vndk_utils.GetVndkExtDirectories(self.abi_bitness)
+        target_dirs += vndk_utils.GetVndkSpExtDirectories(self.abi_bitness)
+        target_dirs += [target_vndk_dir, target_vndk_sp_dir]
+        target_dirs.sort(key=self._GetLinkerSearchIndex)
 
-        error_count = self._ScanLibDirs(
-            dump_dir, [odm_lib_dir, vendor_lib_dir, system_lib_dir], dump_version)
+        host_dirs = [self._ToHostPath(x) for x in target_dirs]
+        for target_dir, host_dir in zip(target_dirs, host_dirs):
+            self._PullOrCreateDir(target_dir, host_dir)
+
+        error_count = self._ScanLibDirs(dump_dir, host_dirs, dump_version)
         asserts.assertEqual(error_count, 0,
                             "Total number of errors: " + str(error_count))
 
diff --git a/dependency/Android.mk b/dependency/Android.mk
index e527023..2c538db 100644
--- a/dependency/Android.mk
+++ b/dependency/Android.mk
@@ -21,7 +21,5 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := VtsVndkDependency
-
-VTS_CONFIG_SRC_DIR := testcases/vndk/dependency
 include test/vts/tools/build/Android.host_config.mk
 
diff --git a/dependency/VtsVndkDependencyTest.py b/dependency/VtsVndkDependencyTest.py
index 094d84a..4878978 100644
--- a/dependency/VtsVndkDependencyTest.py
+++ b/dependency/VtsVndkDependencyTest.py
@@ -111,11 +111,7 @@
 
         sp_hal_strings = vndk_lists[0]
         self._sp_hal = [re.compile(x) for x in sp_hal_strings]
-        (self._ll_ndk,
-         self._vndk,
-         self._vndk_sp) = (
-            set(path_utils.TargetBaseName(path) for path in vndk_list)
-            for vndk_list in vndk_lists[1:])
+        (self._ll_ndk, self._vndk, self._vndk_sp) = vndk_lists[1:]
 
         logging.debug("LL_NDK: %s", self._ll_ndk)
         logging.debug("SP_HAL: %s", sp_hal_strings)
diff --git a/files/VtsVndkFilesTest.py b/files/VtsVndkFilesTest.py
index 5f08ccf..d88cd8a 100644
--- a/files/VtsVndkFilesTest.py
+++ b/files/VtsVndkFilesTest.py
@@ -68,20 +68,21 @@
         return target_file_utils.FindFiles(self._shell, dir_path, "*",
                                            "! -type d")
 
-    def _TestVndkDirectory(self, vndk_dir, *vndk_list_names):
+    def _TestVndkDirectory(self, vndk_dir, vndk_list_names):
         """Verifies that the VNDK directory doesn't contain extra files.
 
         Args:
             vndk_dir: The path to the VNDK directory on device.
-            *vndk_list_names: Strings, the categories of the VNDK libraries
-                              that can be in the directory.
+            vndk_list_names: Strings, the categories of the VNDK libraries
+                             that can be in the directory.
         """
         vndk_lists = vndk_data.LoadVndkLibraryLists(
             self.data_file_path, self._vndk_version, *vndk_list_names)
         asserts.assertTrue(vndk_lists, "Cannot load VNDK library lists.")
         vndk_set = set().union(*vndk_lists)
         logging.debug("vndk set: %s", vndk_set)
-        unexpected = set(self._ListFiles(vndk_dir)) - vndk_set
+        unexpected = [x for x in self._ListFiles(vndk_dir) if
+                      path_utils.TargetBaseName(x) not in vndk_set]
         if unexpected:
             logging.error("Unexpected files:\n%s", "\n".join(unexpected))
             asserts.fail("Total number of errors: %d" % len(unexpected))
@@ -99,9 +100,7 @@
         vndk_lists = vndk_data.LoadVndkLibraryLists(
             self.data_file_path, self._vndk_version, *vndk_list_names)
         asserts.assertTrue(vndk_lists, "Cannot load VNDK library lists.")
-        vndk_set = set()
-        for vndk_list in vndk_lists:
-            vndk_set.update(path_utils.TargetBaseName(x) for x in vndk_list)
+        vndk_set = set().union(*vndk_lists)
         vndk_set.difference_update(except_libs)
         logging.debug("vndk set: %s", vndk_set)
         unexpected = [x for x in self._ListFiles(vndk_dir) if
@@ -117,16 +116,14 @@
         self._TestVndkDirectory(
             vndk_utils.GetVndkCoreDirectory(
                 self.abi_bitness, self._vndk_version),
-            vndk_data.VNDK,
-            vndk_data.VNDK_PRIVATE)
+            (vndk_data.VNDK, vndk_data.VNDK_PRIVATE,))
 
     def testVndkSpDirectory(self):
         """Verifies that VNDK-SP directory doesn't contain extra files."""
         self._TestVndkDirectory(
             vndk_utils.GetVndkSpDirectory(
                 self.abi_bitness, self._vndk_version),
-            vndk_data.VNDK_SP,
-            vndk_data.VNDK_SP_PRIVATE)
+            (vndk_data.VNDK_SP, vndk_data.VNDK_SP_PRIVATE,))
 
     def testNoLlndkInVendor(self):
         """Verifies that vendor partition has no LL-NDK libraries."""
diff --git a/golden/Android.bp b/golden/Android.bp
new file mode 100644
index 0000000..20222e1
--- /dev/null
+++ b/golden/Android.bp
@@ -0,0 +1,28 @@
+// 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.
+
+python_binary_host {
+    name: "extract_lsdump",
+    main: "extract_lsdump.py",
+    srcs: ["extract_lsdump.py"],
+    version: {
+        py2: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    }
+}
diff --git a/golden/dump_abi.py b/golden/dump_abi.py
index 71f9f00..a5767ae 100755
--- a/golden/dump_abi.py
+++ b/golden/dump_abi.py
@@ -16,52 +16,11 @@
 #
 
 import argparse
-import csv
-import importlib
 import os
 import subprocess
 import sys
 
-
-class ExternalModules(object):
-    """This class imports modules dynamically and keeps them as attributes.
-
-    Assume the user runs this script in the source directory. The VTS modules
-    are outside the search path and thus have to be imported dynamically.
-
-    Attribtues:
-        ar_parser: The ar_parser module.
-        elf_parser: The elf_parser module.
-        vtable_parser: The vtable_parser module.
-    """
-    @classmethod
-    def ImportParsers(cls, import_dir):
-        """Imports elf_parser and vtable_parser.
-
-        Args:
-            import_dir: The directory containing vts.utils.python.library.*.
-        """
-        sys.path.append(import_dir)
-        cls.ar_parser = importlib.import_module(
-            "vts.utils.python.library.ar_parser")
-        cls.elf_parser = importlib.import_module(
-            "vts.utils.python.library.elf_parser")
-        cls.vtable_parser = importlib.import_module(
-            "vts.utils.python.library.vtable_parser")
-
-
-def _CreateAndWrite(path, data):
-    """Creates directories on a file path and writes data to it.
-
-    Args:
-        path: The path to the file.
-        data: The data to write.
-    """
-    dir_name = os.path.dirname(path)
-    if dir_name and not os.path.exists(dir_name):
-        os.makedirs(dir_name)
-    with open(path, "w") as f:
-        f.write(data)
+import extract_lsdump
 
 
 def _ExecuteCommand(cmd, **kwargs):
@@ -103,130 +62,43 @@
     return [line.split("=", 1)[1].strip("'") for line in stdout.splitlines()]
 
 
-def FindBinary(file_name):
-    """Finds an executable binary in environment variable PATH.
+def _LoadLibraryNamesFromTxt(vndk_lib_list_file):
+    """Loads VNDK and VNDK-SP library names from a VNDK library list.
 
     Args:
-        file_name: The file name to find.
-
-    Returns:
-        A string which is the path to the binary.
-    """
-    return _ExecuteCommand(["which", file_name])
-
-
-def DumpSymbols(lib_path, dump_path, exclude_symbols):
-    """Dump symbols from a library to a dump file.
-
-    The dump file is a sorted list of symbols. Each line contains one symbol.
-
-    Args:
-        lib_path: The path to the library.
-        dump_path: The path to the dump file.
-        exclude_symbols: A set of strings, the symbols that should not be
-                         written to the dump file.
-
-    Returns:
-        A list of strings which are the symbols written to the dump file.
-
-    Raises:
-        elf_parser.ElfError if fails to load the library.
-        IOError if fails to write to the dump.
-    """
-    elf_parser = ExternalModules.elf_parser
-    parser = None
-    try:
-        parser = elf_parser.ElfParser(lib_path)
-        symbols = [x for x in parser.ListGlobalDynamicSymbols()
-                   if x not in exclude_symbols]
-    finally:
-        if parser:
-            parser.Close()
-    if symbols:
-        symbols.sort()
-        _CreateAndWrite(dump_path, "\n".join(symbols) + "\n")
-    return symbols
-
-
-def DumpVtables(lib_path, dump_path, dumper_dir, include_symbols):
-    """Dump vtables from a library to a dump file.
-
-    The dump file is the raw output of vndk-vtable-dumper.
-
-    Args:
-        lib_path: The path to the library.
-        dump_path: The path to the text file.
-        dumper_dir: The path to the directory containing the dumper executable
-                    and library.
-        include_symbols: A set of strings. A vtable is written to the dump file
-                         only if its symbol is in the set.
-
-    Returns:
-        A string which is the content written to the dump file.
-
-    Raises:
-        vtable_parser.VtableError if fails to load the library.
-        IOError if fails to write to the dump.
-    """
-    vtable_parser = ExternalModules.vtable_parser
-    parser = vtable_parser.VtableParser(dumper_dir)
-
-    def GenerateLines():
-        for line in parser.CallVtableDumper(lib_path).split("\n"):
-            parsed_lines.append(line)
-            yield line
-
-    lines = GenerateLines()
-    dump_lines = []
-    try:
-        while True:
-            parsed_lines = []
-            vtable, entries = parser.ParseOneVtable(lines)
-            if vtable in include_symbols:
-                dump_lines.extend(parsed_lines)
-    except StopIteration:
-        pass
-
-    dump_string = "\n".join(dump_lines).strip("\n")
-    if dump_string:
-        dump_string += "\n"
-        _CreateAndWrite(dump_path, dump_string)
-    return dump_string
-
-
-def _LoadLibraryNamesFromCsv(csv_file):
-    """Loads VNDK and VNDK-SP library names from an eligible list.
-
-    Args:
-        csv_file: A file object of eligible-list.csv.
+        vndk_lib_list_file: A file object of
+                            build/make/target/product/vndk/current.txt
 
     Returns:
         A list of strings, the VNDK and VNDK-SP library names with vndk/vndk-sp
         directory prefixes.
     """
-    lib_names = []
-    # Skip header
-    next(csv_file)
-    reader = csv.reader(csv_file)
-    for cells in reader:
-        if cells[1] not in ("VNDK", "VNDK-SP"):
-            continue
-        lib_name = os.path.normpath(cells[0]).replace("/system/${LIB}/", "", 1)
-        if not (lib_name.startswith("vndk") or lib_name.startswith("vndk-sp")):
-            continue
-        lib_name = lib_name.replace("${VNDK_VER}", "{VNDK_VER}")
-        lib_names.append(lib_name)
-    return lib_names
+    tags = (
+        ("VNDK-core: ", len("VNDK-core: "), False),
+        ("VNDK-SP: ", len("VNDK-SP: "), False),
+        ("VNDK-private: ", len("VNDK-private: "), True),
+        ("VNDK-SP-private: ", len("VNDK-SP-private: "), True),
+    )
+    lib_names = set()
+    lib_names_exclude = set()
+    for line in vndk_lib_list_file:
+        for tag, tag_len, is_exclude in tags:
+            if line.startswith(tag):
+                lib_name = line[tag_len:].strip()
+                if is_exclude:
+                    lib_names_exclude.add(lib_name)
+                else:
+                    lib_names.add(lib_name)
+    return sorted(lib_names - lib_names_exclude)
 
 
 def _LoadLibraryNames(file_names):
     """Loads library names from files.
 
-    Each element in the input list can be a .so file, a text file, or a .csv
-    file. The returned list consists of:
+    Each element in the input list can be a .so file or a .txt file. The
+    returned list consists of:
     - The .so file names in the input list.
-    - The non-empty lines in the text files.
-    - The libraries tagged with VNDK or VNDK-SP in the CSV.
+    - The libraries tagged with VNDK-core or VNDK-SP in the .txt file.
 
     Args:
         file_names: A list of strings, the library or text file names.
@@ -239,143 +111,197 @@
     for file_name in file_names:
         if file_name.endswith(".so"):
             lib_names.append(file_name)
-        elif file_name.endswith(".csv"):
-            with open(file_name, "r") as csv_file:
-                lib_names.extend(_LoadLibraryNamesFromCsv(csv_file))
         else:
-            with open(file_name, "r") as lib_list:
-                lib_names.extend(line.strip() for line in lib_list
-                                 if line.strip())
+            with open(file_name, "r") as txt_file:
+                lib_names.extend(_LoadLibraryNamesFromTxt(txt_file))
     return lib_names
 
 
-def DumpAbi(output_dir, lib_names, lib_dir, object_dir, dumper_dir):
-    """Generates dump from libraries.
+def DumpAbi(output_dir, lib_names, lsdump_path):
+    """Generates ABI dumps from library lsdumps.
 
     Args:
         output_dir: The output directory of dump files.
         lib_names: The names of the libraries to dump.
-        lib_dir: The path to the directory containing the libraries to dump.
-        object_dir: The path to the directory containing intermediate objects.
-        dumper_dir: The path to the directory containing the vtable dumper
-                    executable and library.
+        lsdump_path: The path to the directory containing lsdumps.
 
     Returns:
-        A list of strings, the paths to the libraries not found in lib_dir.
+        A list of strings, the libraries whose ABI dump fails to be created.
     """
-    ar_parser = ExternalModules.ar_parser
-    static_symbols = set()
-    for ar_name in ("libgcc", "libatomic", "libcompiler_rt-extras"):
-        ar_path = os.path.join(
-            object_dir, "STATIC_LIBRARIES", ar_name + "_intermediates",
-            ar_name + ".a")
-        static_symbols.update(ar_parser.ListGlobalSymbols(ar_path))
-
-    missing_libs = []
-    dump_dir = os.path.join(output_dir, os.path.basename(lib_dir))
+    missing_dumps = []
     for lib_name in lib_names:
-        lib_path = os.path.join(lib_dir, lib_name)
-        symbol_dump_path = os.path.join(dump_dir, lib_name + "_symbol.dump")
-        vtable_dump_path = os.path.join(dump_dir, lib_name + "_vtable.dump")
-        print(lib_path)
-        if not os.path.isfile(lib_path):
-            missing_libs.append(lib_path)
-            print("Warning: Not found")
-            print("")
-            continue
-        symbols = DumpSymbols(lib_path, symbol_dump_path, static_symbols)
-        if symbols:
-            print("Output: " + symbol_dump_path)
+        dump_path = os.path.join(output_dir, lib_name + '.abi.dump')
+        lib_lsdump_path = os.path.join(lsdump_path, lib_name + '.lsdump')
+        if os.path.isfile(lib_lsdump_path + '.gz'):
+            lib_lsdump_path += '.gz'
+
+        print(lib_lsdump_path)
+        try:
+            extract_lsdump.ParseLsdumpFile(lib_lsdump_path, dump_path)
+        except extract_lsdump.LsdumpError as e:
+            missing_dumps.append(lib_name)
+            print(e)
         else:
-            print("No symbols")
-        vtables = DumpVtables(
-            lib_path, vtable_dump_path, dumper_dir, set(symbols))
-        if vtables:
-            print("Output: " + vtable_dump_path)
-        else:
-            print("No vtables")
-        print("")
-    return missing_libs
+            print('Output: ' + dump_path)
+        print('')
+    return missing_dumps
+
+
+def _GetTargetArchDir(target_arch, target_arch_variant):
+    if target_arch == target_arch_variant:
+        return target_arch
+    return '{}_{}'.format(target_arch, target_arch_variant)
+
+
+def _GetAbiBitnessFromArch(target_arch):
+    arch_bitness = {
+        'arm': '32',
+        'arm64': '64',
+        'x86': '32',
+        'x86_64': '64',
+    }
+    return arch_bitness[target_arch]
 
 
 def main():
     # Parse arguments
-    arg_parser = argparse.ArgumentParser()
+    description = (
+        'Generates VTS VNDK ABI test abidumps from lsdump. '
+        'Option values are read from build variables if no value is given. '
+        'If none of the options are specified, then abidumps for target second '
+        'arch are also generated.'
+    )
+    arg_parser = argparse.ArgumentParser(description=description)
     arg_parser.add_argument("file", nargs="*",
                             help="the libraries to dump. Each file can be "
-                                 ".so, text, or .csv. The text file contains "
-                                 "a list of paths. The CSV file follows the"
-                                 "format of eligible list output from VNDK"
-                                 "definition tool. {VNDK_VER} in the library"
-                                 "paths is replaced with "
-                                 "\"-\" + PLATFORM_VNDK_VERSION.")
-    arg_parser.add_argument("--dumper-dir", "-d", action="store",
-                            help="the path to the directory containing "
-                                 "bin/vndk-vtable-dumper.")
-    arg_parser.add_argument("--import-path", "-i", action="store",
-                            help="the directory for VTS python modules. "
-                                 "Default value is $ANDROID_BUILD_TOP/test")
+                                 ".so or .txt. The text file can be found at "
+                                 "build/make/target/product/vndk/current.txt.")
     arg_parser.add_argument("--output", "-o", action="store",
                             help="output directory for ABI reference dump. "
                                  "Default value is PLATFORM_VNDK_VERSION.")
+    arg_parser.add_argument('--platform-vndk-version',
+                            help='platform VNDK version. '
+                                 'Default value is PLATFORM_VNDK_VERSION.')
+    arg_parser.add_argument('--binder-bitness',
+                            choices=['32', '64'],
+                            help='bitness of binder interface. '
+                                 'Default value is 32 if BINDER32BIT is set '
+                                 'else is 64.')
+    arg_parser.add_argument('--target-main-arch',
+                            choices=['arm', 'arm64', 'x86', 'x86_64'],
+                            help='main CPU arch of the device. '
+                                 'Default value is TARGET_ARCH.')
+    arg_parser.add_argument('--target-arch',
+                            choices=['arm', 'arm64', 'x86', 'x86_64'],
+                            help='CPU arch of the libraries to dump. '
+                                 'Default value is TARGET_ARCH.')
+    arg_parser.add_argument('--target-arch-variant',
+                            help='CPU arch variant of the libraries to dump. '
+                                 'Default value is TARGET_ARCH_VARIANT.')
+
     args = arg_parser.parse_args()
 
-    # Get target architectures
     build_top_dir = os.getenv("ANDROID_BUILD_TOP")
     if not build_top_dir:
         sys.exit("env var ANDROID_BUILD_TOP is not set")
 
-    (binder_32_bit,
-     vndk_version,
-     target_arch,
-     target_2nd_arch) = GetBuildVariables(
-        build_top_dir, abs_path=False, vars=(
-            "BINDER32BIT",
-            "PLATFORM_VNDK_VERSION",
-            "TARGET_ARCH",
-            "TARGET_2ND_ARCH"))
+    # If some options are not specified, read build variables as default values.
+    if not all([args.platform_vndk_version,
+                args.binder_bitness,
+                args.target_main_arch,
+                args.target_arch,
+                args.target_arch_variant]):
+        [platform_vndk_version,
+         binder_32_bit,
+         target_arch,
+         target_arch_variant,
+         target_2nd_arch,
+         target_2nd_arch_variant] = GetBuildVariables(
+            build_top_dir,
+            False,
+            ['PLATFORM_VNDK_VERSION',
+             'BINDER32BIT',
+             'TARGET_ARCH',
+             'TARGET_ARCH_VARIANT',
+             'TARGET_2ND_ARCH',
+             'TARGET_2ND_ARCH_VARIANT']
+        )
+        target_main_arch = target_arch
+        binder_bitness = '32' if binder_32_bit else '64'
 
-    (target_lib_dir,
-     target_obj_dir,
-     target_2nd_lib_dir,
-     target_2nd_obj_dir) = GetBuildVariables(
-        build_top_dir, abs_path=True, vars=(
-            "TARGET_OUT_SHARED_LIBRARIES",
-            "TARGET_OUT_INTERMEDIATES",
-            "2ND_TARGET_OUT_SHARED_LIBRARIES",
-            "2ND_TARGET_OUT_INTERMEDIATES"))
+    if args.platform_vndk_version:
+        platform_vndk_version = args.platform_vndk_version
 
-    # Import elf_parser and vtable_parser
-    ExternalModules.ImportParsers(args.import_path if args.import_path else
-                                  os.path.join(build_top_dir, "test"))
+    if args.binder_bitness:
+        binder_bitness = args.binder_bitness
 
-    # Find vtable dumper
-    if args.dumper_dir:
-        dumper_dir = args.dumper_dir
-    else:
-        dumper_path = FindBinary(
-            ExternalModules.vtable_parser.VtableParser.VNDK_VTABLE_DUMPER)
-        dumper_dir = os.path.dirname(os.path.dirname(dumper_path))
-    print("DUMPER_DIR=" + dumper_dir)
+    if args.target_main_arch:
+        target_main_arch = args.target_main_arch
 
-    output_dir = os.path.join((args.output if args.output else vndk_version),
-                              ("binder32" if binder_32_bit else "binder64"),
-                              target_arch)
-    print("OUTPUT_DIR=" + output_dir)
+    if args.target_arch:
+        target_arch = args.target_arch
 
-    lib_names = [name.format(VNDK_VER="-" + vndk_version) for
-                 name in _LoadLibraryNames(args.file)]
+    if args.target_arch_variant:
+        target_arch_variant = args.target_arch_variant
 
-    missing_libs = DumpAbi(output_dir, lib_names, target_lib_dir,
-                           target_obj_dir, dumper_dir)
-    if target_2nd_arch:
-        missing_libs += DumpAbi(output_dir, lib_names, target_2nd_lib_dir,
-                                target_2nd_obj_dir, dumper_dir)
+    dump_targets = [(platform_vndk_version,
+                     binder_bitness,
+                     target_main_arch,
+                     target_arch,
+                     target_arch_variant)]
 
-    if missing_libs:
-        print("Warning: Could not find libraries:")
-        for lib_path in missing_libs:
-            print(lib_path)
+    # If all options are not specified, then also create dump for 2nd arch.
+    if not any([args.platform_vndk_version,
+                args.binder_bitness,
+                args.target_main_arch,
+                args.target_arch,
+                args.target_arch_variant]):
+        dump_targets.append((platform_vndk_version,
+                             binder_bitness,
+                             target_main_arch,
+                             target_2nd_arch,
+                             target_2nd_arch_variant))
+
+    for target_tuple in dump_targets:
+        (platform_vndk_version,
+         binder_bitness,
+         target_main_arch,
+         target_arch,
+         target_arch_variant) = target_tuple
+
+        # Determine abi_bitness from target architecture
+        abi_bitness = _GetAbiBitnessFromArch(target_arch)
+
+        # Generate ABI dump from lsdump in TOP/prebuilts/abi-dumps
+        lsdump_path = os.path.join(
+            build_top_dir,
+            'prebuilts',
+            'abi-dumps',
+            'vndk',
+            platform_vndk_version,
+            binder_bitness,
+            _GetTargetArchDir(target_arch, target_arch_variant),
+            'source-based')
+        if not os.path.exists(lsdump_path):
+            print('Warning: lsdump path does not exist: ' + lsdump_path)
+            print('No abidump created.')
+            continue
+
+        output_dir = os.path.join(
+            args.output if args.output else platform_vndk_version,
+            'binder' + binder_bitness,
+            target_main_arch,
+            'lib64' if abi_bitness == '64' else 'lib')
+        print("OUTPUT_DIR=" + output_dir)
+
+        lib_names = _LoadLibraryNames(args.file)
+
+        missing_dumps = DumpAbi(output_dir, lib_names, lsdump_path)
+
+        if missing_dumps:
+            print('Warning: Fails to create ABI dumps for libraries:')
+            for lib_name in missing_dumps:
+                print(lib_name)
 
 
 if __name__ == "__main__":
diff --git a/golden/extract_lsdump.py b/golden/extract_lsdump.py
new file mode 100755
index 0000000..45e9c38
--- /dev/null
+++ b/golden/extract_lsdump.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+import argparse
+import gzip
+import json
+import os
+
+
+class LsdumpError(Exception):
+    """The exception raised by ParseLsdumpFile."""
+    pass
+
+
+class AttrDict(dict):
+    """A dictionary with attribute accessors."""
+
+    def __getattr__(self, key):
+        """Returns self[key]."""
+        try:
+            return self[key]
+        except KeyError:
+            raise AttributeError('Failed to get attribute: %s' % key)
+
+    def __setattr__(self, key, value):
+        """Assigns value to self[key]."""
+        self[key] = value
+
+
+def _OpenFileOrGzipped(file_name):
+    """Opens a file that is either in gzip or uncompressed format.
+
+    If file_name ends with '.gz' then return gzip.open(file_name, 'rb'),
+    else return open(file_name, 'rb').
+
+    Args:
+        file_name: The file name to open.
+
+    Returns:
+        A file object.
+
+    Raises:
+        IOError if fails to open.
+    """
+    if file_name.endswith('.gz'):
+        return gzip.open(file_name, 'rb')
+    return open(file_name, 'rb')
+
+
+def _ConsumeOffset(tok, beg=0):
+    """Consumes a <offset-number> in a thunk symbol."""
+    pos = tok.find('_', beg) + 1
+    return tok[:pos], tok[pos:]
+
+
+def _ConsumeCallOffset(tok):
+    """Consumes a <call-offset> in a thunk symbol."""
+    if tok[:1] == 'h':
+        lhs, rhs = _ConsumeOffset(tok, 1)
+    elif tok[:1] == 'v':
+        lhs, rhs = _ConsumeOffset(tok, 1)
+        lhs2, rhs = _ConsumeOffset(rhs)
+        if lhs and lhs2:
+            lhs = lhs + lhs2
+        else:
+            lhs, rhs = '', tok
+    else:
+        lhs, rhs = '', tok
+    return lhs, rhs
+
+
+def _FindThunkTarget(name):
+    """Finds thunk symbol's target function.
+
+    <thunk-symbol> ::= _ZT <call-offset> <base-encoding>
+                     | _ZTc <call-offset> <call-offset> <base-encoding>
+    <call-offset>  ::= h <nv-offset>
+                     | v <v-offset>
+    <nv-offset>    ::= <offset-number> _
+    <v-offset>     ::= <offset-number> _ <offset-number> _
+
+    Args:
+        name: A string, the symbol name to resolve.
+
+    Returns:
+        A string, symbol name of the nominal target function.
+        The input symbol name if it is not a thunk symbol.
+    """
+    if name.startswith('_ZTh') or name.startswith('_ZTv'):
+        lhs, rhs = _ConsumeCallOffset(name[len('_ZT'):])
+        if lhs:
+            return '_Z' + rhs
+    if name.startswith('_ZTc'):
+        lhs, rhs = _ConsumeCallOffset(name[len('_ZTc'):])
+        lhs2, rhs = _ConsumeCallOffset(rhs)
+        if lhs and lhs2:
+            return '_Z' + rhs
+    return name
+
+
+def _FilterElfFunctions(lsdump):
+    """Finds exported functions and thunks in lsdump.
+
+    Args:
+        lsdump: An AttrDict object containing the lsdump.
+
+    Yields:
+        The AttrDict objects in lsdump.elf_functions.
+    """
+    functions = {function.linker_set_key for function in lsdump.functions}
+
+    for elf_function in lsdump.elf_functions:
+        if elf_function.name in functions:
+            yield elf_function
+        elif _FindThunkTarget(elf_function.name) in functions:
+            yield elf_function
+
+
+def _FilterElfObjects(lsdump):
+    """Finds exported variables, type info, and vtables in lsdump.
+
+    Args:
+        lsdump: An AttrDict object containing the lsdump.
+
+    Yields:
+        The AttrDict objects in lsdump.elf_objects.
+    """
+    global_vars = {global_var.linker_set_key
+                   for global_var in lsdump.global_vars}
+    record_names = {record_type.unique_id[len('_ZTS'):]
+                    for record_type in lsdump.record_types}
+
+    for elf_object in lsdump.elf_objects:
+        name = elf_object.name
+        if name in global_vars:
+            yield elf_object
+        elif (name[:len('_ZTS')] in {'_ZTV', '_ZTT', '_ZTI', '_ZTS'} and
+                name[len('_ZTS'):] in record_names):
+            yield elf_object
+
+
+def _ParseSymbolsFromLsdump(lsdump, output_dump):
+    """Parses symbols from an lsdump.
+
+    Args:
+        lsdump: An AttrDict object containing the lsdump.
+        output_dump: An AttrDict object containing the output.
+    """
+    output_dump.elf_functions = list(_FilterElfFunctions(lsdump))
+    output_dump.elf_objects = list(_FilterElfObjects(lsdump))
+
+
+def _ParseVtablesFromLsdump(lsdump, output_dump):
+    """Parses vtables from an lsdump.
+
+    Args:
+        lsdump: An AttrDict object containing the lsdump.
+        output_dump: An AttrDict object containing the output.
+    """
+    vtable_symbols = {elf_object.name for elf_object in lsdump.elf_objects
+                      if elf_object.name.startswith('_ZTV')}
+
+    output_dump.record_types = []
+    for lsdump_record_type in lsdump.record_types:
+        type_symbol = lsdump_record_type.unique_id
+        vtable_symbol = '_ZTV' + type_symbol[len('_ZTS'):]
+        if vtable_symbol not in vtable_symbols:
+            continue
+        record_type = AttrDict()
+        record_type.unique_id = lsdump_record_type.unique_id
+        record_type.vtable_components = lsdump_record_type.vtable_components
+        output_dump.record_types.append(record_type)
+
+
+def ParseLsdumpFile(input_path, output_path):
+    """Converts an lsdump file to a dump file for the ABI test.
+
+    Args:
+        input_path: The path to the (gzipped) lsdump file.
+        output_path: The path to the output dump file.
+
+    Raises:
+        LsdumpError if fails to create the dump file.
+    """
+    try:
+        with _OpenFileOrGzipped(input_path) as lsdump_file:
+            lsdump = json.load(lsdump_file, object_hook=AttrDict)
+    except (IOError, ValueError) as e:
+        raise LsdumpError(e)
+
+    try:
+        output_dump = AttrDict()
+        _ParseVtablesFromLsdump(lsdump, output_dump)
+        _ParseSymbolsFromLsdump(lsdump, output_dump)
+    except AttributeError as e:
+        raise LsdumpError(e)
+
+    abs_output_path = os.path.abspath(output_path)
+    abs_output_dir = os.path.dirname(abs_output_path)
+
+    try:
+        if abs_output_dir and not os.path.exists(abs_output_dir):
+            os.makedirs(abs_output_dir)
+        with open(output_path, 'wb') as output_file:
+            json.dump(output_dump, output_file,
+                      indent=1, separators=(',', ':'))
+    except (IOError, OSError) as e:
+        raise LsdumpError(e)
+
+
+def main():
+    arg_parser = argparse.ArgumentParser(
+        description='This script converts an lsdump file to a dump file for '
+                    'the ABI test in VTS.')
+    arg_parser.add_argument('input_path',
+                            help='input lsdump file path.')
+    arg_parser.add_argument('output_path',
+                            help='output dump file path.')
+    args = arg_parser.parse_args()
+
+    try:
+        ParseLsdumpFile(args.input_path, args.output_path)
+    except LsdumpError as e:
+        print(e)
+        exit(1)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/golden/vndk_data.py b/golden/vndk_data.py
index 014453d..e215a91 100644
--- a/golden/vndk_data.py
+++ b/golden/vndk_data.py
@@ -14,32 +14,32 @@
 # limitations under the License.
 #
 
-import csv
+import collections
 import logging
 import os
 
-# The tags in VNDK spreadsheet:
+# The tags in VNDK list:
 # Low-level NDK libraries that can be used by framework and vendor modules.
-LL_NDK = "LL-NDK"
+LL_NDK = "LLNDK"
 
 # LL-NDK dependencies that vendor modules cannot directly access.
-LL_NDK_PRIVATE = "LL-NDK-Private"
+LL_NDK_PRIVATE = "LLNDK-private"
 
 # Same-process HAL implementation in vendor partition.
 SP_HAL = "SP-HAL"
 
 # Framework libraries that can be used by vendor modules except same-process HAL
 # and its dependencies in vendor partition.
-VNDK = "VNDK"
+VNDK = "VNDK-core"
 
 # VNDK dependencies that vendor modules cannot directly access.
-VNDK_PRIVATE = "VNDK-Private"
+VNDK_PRIVATE = "VNDK-core-private"
 
 # Same-process HAL dependencies in framework.
 VNDK_SP = "VNDK-SP"
 
 # VNDK-SP dependencies that vendor modules cannot directly access.
-VNDK_SP_PRIVATE = "VNDK-SP-Private"
+VNDK_SP_PRIVATE = "VNDK-SP-private"
 
 # The ABI dump directories. 64-bit comes before 32-bit in order to sequentially
 # search for longest prefix.
@@ -48,6 +48,8 @@
 # The data directory.
 _GOLDEN_DIR = os.path.join("vts", "testcases", "vndk", "golden")
 
+# Regular expression prefix for library name patterns.
+_REGEX_PREFIX = "[regex]"
 
 def LoadDefaultVndkVersion(data_file_path):
     """Loads the name of the data directory for devices with no VNDK version.
@@ -106,6 +108,52 @@
     return dump_dir
 
 
+def _LoadVndkLibraryListsFile(vndk_lists, tags, vndk_lib_list_path):
+    """Load VNDK libraries from the file to the specified tuple.
+
+    Args:
+        vndk_lists: The output tuple of lists containing library names.
+        tags: Strings, the tags of the libraries to find.
+        vndk_lib_list_path: The path to load the VNDK library list.
+    """
+
+    lib_sets = collections.defaultdict(set)
+
+    # Load VNDK tags from the list.
+    with open(vndk_lib_list_path) as vndk_lib_list_file:
+        for line in vndk_lib_list_file:
+            # Ignore comments.
+            if line.startswith('#'):
+                continue
+
+            # Split columns.
+            cells = line.split(': ', 1)
+            if len(cells) < 2:
+                continue
+            tag = cells[0]
+            lib_name = cells[1].strip()
+
+            lib_sets[tag].add(lib_name)
+
+    # Compute VNDK-core-private and VNDK-SP-private.
+    private = lib_sets.get('VNDK-private', set())
+
+    lib_sets[LL_NDK_PRIVATE].update(lib_sets[LL_NDK] & private)
+    lib_sets[VNDK_PRIVATE].update(lib_sets[VNDK] & private)
+    lib_sets[VNDK_SP_PRIVATE].update(lib_sets[VNDK_SP] & private)
+
+    lib_sets[LL_NDK].difference_update(private)
+    lib_sets[VNDK].difference_update(private)
+    lib_sets[VNDK_SP].difference_update(private)
+
+    # Update the output entries.
+    for index, tag in enumerate(tags):
+        for lib_name in lib_sets.get(tag, tuple()):
+            if lib_name.startswith(_REGEX_PREFIX):
+                lib_name = lib_name[len(_REGEX_PREFIX):]
+            vndk_lists[index].append(lib_name)
+
+
 def LoadVndkLibraryLists(data_file_path, version, *tags):
     """Find the VNDK libraries with specific tags.
 
@@ -125,25 +173,20 @@
     if not version_dir:
         return None
 
-    path = os.path.join(
-        data_file_path, _GOLDEN_DIR, version_dir, "eligible-list.csv")
-    if not os.path.isfile(path):
-        logging.warning("Cannot load %s.", path)
+    vndk_lib_list_path = os.path.join(
+        data_file_path, _GOLDEN_DIR, version_dir, "vndk-lib-list.txt")
+    if not os.path.isfile(vndk_lib_list_path):
+        logging.warning("Cannot load %s.", vndk_lib_list_path)
         return None
 
-    dir_suffix = "-" + version if version else ""
+    vndk_lib_extra_list_path = os.path.join(
+        data_file_path, _GOLDEN_DIR, version_dir, "vndk-lib-extra-list.txt")
+    if not os.path.isfile(vndk_lib_extra_list_path):
+        logging.warning("Cannot load %s.", vndk_lib_extra_list_path)
+        return None
+
     vndk_lists = tuple([] for x in tags)
-    with open(path) as csv_file:
-        # Skip header
-        next(csv_file)
-        reader = csv.reader(csv_file)
-        for cells in reader:
-            for tag_index, tag in enumerate(tags):
-                if tag == cells[1]:
-                    lib_name = cells[0].replace("${VNDK_VER}", dir_suffix)
-                    if lib_name.startswith("[regex]"):
-                        lib_name = lib_name[len("[regex]"):]
-                    vndk_lists[tag_index].extend(
-                        lib_name.replace("${LIB}", lib)
-                        for lib in ("lib", "lib64"))
+
+    _LoadVndkLibraryListsFile(vndk_lists, tags, vndk_lib_list_path)
+    _LoadVndkLibraryListsFile(vndk_lists, tags, vndk_lib_extra_list_path)
     return vndk_lists
diff --git a/open_libraries/VtsVndkOpenLibrariesTest.py b/open_libraries/VtsVndkOpenLibrariesTest.py
index f51502d..8dbfda6 100644
--- a/open_libraries/VtsVndkOpenLibrariesTest.py
+++ b/open_libraries/VtsVndkOpenLibrariesTest.py
@@ -23,6 +23,7 @@
 from vts.runners.host import keys
 from vts.runners.host import test_runner
 from vts.testcases.vndk.golden import vndk_data
+from vts.utils.python.os import path_utils
 from vts.utils.python.vndk import vndk_utils
 
 
@@ -110,19 +111,20 @@
             vndk_data.VNDK_SP,
             vndk_data.VNDK_SP_PRIVATE)
         asserts.assertTrue(vndk_lists, "Cannot load VNDK library lists.")
-        allowed_libs = set()
-        for vndk_list in vndk_lists:
-            allowed_libs.update(vndk_list)
+        allowed_libs = set().union(*vndk_lists)
         logging.debug("Allowed system libraries: %s", allowed_libs)
 
         asserts.assertTrue(self._dut.isAdbRoot,
                            "Must be root to find all libraries in use.")
         cmds = self._ListProcessCommands(lambda x: (x.startswith("/odm/") or
                                                     x.startswith("/vendor/")))
-        deps = self._ListOpenFiles(cmds.keys(),
-                                   lambda x: (x.startswith("/system/") and
-                                              x.endswith(".so") and
-                                              x not in allowed_libs))
+
+        def _IsDisallowedSystemLib(lib_path):
+            return (lib_path.startswith("/system/") and
+                    lib_path.endswith(".so") and
+                    path_utils.TargetBaseName(lib_path) not in allowed_libs)
+
+        deps = self._ListOpenFiles(cmds.keys(), _IsDisallowedSystemLib)
         if deps:
             error_lines = ["%s %s %s" % (pid, cmds[pid], libs)
                            for pid, libs in deps.iteritems()]