summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format13
-rw-r--r--OWNERS3
-rw-r--r--PREUPLOAD.cfg6
-rw-r--r--dependency/VtsVndkDependencyTest.py116
-rw-r--r--dependency/elf_parser.py275
-rwxr-xr-xgolden/dump_abi.py260
6 files changed, 335 insertions, 338 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..6027082
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,13 @@
+---
+Language: Cpp
+BasedOnStyle: Google
+ColumnLimit: 80
+IndentWidth: 2
+ContinuationIndentWidth: 4
+---
+Language: Java
+BasedOnStyle: Google
+ColumnLimit: 100
+IndentWidth: 4
+ContinuationIndentWidth: 8
+...
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..2d9b085
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+hsinyichen@google.com
+yim@google.com
+yuexima@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..6be6e5f
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,6 @@
+[Builtin Hooks]
+clang_format = true
+
+[Builtin Hooks Options]
+clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp,java
+
diff --git a/dependency/VtsVndkDependencyTest.py b/dependency/VtsVndkDependencyTest.py
index 9c52276..4ccea2a 100644
--- a/dependency/VtsVndkDependencyTest.py
+++ b/dependency/VtsVndkDependencyTest.py
@@ -26,9 +26,9 @@ from vts.runners.host import base_test
from vts.runners.host import test_runner
from vts.runners.host import utils
from vts.utils.python.controllers import android_device
-from vts.utils.python.file import file_utils
+from vts.utils.python.file import target_file_utils
+from vts.utils.python.library import elf_parser
from vts.utils.python.os import path_utils
-from vts.testcases.vndk.dependency import elf_parser
class VtsVndkDependencyTest(base_test.BaseTestClass):
@@ -52,45 +52,28 @@ class VtsVndkDependencyTest(base_test.BaseTestClass):
# copied from development/vndk/tools/definition-tool/vndk_definition_tool.py
_LOW_LEVEL_NDK = [
- "libandroid_net.so",
- "libc.so",
- "libdl.so",
- "liblog.so",
- "libm.so",
- "libstdc++.so",
- "libvndksupport.so",
- "libz.so"
+ "libandroid_net.so", "libc.so", "libdl.so", "liblog.so", "libm.so",
+ "libstdc++.so", "libvndksupport.so", "libz.so"
+ ]
+ _SAME_PROCESS_HAL = [
+ re.compile(p)
+ for p in [
+ "android\\.hardware\\.graphics\\.mapper@\\d+\\.\\d+-impl\\.so$",
+ "gralloc\\..*\\.so$", "libEGL_.*\\.so$", "libGLES_.*\\.so$",
+ "libGLESv1_CM_.*\\.so$", "libGLESv2_.*\\.so$",
+ "libGLESv3_.*\\.so$", "libPVRRS\\.so$", "libRSDriver.*\\.so$",
+ "vulkan.*\\.so$"
+ ]
]
- _SAME_PROCESS_HAL = [re.compile(p) for p in [
- "android\\.hardware\\.graphics\\.mapper@\\d+\\.\\d+-impl\\.so$",
- "gralloc\\..*\\.so$",
- "libEGL_.*\\.so$",
- "libGLES_.*\\.so$",
- "libGLESv1_CM_.*\\.so$",
- "libGLESv2_.*\\.so$",
- "libGLESv3_.*\\.so$",
- "libPVRRS\\.so$",
- "libRSDriver.*\\.so$",
- "vulkan.*\\.so$"
- ]]
_SAME_PROCESS_NDK = [
- "libEGL.so",
- "libGLESv1_CM.so",
- "libGLESv2.so",
- "libGLESv3.so",
- "libnativewindow.so",
- "libsync.so",
- "libvulkan.so"
+ "libEGL.so", "libGLESv1_CM.so", "libGLESv2.so", "libGLESv3.so",
+ "libnativewindow.so", "libsync.so", "libvulkan.so"
]
_SP_HAL_LINK_PATHS_32 = [
- "/vendor/lib/egl",
- "/vendor/lib/hw",
- "/vendor/lib"
+ "/vendor/lib/egl", "/vendor/lib/hw", "/vendor/lib"
]
_SP_HAL_LINK_PATHS_64 = [
- "/vendor/lib64/egl",
- "/vendor/lib64/hw",
- "/vendor/lib64"
+ "/vendor/lib64/egl", "/vendor/lib64/hw", "/vendor/lib64"
]
class ElfObject(object):
@@ -103,6 +86,7 @@ class VtsVndkDependencyTest(base_test.BaseTestClass):
bitness: Integer. Bitness of the ELF.
deps: List of strings. The names of the depended libraries.
"""
+
def __init__(self, target_path, bitness, deps):
self.target_path = target_path
self.name = path_utils.TargetBaseName(target_path)
@@ -117,8 +101,8 @@ class VtsVndkDependencyTest(base_test.BaseTestClass):
self._shell = self._dut.shell.one
self._temp_dir = tempfile.mkdtemp()
logging.info("adb pull %s %s", self._TARGET_VENDOR_DIR, self._temp_dir)
- pull_output = self._dut.adb.pull(
- self._TARGET_VENDOR_DIR, self._temp_dir)
+ pull_output = self._dut.adb.pull(self._TARGET_VENDOR_DIR,
+ self._temp_dir)
logging.debug(pull_output)
def tearDownClass(self):
@@ -144,25 +128,26 @@ class VtsVndkDependencyTest(base_test.BaseTestClass):
full_path = os.path.join(root_dir, file_name)
rel_path = os.path.relpath(full_path, host_dir)
target_path = path_utils.JoinTargetPath(
- target_dir, *rel_path.split(os.path.sep));
+ target_dir, *rel_path.split(os.path.sep))
try:
elf = elf_parser.ElfParser(full_path)
except elf_parser.ElfError:
logging.debug("%s is not an ELF file", target_path)
continue
try:
- deps = elf.listDependencies()
+ deps = elf.ListDependencies()
except elf_parser.ElfError as e:
elf_error_handler(target_path, e)
continue
finally:
- elf.close()
+ elf.Close()
logging.info("%s depends on: %s", target_path, ", ".join(deps))
objs.append(self.ElfObject(target_path, elf.bitness, deps))
return objs
- def _isAllowedSpHalDependency(self, lib_name, vndk_sp_names, linkable_libs):
+ def _isAllowedSpHalDependency(self, lib_name, vndk_sp_names,
+ linkable_libs):
"""Checks whether a same-process HAL library dependency is allowed.
A same-process HAL library is allowed to depend on
@@ -182,9 +167,8 @@ class VtsVndkDependencyTest(base_test.BaseTestClass):
A boolean representing whether the dependency is allowed.
"""
if (lib_name in self._LOW_LEVEL_NDK or
- lib_name in self._SAME_PROCESS_NDK or
- lib_name in vndk_sp_names or
- lib_name in linkable_libs):
+ lib_name in self._SAME_PROCESS_NDK or
+ lib_name in vndk_sp_names or lib_name in linkable_libs):
return True
return False
@@ -242,8 +226,8 @@ class VtsVndkDependencyTest(base_test.BaseTestClass):
searched.add(lib)
for dep_name in lib.deps:
if dep_name in searchable:
- self._dfsDependencies(
- searchable[dep_name], searched, searchable)
+ self._dfsDependencies(searchable[dep_name], searched,
+ searchable)
def _testSpHalDependency(self, bitness, objs):
"""Scans same-process HAL dependency on vendor partition.
@@ -253,32 +237,38 @@ class VtsVndkDependencyTest(base_test.BaseTestClass):
disallowed dependencies and list of the dependencies.
"""
vndk_sp_dir = self._getTargetVndkSpDir(bitness)
- vndk_sp_paths = file_utils.FindFiles(self._shell, vndk_sp_dir, "*.so")
- vndk_sp_names = set(path_utils.TargetBaseName(x) for x in vndk_sp_paths)
- logging.info("%s libraries: %s" % (
- vndk_sp_dir, ", ".join(vndk_sp_names)))
+ vndk_sp_paths = target_file_utils.FindFiles(self._shell, vndk_sp_dir,
+ "*.so")
+ vndk_sp_names = set(
+ path_utils.TargetBaseName(x) for x in vndk_sp_paths)
+ logging.info("%s libraries: %s" % (vndk_sp_dir,
+ ", ".join(vndk_sp_names)))
# map file names to libraries which can be linked to same-process HAL
linkable_libs = dict()
- for obj in [x for x in objs
- if x.bitness == bitness and self._isInSpHalLinkPaths(x)]:
+ for obj in [
+ x for x in objs
+ if x.bitness == bitness and self._isInSpHalLinkPaths(x)
+ ]:
if obj.name not in linkable_libs:
linkable_libs[obj.name] = obj
else:
- linkable_libs[obj.name] = min(linkable_libs[obj.name], obj,
- key=self._spHalLinkOrder)
+ linkable_libs[obj.name] = min(
+ linkable_libs[obj.name], obj, key=self._spHalLinkOrder)
# find same-process HAL and dependencies
sp_hal_libs = set()
for file_name, obj in linkable_libs.iteritems():
if any([x.match(file_name) for x in self._SAME_PROCESS_HAL]):
self._dfsDependencies(obj, sp_hal_libs, linkable_libs)
- logging.info("%d-bit SP HAL libraries: %s" % (
- bitness, ", ".join([x.name for x in sp_hal_libs])))
+ logging.info("%d-bit SP HAL libraries: %s" %
+ (bitness, ", ".join([x.name for x in sp_hal_libs])))
# check disallowed dependencies
dep_errors = []
for obj in sp_hal_libs:
- disallowed_libs = [x for x in obj.deps
- if not self._isAllowedSpHalDependency(x, vndk_sp_names,
- linkable_libs)]
+ disallowed_libs = [
+ x for x in obj.deps
+ if not self._isAllowedSpHalDependency(x, vndk_sp_names,
+ linkable_libs)
+ ]
if disallowed_libs:
dep_errors.append((obj.target_path, disallowed_libs))
return dep_errors
@@ -287,9 +277,9 @@ class VtsVndkDependencyTest(base_test.BaseTestClass):
"""Scans library/executable dependency on vendor partition."""
read_errors = []
objs = self._loadElfObjects(
- self._temp_dir,
- path_utils.TargetDirName(self._TARGET_VENDOR_DIR),
- lambda p, e: read_errors.append((p, str(e))))
+ self._temp_dir,
+ path_utils.TargetDirName(self._TARGET_VENDOR_DIR),
+ lambda p, e: read_errors.append((p, str(e))))
dep_errors = self._testSpHalDependency(32, objs)
if self._dut.is64Bit:
@@ -306,7 +296,7 @@ class VtsVndkDependencyTest(base_test.BaseTestClass):
logging.error("%s: %s", x[0], ", ".join(x[1]))
error_count = len(read_errors) + len(dep_errors)
asserts.assertEqual(error_count, 0,
- "Total number of errors: " + str(error_count))
+ "Total number of errors: " + str(error_count))
if __name__ == "__main__":
diff --git a/dependency/elf_parser.py b/dependency/elf_parser.py
deleted file mode 100644
index 1d3c7e5..0000000
--- a/dependency/elf_parser.py
+++ /dev/null
@@ -1,275 +0,0 @@
-#!/usr/bin/env python
-#
-# 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.
-#
-
-import os
-import struct
-
-
-class ElfError(Exception):
- """The exception raised by ElfParser."""
- pass
-
-
-class ElfParser(object):
- """The class reads information from an ELF file.
-
- Attributes:
- _file: The ELF file object.
- _file_size: Size of the ELF.
- bitness: Bitness of the ELF.
- _address_size: Size of address or offset in the ELF.
- _offsets: Offset of each entry in the ELF.
- _seek_read_address: The function to read an address or offset entry
- from the ELF.
- _sh_offset: Offset of section header table in the file.
- _sh_size: Size of section header table entry.
- _sh_count: Number of section header table entries.
- _section_headers: List of SectionHeader objects read from the ELF.
- """
- _MAGIC_OFFSET = 0
- _MAGIC_BYTES = b"\x7fELF"
- _BITNESS_OFFSET = 4
- _BITNESS_32 = 1
- _BITNESS_64 = 2
- # Section type
- _SHT_DYNAMIC = 6
- # Tag in dynamic section
- _DT_NULL = 0
- _DT_NEEDED = 1
- _DT_STRTAB = 5
-
- class ElfOffsets32(object):
- """Offset of each entry in 32-bit ELF"""
- # offset from ELF header
- SECTION_HEADER_OFFSET = 0x20
- SECTION_HEADER_SIZE = 0x2e
- SECTION_HEADER_COUNT = 0x30
- # offset from section header
- SECTION_TYPE = 0x04
- SECTION_ADDRESS = 0x0c
- SECTION_OFFSET = 0x10
-
- class ElfOffsets64(object):
- """Offset of each entry in 64-bit ELF"""
- # offset from ELF header
- SECTION_HEADER_OFFSET = 0x28
- SECTION_HEADER_SIZE = 0x3a
- SECTION_HEADER_COUNT = 0x3c
- # offset from section header
- SECTION_TYPE = 0x04
- SECTION_ADDRESS = 0x10
- SECTION_OFFSET = 0x18
-
- class SectionHeader(object):
- """Contains section header entries as attributes.
-
- Attributes:
- type: Type of the section.
- address: The virtual memory address where the section is loaded.
- offset: The offset of the section in the ELF file.
- """
- def __init__(self, type, address, offset):
- self.type = type
- self.address = address
- self.offset = offset
-
- def __init__(self, file_path):
- """Creates a parser to open and read an ELF file.
-
- Args:
- file_path: The path to the ELF.
-
- Raises:
- ElfError if the file is not a valid ELF.
- """
- try:
- self._file = open(file_path, "rb")
- except IOError as e:
- raise ElfError(e)
- try:
- self._loadElfHeader()
- self._section_headers = [
- self._loadSectionHeader(self._sh_offset + i * self._sh_size)
- for i in range(self._sh_count)]
- except:
- self._file.close()
- raise
-
- def __del__(self):
- """Closes the ELF file."""
- self.close()
-
- def close(self):
- """Closes the ELF file."""
- self._file.close()
-
- def _seekRead(self, offset, read_size):
- """Reads a byte string at specific offset in the file.
-
- Args:
- offset: An integer, the offset from the beginning of the file.
- read_size: An integer, number of bytes to read.
-
- Returns:
- A byte string which is the file content.
-
- Raises:
- ElfError if fails to seek and read.
- """
- if offset + read_size > self._file_size:
- raise ElfError("Read beyond end of file.")
- try:
- self._file.seek(offset)
- return self._file.read(read_size)
- except IOError as e:
- raise ElfError(e)
-
- def _seekRead8(self, offset):
- """Reads an 1-byte integer from file."""
- return struct.unpack("B", self._seekRead(offset, 1))[0]
-
- def _seekRead16(self, offset):
- """Reads a 2-byte integer from file."""
- return struct.unpack("H", self._seekRead(offset, 2))[0]
-
- def _seekRead32(self, offset):
- """Reads a 4-byte integer from file."""
- return struct.unpack("I", self._seekRead(offset, 4))[0]
-
- def _seekRead64(self, offset):
- """Reads an 8-byte integer from file."""
- return struct.unpack("Q", self._seekRead(offset, 8))[0]
-
- def _seekReadString(self, offset):
- """Reads a null-terminated string starting from specific offset.
-
- Args:
- offset: The offset from the beginning of the file.
-
- Returns:
- A byte string, excluding the null character.
- """
- ret = ""
- buf_size = 16
- self._file.seek(offset)
- while True:
- try:
- buf = self._file.read(buf_size)
- except IOError as e:
- raise ElfError(e)
- end_index = buf.find('\0')
- if end_index < 0:
- ret += buf
- else:
- ret += buf[:end_index]
- return ret
- if len(buf) != buf_size:
- raise ElfError("Null-terminated string reaches end of file.")
-
- def _loadElfHeader(self):
- """Loads ElfHeader and initializes attributes"""
- try:
- self._file_size = os.fstat(self._file.fileno()).st_size
- except OSError as e:
- raise ElfError(e)
-
- magic = self._seekRead(self._MAGIC_OFFSET, 4)
- if magic != self._MAGIC_BYTES:
- raise ElfError("Wrong magic bytes.")
- bitness = self._seekRead8(self._BITNESS_OFFSET)
- if bitness == self._BITNESS_32:
- self.bitness = 32
- self._address_size = 4
- self._offsets = self.ElfOffsets32
- self._seek_read_address = self._seekRead32
- elif bitness == self._BITNESS_64:
- self.bitness = 64
- self._address_size = 8
- self._offsets = self.ElfOffsets64
- self._seek_read_address = self._seekRead64
- else:
- raise ElfError("Wrong bitness value.")
-
- self._sh_offset = self._seek_read_address(
- self._offsets.SECTION_HEADER_OFFSET)
- self._sh_size = self._seekRead16(self._offsets.SECTION_HEADER_SIZE)
- self._sh_count = self._seekRead16(self._offsets.SECTION_HEADER_COUNT)
- return True
-
- def _loadSectionHeader(self, offset):
- """Loads a section header from ELF file.
-
- Args:
- offset: The starting offset of the section header.
-
- Returns:
- An instance of SectionHeader.
- """
- return self.SectionHeader(
- self._seekRead32(offset + self._offsets.SECTION_TYPE),
- self._seek_read_address(offset + self._offsets.SECTION_ADDRESS),
- self._seek_read_address(offset + self._offsets.SECTION_OFFSET))
-
- def _loadDtNeeded(self, offset):
- """Reads DT_NEEDED entries from dynamic section.
-
- Args:
- offset: The offset of the dynamic section from the beginning of
- the file
-
- Returns:
- A list of strings, the names of libraries.
- """
- strtab_address = None
- name_offsets = []
- while True:
- tag = self._seek_read_address(offset)
- offset += self._address_size
- value = self._seek_read_address(offset)
- offset += self._address_size
-
- if tag == self._DT_NULL:
- break
- if tag == self._DT_NEEDED:
- name_offsets.append(value)
- if tag == self._DT_STRTAB:
- strtab_address = value
-
- if strtab_address is None:
- raise ElfError("Cannot find string table offset in dynamic section")
-
- try:
- strtab_offset = next(x.offset for x in self._section_headers
- if x.address == strtab_address)
- except StopIteration:
- raise ElfError("Cannot find dynamic string table.")
-
- names = [self._seekReadString(strtab_offset + x)
- for x in name_offsets]
- return names
-
- def listDependencies(self):
- """Lists the shared libraries that the ELF depends on.
-
- Returns:
- A list of strings, the names of the depended libraries.
- """
- deps = []
- for sh in self._section_headers:
- if sh.type == self._SHT_DYNAMIC:
- deps.extend(self._loadDtNeeded(sh.offset))
- return deps
diff --git a/golden/dump_abi.py b/golden/dump_abi.py
new file mode 100755
index 0000000..e4202d1
--- /dev/null
+++ b/golden/dump_abi.py
@@ -0,0 +1,260 @@
+#!/usr/bin/env python
+#
+# 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.
+#
+
+import argparse
+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:
+ 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.elf_parser = importlib.import_module(
+ "vts.utils.python.library.elf_parser")
+ cls.vtable_parser = importlib.import_module(
+ "vts.utils.python.library.vtable_parser")
+
+
+def GetBuildVariable(build_top_dir, var):
+ """Gets value of a variable from build config.
+
+ Args:
+ build_top_dir: The path to root directory of Android source.
+ var: The name of the variable.
+
+ Returns:
+ A string which is the value of the variable.
+ """
+ build_core = os.path.join(build_top_dir, "build", "core")
+ env = dict(os.environ)
+ env["CALLED_FROM_SETUP"] = "true"
+ env["BUILD_SYSTEM"] = build_core
+ cmd = ["make", "--no-print-directory",
+ "-f", os.path.join(build_core, "config.mk"),
+ "dumpvar-" + var]
+ proc = subprocess.Popen(cmd, env=env, cwd=build_top_dir,
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = proc.communicate()
+ if stderr:
+ sys.exit("Cannot get variable: cmd=%s\nstdout=%s\nstderr=%s" % (
+ cmd, stdout, stderr))
+ return stdout.strip()
+
+
+def FindBinary(file_name):
+ """Finds an executable binary in environment variable PATH.
+
+ Args:
+ file_name: The file name to find.
+
+ Returns:
+ A string which is the path to the binary.
+ """
+ cmd = ["which", file_name]
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ stdout, stderr = proc.communicate()
+ if proc.returncode:
+ sys.exit("Cannot find file: cmd=%s\nstdout=%s\nstderr=%s" % (
+ cmd, stdout, stderr))
+ return stdout
+
+
+def DumpSymbols(lib_path, dump_path):
+ """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.
+
+ Returns:
+ A string which is the description about the result.
+
+ 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 = parser.ListGlobalDynamicSymbols()
+ finally:
+ if parser:
+ parser.Close()
+ if not symbols:
+ return "No symbols"
+ symbols.sort()
+ with open(dump_path, "w") as dump_file:
+ dump_file.write("\n".join(symbols) + "\n")
+ return "Output: " + dump_path
+
+
+def DumpVtables(lib_path, dump_path, dumper_dir):
+ """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.
+
+ Returns:
+ A string which is the description about the result.
+
+ 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)
+ vtables = parser.CallVtableDumper(lib_path)
+ if not vtables:
+ return "No vtables"
+ with open(dump_path, "w+") as dump_file:
+ dump_file.write(vtables)
+ return "Output: " + dump_path
+
+
+def GetSystemLibDirByArch(product_dir, arch_name):
+ """Returns the directory containing libraries for specific architecture.
+
+ Args:
+ product_dir: The path to the product output directory in Android source.
+ arch_name: The name of the CPU architecture.
+
+ Returns:
+ The path to the directory containing the libraries.
+ """
+ if arch_name in ("arm", "x86", "mips"):
+ src_dir = os.path.join(product_dir, "system", "lib")
+ elif arch_name in ("arm64", "x86_64", "mips64"):
+ src_dir = os.path.join(product_dir, "system", "lib64")
+ else:
+ sys.exit("Unknown target arch " + str(target_arch))
+ return src_dir
+
+
+def DumpAbi(output_dir, input_files, product_dir, archs, dumper_dir):
+ """Generates dump from libraries.
+
+ Args:
+ output_dir: The output directory of dump files.
+ input_files: A list of strings. Each element can be .so file or a text
+ file which contains list of libraries.
+ product_dir: The path to the product output directory in Android source.
+ archs: A list of strings which are the CPU architectures of the
+ libraries.
+ dumper_dir: The path to the directory containing the vtable dumper
+ executable and library.
+ """
+ # Get names of the libraries to dump
+ lib_names = []
+ for input_file in input_files:
+ if input_file.endswith(".so"):
+ lib_names.append(input_file)
+ else:
+ with open(input_file, "r") as lib_list:
+ lib_names.extend(line.strip() for line in lib_list
+ if line.strip())
+ # Create the dumps
+ for arch in archs:
+ lib_dir = GetSystemLibDirByArch(product_dir, arch)
+ dump_dir = os.path.join(output_dir, arch)
+ if not os.path.exists(dump_dir):
+ os.makedirs(dump_dir)
+ 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)
+ print(DumpSymbols(lib_path, symbol_dump_path))
+ print(DumpVtables(lib_path, vtable_dump_path, dumper_dir))
+ print("")
+
+
+def main():
+ # Parse arguments
+ arg_parser = argparse.ArgumentParser()
+ arg_parser.add_argument("file", nargs="*",
+ help="the library to dump. Can be .so file or a"
+ "text file containing list of libraries.")
+ 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")
+ arg_parser.add_argument("--output", "-o", action="store", required=True,
+ help="output directory for ABI reference dump.")
+ args = arg_parser.parse_args()
+
+ # Get product directory
+ product_dir = os.getenv("ANDROID_PRODUCT_OUT")
+ if not product_dir:
+ sys.exit("env var ANDROID_PRODUCT_OUT is not set")
+ print("ANDROID_PRODUCT_OUT=" + product_dir)
+
+ # 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")
+ target_arch = GetBuildVariable(build_top_dir, "TARGET_ARCH")
+ target_2nd_arch = GetBuildVariable(build_top_dir, "TARGET_2ND_ARCH")
+ print("TARGET_ARCH=" + target_arch)
+ print("TARGET_2ND_ARCH=" + target_2nd_arch)
+ archs = [target_arch]
+ if target_2nd_arch:
+ archs.append(target_2nd_arch)
+
+ # Import elf_parser and vtable_parser
+ ExternalModules.ImportParsers(args.import_path if args.import_path else
+ os.path.join(build_top_dir, "test"))
+
+ # Find vtable dumper
+ if args.dumper_dir:
+ dumper_dir = args.dumper_dir
+ else:
+ dumper_path = FindBinary(vtable_parser.VtableParser.VNDK_VTABLE_DUMPER)
+ dumper_dir = os.path.dirname(os.path.dirname(dumper_path))
+ print("DUMPER_DIR=" + dumper_dir)
+
+ DumpAbi(args.output, args.file, product_dir, archs, dumper_dir)
+
+
+if __name__ == "__main__":
+ main()