Merge "Add VNDK definition tool." am: 3a955d4ad6

am: 24868d284c

Change-Id: I22a8a1118d4fbf75b0fe4df8931cfb8d8b491ebf
This commit is contained in:
Logan Chien
2017-01-26 07:29:57 +00:00
committed by android-build-merger
30 changed files with 1646 additions and 0 deletions

View File

@@ -0,0 +1,157 @@
VNDK Definition Tool
====================
## Usage
Create a generic reference symbols from AOSP build:
$ python3 vndk_definition_tool.py create-generic-ref \
-o generic_arm64/system \
${OUT_DIR_COMMON_BASE}/target/product/generic_arm64/system
Run the VNDK definition tool with:
$ python3 vndk_definition_tool.py vndk \
--system ${ANDROID_PRODUCT_OUT}/system \
--vendor ${ANDROID_PRODUCT_OUT}/vendor \
--load-generic-refs generic_arm64
This command will give you three different lists:
- vndk-core: VNDK libraries that are both used by frameworks and HALs. This
set will be installed on system partition.
- vndk-indirect: VNDK libraries that are indirectly used by vndk-core but
not directly used by vendors. This set will be installed on system partition.
- vndk-ext: VNDK libraries that are both used by frameworks and HALs. This set
is for the libraries that do not have an functionally-equivalent AOSP
counterpart. This set will be installed on vendor partition.
# Sub Directory Tagging
If there are some sub directory under system partition must be treated as
vendor files, then specify such directory with: `--system-dir-as-vendor`.
Conversely, if there are some sub directory under vendor partition must be
treated as system files, then specify such directory with:
`--vendor-dir-as-system`.
For example, if the device does not have an independent `vendor` partition (but
with a `vendor` folder in the `system` partition), then run this command:
$ python3 vndk_definition_tool.py vndk \
--system ${ANDROID_PRODUCT_OUT}/system \
--system-dir-as-vendor vendor \
--load-generic-refs generic_arm64
For example, if `/system/bin/hw`, `/system/lib/hw`, and `/system/lib64/hw` are
containing vendor files, then run this command:
$ python3 vndk_definition_tool.py vndk \
--system ${ANDROID_PRODUCT_OUT}/system \
--system-dir-as-vendor bin/hw \
--system-dir-as-vendor lib/hw \
--system-dir-as-vendor lib64/hw \
--vendor ${ANDROID_PRODUCT_OUT}/vendor \
--load-generic-refs generic_arm64
## Implicit Dependencies
If there are implicit dependencies, such as `dlopen()`, we can specify them in
a dependency file and load the dependency file with `--load-extra-deps`. The
dependency file format is simple: (a) each line stands for a dependency, and
(b) the file before the colon depends on the file after the colon. For
example, `libart.so` depends on `libart-compiler.so`:
/system/lib64/libart.so: /system/lib64/libart-compiler.so
And then, run VNDK definition tool with:
$ python3 vndk_definition_tool.py vndk \
--system ${ANDROID_PRODUCT_OUT}/system \
--vendor ${ANDROID_PRODUCT_OUT}/vendor \
--load-generic-refs generic_arm64 \
--load-extra-deps dlopen.dep
## Warnings
### Incorrect Partition
If you specify `--warn-incorrect-partition` command line option, then VNDK
definition tool will emit warnings when:
1. A framework library is only used by vendor binaries.
2. A vendor library is only used by framework binaries.
This allows people to review the correct partition for the module. For example,
warning: /system/lib/libtinyxml.so: This is a framework library with
vendor-only usages.
warning: /system/lib64/libtinyxml.so: This is a framework library with
vendor-only usages.
These warnings suggest that `libtinyxml.so` might be better to move to vendor
partition.
### VNDK Extension
If you specify `--load-generic-refs`, then you may see some warnings:
warning: /system/lib/libhardware_legacy.so: This is a VNDK extension and
must be moved to vendor partition.
This warning indicates that the library is not in the generic reference or the
library contains some symbols that are not available in generic build. It must
be installed into vendor partition instead. As the result, it will be included
in vndk-ext.
## Example
We can run this against Nexus 6p Factory Image:
$ unzip angler-nmf26f-factory-ef607244.zip
$ cd angler-nmf26f
$ unzip image-angler-nmf26f.zip
$ simg2img system.img system.raw.img
$ simg2img vendor.img vendor.raw.img
$ mkdir system
$ mkdir vendor
$ sudo mount -o loop,ro system.raw.img system
$ sudo mount -o loop,ro vendor.raw.img vendor
$ sudo python3 vndk_definition_tool.py vndk \
--system system --vendor vendor
We can run this against latest Android build:
$ python3 vndk_definition_tool.py vndk \
--system ${ANDROID_PRODUCT_OUT}/system \
--system-dir-as-vendor bin/hw \
--system-dir-as-vendor lib/hw \
--system-dir-as-vendor lib64/hw \
--vendor ${ANDROID_PRODUCT_OUT}/vendor
## Python 2 Support
Since `vndk_definition_tool.py` runs 3x faster with Python 3, the shebang is
specifying `python3` by default. To run `vndk_definition_tool.py` with
python2, run the following command:
$ python vndk_definition_tool.py [options]

View File

@@ -0,0 +1,9 @@
EI_CLASS 32
EI_DATA Little-Endian
E_MACHINE EM_ARM
DT_RPATH $ORIGIN/../lib
DT_NEEDED libc.so
SYMBOL __bss_start
SYMBOL _edata
SYMBOL _end
SYMBOL test

View File

@@ -0,0 +1,9 @@
EI_CLASS 32
EI_DATA Little-Endian
E_MACHINE EM_ARM
DT_RUNPATH $ORIGIN/../lib
DT_NEEDED libc.so
SYMBOL __bss_start
SYMBOL _edata
SYMBOL _end
SYMBOL test

View File

@@ -0,0 +1,8 @@
EI_CLASS 32
EI_DATA Little-Endian
E_MACHINE EM_ARM
DT_NEEDED libc.so
SYMBOL __bss_start
SYMBOL _edata
SYMBOL _end
SYMBOL test

View File

@@ -0,0 +1,9 @@
EI_CLASS 32
EI_DATA Little-Endian
E_MACHINE EM_ARM
DT_NEEDED libdl.so
DT_NEEDED libc.so
DT_NEEDED libstdc++.so
SYMBOL __bss_start
SYMBOL _edata
SYMBOL _end

View File

@@ -0,0 +1,13 @@
EI_CLASS 64
EI_DATA Little-Endian
E_MACHINE EM_AARCH64
DT_RPATH $ORIGIN/../lib
DT_NEEDED libc.so
SYMBOL __bss_end__
SYMBOL __bss_start
SYMBOL __bss_start__
SYMBOL __end__
SYMBOL _bss_end__
SYMBOL _edata
SYMBOL _end
SYMBOL test

View File

@@ -0,0 +1,13 @@
EI_CLASS 64
EI_DATA Little-Endian
E_MACHINE EM_AARCH64
DT_RUNPATH $ORIGIN/../lib
DT_NEEDED libc.so
SYMBOL __bss_end__
SYMBOL __bss_start
SYMBOL __bss_start__
SYMBOL __end__
SYMBOL _bss_end__
SYMBOL _edata
SYMBOL _end
SYMBOL test

View File

@@ -0,0 +1,12 @@
EI_CLASS 64
EI_DATA Little-Endian
E_MACHINE EM_AARCH64
DT_NEEDED libc.so
SYMBOL __bss_end__
SYMBOL __bss_start
SYMBOL __bss_start__
SYMBOL __end__
SYMBOL _bss_end__
SYMBOL _edata
SYMBOL _end
SYMBOL test

View File

@@ -0,0 +1,17 @@
EI_CLASS 64
EI_DATA Little-Endian
E_MACHINE EM_AARCH64
DT_NEEDED libdl.so
DT_NEEDED libc.so
DT_NEEDED libstdc++.so
SYMBOL __FINI_ARRAY__
SYMBOL __INIT_ARRAY__
SYMBOL __PREINIT_ARRAY__
SYMBOL __bss_end__
SYMBOL __bss_start
SYMBOL __bss_start__
SYMBOL __end__
SYMBOL _bss_end__
SYMBOL _edata
SYMBOL _end
SYMBOL main

View File

@@ -0,0 +1,16 @@
EI_CLASS 32
EI_DATA Little-Endian
E_MACHINE EM_MIPS
DT_RPATH $ORIGIN/../lib
DT_NEEDED libc.so
SYMBOL __bss_end__
SYMBOL __bss_start
SYMBOL __end__
SYMBOL _bss_end__
SYMBOL _edata
SYMBOL _end
SYMBOL _fbss
SYMBOL _fdata
SYMBOL _ftext
SYMBOL _gp_disp
SYMBOL test

View File

@@ -0,0 +1,16 @@
EI_CLASS 32
EI_DATA Little-Endian
E_MACHINE EM_MIPS
DT_RUNPATH $ORIGIN/../lib
DT_NEEDED libc.so
SYMBOL __bss_end__
SYMBOL __bss_start
SYMBOL __end__
SYMBOL _bss_end__
SYMBOL _edata
SYMBOL _end
SYMBOL _fbss
SYMBOL _fdata
SYMBOL _ftext
SYMBOL _gp_disp
SYMBOL test

View File

@@ -0,0 +1,15 @@
EI_CLASS 32
EI_DATA Little-Endian
E_MACHINE EM_MIPS
DT_NEEDED libc.so
SYMBOL __bss_end__
SYMBOL __bss_start
SYMBOL __end__
SYMBOL _bss_end__
SYMBOL _edata
SYMBOL _end
SYMBOL _fbss
SYMBOL _fdata
SYMBOL _ftext
SYMBOL _gp_disp
SYMBOL test

View File

@@ -0,0 +1,23 @@
EI_CLASS 32
EI_DATA Little-Endian
E_MACHINE EM_MIPS
DT_NEEDED libdl.so
DT_NEEDED libc.so
DT_NEEDED libstdc++.so
SYMBOL _DYNAMIC_LINKING
SYMBOL __CTOR_LIST__
SYMBOL __DTOR_LIST__
SYMBOL __FINI_ARRAY__
SYMBOL __INIT_ARRAY__
SYMBOL __PREINIT_ARRAY__
SYMBOL __RLD_MAP
SYMBOL __bss_end__
SYMBOL __bss_start
SYMBOL __end__
SYMBOL _bss_end__
SYMBOL _edata
SYMBOL _end
SYMBOL _fbss
SYMBOL _fdata
SYMBOL _ftext
SYMBOL main

View File

@@ -0,0 +1,15 @@
EI_CLASS 64
EI_DATA Little-Endian
E_MACHINE EM_MIPS
DT_RPATH $ORIGIN/../lib
DT_NEEDED libc.so
SYMBOL __bss_end__
SYMBOL __bss_start
SYMBOL __end__
SYMBOL _bss_end__
SYMBOL _edata
SYMBOL _end
SYMBOL _fbss
SYMBOL _fdata
SYMBOL _ftext
SYMBOL test

View File

@@ -0,0 +1,15 @@
EI_CLASS 64
EI_DATA Little-Endian
E_MACHINE EM_MIPS
DT_RUNPATH $ORIGIN/../lib
DT_NEEDED libc.so
SYMBOL __bss_end__
SYMBOL __bss_start
SYMBOL __end__
SYMBOL _bss_end__
SYMBOL _edata
SYMBOL _end
SYMBOL _fbss
SYMBOL _fdata
SYMBOL _ftext
SYMBOL test

View File

@@ -0,0 +1,14 @@
EI_CLASS 64
EI_DATA Little-Endian
E_MACHINE EM_MIPS
DT_NEEDED libc.so
SYMBOL __bss_end__
SYMBOL __bss_start
SYMBOL __end__
SYMBOL _bss_end__
SYMBOL _edata
SYMBOL _end
SYMBOL _fbss
SYMBOL _fdata
SYMBOL _ftext
SYMBOL test

View File

@@ -0,0 +1,21 @@
EI_CLASS 64
EI_DATA Little-Endian
E_MACHINE EM_MIPS
DT_NEEDED libdl.so
DT_NEEDED libc.so
DT_NEEDED libstdc++.so
SYMBOL _DYNAMIC_LINKING
SYMBOL __FINI_ARRAY__
SYMBOL __INIT_ARRAY__
SYMBOL __PREINIT_ARRAY__
SYMBOL __RLD_MAP
SYMBOL __bss_end__
SYMBOL __bss_start
SYMBOL __end__
SYMBOL _bss_end__
SYMBOL _edata
SYMBOL _end
SYMBOL _fbss
SYMBOL _fdata
SYMBOL _ftext
SYMBOL main

View File

@@ -0,0 +1,9 @@
EI_CLASS 32
EI_DATA Little-Endian
E_MACHINE EM_386
DT_RPATH $ORIGIN/../lib
DT_NEEDED libc.so
SYMBOL __bss_start
SYMBOL _edata
SYMBOL _end
SYMBOL test

View File

@@ -0,0 +1,9 @@
EI_CLASS 32
EI_DATA Little-Endian
E_MACHINE EM_386
DT_RUNPATH $ORIGIN/../lib
DT_NEEDED libc.so
SYMBOL __bss_start
SYMBOL _edata
SYMBOL _end
SYMBOL test

View File

@@ -0,0 +1,8 @@
EI_CLASS 32
EI_DATA Little-Endian
E_MACHINE EM_386
DT_NEEDED libc.so
SYMBOL __bss_start
SYMBOL _edata
SYMBOL _end
SYMBOL test

View File

@@ -0,0 +1,9 @@
EI_CLASS 32
EI_DATA Little-Endian
E_MACHINE EM_386
DT_NEEDED libdl.so
DT_NEEDED libc.so
DT_NEEDED libstdc++.so
SYMBOL __bss_start
SYMBOL _edata
SYMBOL _end

View File

@@ -0,0 +1,9 @@
EI_CLASS 64
EI_DATA Little-Endian
E_MACHINE EM_X86_64
DT_RPATH $ORIGIN/../lib
DT_NEEDED libc.so
SYMBOL __bss_start
SYMBOL _edata
SYMBOL _end
SYMBOL test

View File

@@ -0,0 +1,9 @@
EI_CLASS 64
EI_DATA Little-Endian
E_MACHINE EM_X86_64
DT_RUNPATH $ORIGIN/../lib
DT_NEEDED libc.so
SYMBOL __bss_start
SYMBOL _edata
SYMBOL _end
SYMBOL test

View File

@@ -0,0 +1,8 @@
EI_CLASS 64
EI_DATA Little-Endian
E_MACHINE EM_X86_64
DT_NEEDED libc.so
SYMBOL __bss_start
SYMBOL _edata
SYMBOL _end
SYMBOL test

View File

@@ -0,0 +1,9 @@
EI_CLASS 64
EI_DATA Little-Endian
E_MACHINE EM_X86_64
DT_NEEDED libdl.so
DT_NEEDED libc.so
DT_NEEDED libstdc++.so
SYMBOL __bss_start
SYMBOL _edata
SYMBOL _end

View File

@@ -0,0 +1,25 @@
#include <dlfcn.h>
#include <stdio.h>
int main(int argc, char **argv) {
if (argc < 2) {
puts("usage: main.out libtest.so");
return 1;
}
void *handle = dlopen(argv[1], RTLD_NOW);
if (!handle) {
puts("failed to open lib");
return 1;
}
void (*test)(void) = dlsym(handle, "test");
if (!test) {
puts("failed to find test() function");
} else {
test();
}
dlclose(handle);
return 0;
}

View File

@@ -0,0 +1,7 @@
extern void test();
extern int puts(const char *);
void test() {
puts("hello world");
}

View File

@@ -0,0 +1,121 @@
#!/usr/bin/env python
import os
import subprocess
import sys
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
ANDROID_BUILD_TOP = os.path.abspath(os.path.join(SCRIPT_DIR, *(['..'] * 5)))
NDK_VERSION = 'r11'
API_LEVEL = 'android-24'
def get_prebuilts_host():
if sys.platform.startswith('linux'):
return 'linux-x86'
if sys.platform.startswith('darwin'):
return 'darwin-x86'
raise NotImplementedError('unknown platform')
def get_prebuilts_gcc(arch, gcc_version):
return os.path.join(ANDROID_BUILD_TOP, 'prebuilts', 'gcc',
get_prebuilts_host(), arch, gcc_version)
def get_prebuilts_clang():
return os.path.join(ANDROID_BUILD_TOP, 'prebuilts', 'clang', 'host',
get_prebuilts_host(), 'clang-stable')
def get_prebuilts_ndk(subdirs):
return os.path.join(ANDROID_BUILD_TOP, 'prebuilts', 'ndk', NDK_VERSION,
'platforms', API_LEVEL, *subdirs)
class Target(object):
def __init__(self, name, triple, cflags, ldflags, gcc_toolchain_dir,
clang_dir, ndk_include, ndk_lib):
self.name = name
self.target_triple = triple
self.target_cflags = cflags
self.target_ldflags = ldflags
self.gcc_toolchain_dir = gcc_toolchain_dir
self.clang_dir = clang_dir
self.ndk_include = ndk_include
self.ndk_lib = ndk_lib
def compile(self, obj_file, src_file, cflags):
clang = os.path.join(self.clang_dir, 'bin', 'clang')
cmd = [clang, '-o', obj_file, '-c', src_file]
cmd.extend(['-fPIE', '-fPIC'])
cmd.extend(['-gcc-toolchain', self.gcc_toolchain_dir])
cmd.extend(['-target', self.target_triple])
cmd.extend(['-isystem', self.ndk_include])
cmd.extend(cflags)
cmd.extend(self.target_cflags)
subprocess.check_call(cmd)
def link(self, out_file, obj_files, ldflags):
if '-shared' in ldflags:
crtbegin = os.path.join(self.ndk_lib, 'crtbegin_so.o')
crtend = os.path.join(self.ndk_lib, 'crtend_so.o')
else:
crtbegin = os.path.join(self.ndk_lib, 'crtbegin_static.o')
crtend = os.path.join(self.ndk_lib, 'crtend_android.o')
clang = os.path.join(self.clang_dir, 'bin', 'clang')
cmd = [clang, '-o', out_file]
cmd.extend(['-fPIE', '-fPIC', '-Wl,--no-undefined', '-nostdlib'])
cmd.append('-L' + self.ndk_lib)
cmd.extend(['-gcc-toolchain', self.gcc_toolchain_dir])
cmd.extend(['-target', self.target_triple])
cmd.append(crtbegin)
cmd.extend(obj_files)
cmd.append(crtend)
cmd.extend(ldflags)
cmd.extend(self.target_ldflags)
if '-shared' not in ldflags:
cmd.append('-Wl,-pie')
subprocess.check_call(cmd)
def create_targets():
return {
'arm': Target('arm', 'arm-linux-androideabi', [],[],
get_prebuilts_gcc('arm', 'arm-linux-androideabi-4.9'),
get_prebuilts_clang(),
get_prebuilts_ndk(['arch-arm', 'usr', 'include']),
get_prebuilts_ndk(['arch-arm', 'usr', 'lib'])),
'arm64': Target('arm64', 'aarch64-linux-android', [], [],
get_prebuilts_gcc('aarch64', 'aarch64-linux-android-4.9'),
get_prebuilts_clang(),
get_prebuilts_ndk(['arch-arm64', 'usr', 'include']),
get_prebuilts_ndk(['arch-arm64', 'usr', 'lib'])),
'x86': Target('x86', 'x86_64-linux-android', ['-m32'], ['-m32'],
get_prebuilts_gcc('x86', 'x86_64-linux-android-4.9'),
get_prebuilts_clang(),
get_prebuilts_ndk(['arch-x86', 'usr', 'include']),
get_prebuilts_ndk(['arch-x86', 'usr', 'lib'])),
'x86_64': Target('x86_64', 'x86_64-linux-android', ['-m64'], ['-m64'],
get_prebuilts_gcc('x86', 'x86_64-linux-android-4.9'),
get_prebuilts_clang(),
get_prebuilts_ndk(['arch-x86_64', 'usr', 'include']),
get_prebuilts_ndk(['arch-x86_64', 'usr', 'lib64'])),
'mips': Target('mips', 'mipsel-linux-android', [], [],
get_prebuilts_gcc('mips', 'mips64el-linux-android-4.9'),
get_prebuilts_clang(),
get_prebuilts_ndk(['arch-mips', 'usr', 'include']),
get_prebuilts_ndk(['arch-mips', 'usr', 'lib'])),
'mips64': Target('mips64', 'mips64el-linux-android',
['-march=mips64el', '-mcpu=mips64r6'],
['-march=mips64el', '-mcpu=mips64r6'],
get_prebuilts_gcc('mips', 'mips64el-linux-android-4.9'),
get_prebuilts_clang(),
get_prebuilts_ndk(['arch-mips64', 'usr', 'include']),
get_prebuilts_ndk(['arch-mips64', 'usr', 'lib64'])),
}

View File

@@ -0,0 +1,163 @@
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import collections
import difflib
import os
import subprocess
import sys
import unittest
import targets
try:
from tempfile import TemporaryDirectory
except ImportError:
import shutil
import tempfile
class TemporaryDirectory(object):
def __init__(self, suffix='', prefix='tmp', dir=None):
self.name = tempfile.mkdtemp(suffix, prefix, dir)
def __del__(self):
self.cleanup()
def __enter__(self):
return self.name
def __exit__(self, exc, value, tb):
self.cleanup()
def cleanup(self):
if self.name:
shutil.rmtree(self.name)
self.name = None
if sys.version_info >= (3, 0):
from os import makedirs
else:
def makedirs(path, exist_ok):
if exist_ok and os.path.exists(path):
return
return os.makedirs(path)
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
VNDK_DEF_TOOL = os.path.join(SCRIPT_DIR, '..', 'vndk_definition_tool.py')
expected_dir = os.path.join(SCRIPT_DIR, 'expected')
test_dir_base = None
def run_elf_dump(path):
cmd = [sys.executable, VNDK_DEF_TOOL, 'elfdump', path]
return subprocess.check_output(cmd, universal_newlines=True)
class ELFDumpTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.targets = targets.create_targets()
def setUp(self):
if test_dir_base:
self.test_dir_base = test_dir_base
else:
self.tmp_dir = TemporaryDirectory()
self.test_dir_base = self.tmp_dir.name
def tearDown(self):
if not test_dir_base:
self.tmp_dir.cleanup()
def _prepare_dir(self, target_name):
self.expected_dir = os.path.join(expected_dir, target_name)
self.test_dir = os.path.join(self.test_dir_base, target_name)
makedirs(self.test_dir, exist_ok=True)
def _assert_equal_to_file(self, expected_file_name, actual):
actual = actual.splitlines(True)
expected_file_path = os.path.join(self.expected_dir, expected_file_name)
with open(expected_file_path, 'r') as f:
expected = f.readlines()
self.assertEqual(expected, actual)
def _test_main_out(self, target):
self._prepare_dir(target.name)
src_file = os.path.join(SCRIPT_DIR, 'input', 'main.c')
obj_file = os.path.join(self.test_dir, 'main.o')
target.compile(obj_file, src_file, [])
out_file = os.path.join(self.test_dir, 'main.out')
target.link(out_file, [obj_file], ['-ldl', '-lc', '-lstdc++'])
self._assert_equal_to_file('main.out.txt', run_elf_dump(out_file))
def _test_libtest(self, target, ldflags, output_name, expected_file_name):
self._prepare_dir(target.name)
src_file = os.path.join(SCRIPT_DIR, 'input', 'test.c')
obj_file = os.path.join(self.test_dir, 'test.o')
target.compile(obj_file, src_file, [])
out_file = os.path.join(self.test_dir, output_name)
target.link(out_file, [obj_file], ['-shared', '-lc'] + ldflags)
self._assert_equal_to_file(expected_file_name, run_elf_dump(out_file))
def create_target_test(target_name):
def test_main(self):
self._test_main_out(self.targets[target_name])
def test_libtest(self):
self._test_libtest(
self.targets[target_name], [], 'libtest.so', 'libtest.so.txt')
def test_libtest_rpath(self):
self._test_libtest(
self.targets[target_name], ['-Wl,-rpath,$ORIGIN/../lib'],
'libtest-rpath.so', 'libtest-rpath.so.txt')
def test_libtest_runpath(self):
self._test_libtest(
self.targets[target_name],
['-Wl,-rpath,$ORIGIN/../lib', '-Wl,--enable-new-dtags'],
'libtest-runpath.so', 'libtest-runpath.so.txt')
class_name = 'ELFDumpTest_' + target_name
globals()[class_name] = type(
class_name, (ELFDumpTest,),
dict(test_main=test_main,
test_libtest=test_libtest,
test_libtest_rpath=test_libtest_rpath,
test_libtest_runpath=test_libtest_runpath))
for target in ('arm', 'arm64', 'mips', 'mips64', 'x86', 'x86_64'):
create_target_test(target)
def main():
# Parse command line arguments.
parser = argparse.ArgumentParser()
parser.add_argument('--test-dir',
help='directory for temporary files')
parser.add_argument('--expected-dir', help='directory with expected output')
args, unittest_args = parser.parse_known_args()
# Convert command line options.
global expected_dir
global test_dir_base
if args.expected_dir:
expected_dir = args.expected_dir
if args.test_dir:
test_dir_base = args.test_dir
makedirs(test_dir_base, exist_ok=True)
# Run unit test.
unittest.main(argv=[sys.argv[0]] + unittest_args)
if __name__ == '__main__':
sys.exit(main())

View File

@@ -0,0 +1,878 @@
#!/usr/bin/env python3
from __future__ import print_function
import argparse
import collections
import mmap
import os
import re
import stat
import struct
import sys
if sys.version_info >= (3, 0):
from os import makedirs
from mmap import ACCESS_READ, mmap
else:
from mmap import ACCESS_READ, mmap
def makedirs(path, exist_ok):
if exist_ok and os.path.isdir(path):
return
return os.makedirs(path)
class mmap(mmap):
def __enter__(self):
return self
def __exit__(self, exc, value, tb):
self.close()
def __getitem__(self, key):
res = super(mmap, self).__getitem__(key)
if type(key) == int:
return ord(res)
return res
FileNotFoundError = OSError
try:
from sys import intern
except ImportError:
pass
EI_CLASS = 4
EI_DATA = 5
ELFCLASSNONE = 0
ELFCLASS32 = 1
ELFCLASS64 = 2
ELFDATANONE = 0
ELFDATA2LSB = 1
ELFDATA2MSB = 2
DT_NEEDED = 1
DT_RPATH = 15
DT_RUNPATH = 29
SHN_UNDEF = 0
STB_LOCAL = 0
STB_GLOBAL = 1
STB_WEAK = 2
Elf_Hdr = collections.namedtuple(
'Elf_Hdr',
'ei_class ei_data ei_version ei_osabi e_type e_machine e_version ' +
'e_entry e_phoff e_shoff e_flags e_ehsize e_phentsize e_phnum ' +
'e_shentsize e_shnum e_shstridx')
Elf_Shdr = collections.namedtuple(
'Elf_Shdr',
'sh_name sh_type sh_flags sh_addr sh_offset sh_size sh_link sh_info ' +
'sh_addralign sh_entsize')
Elf_Dyn = collections.namedtuple('Elf_Dyn', 'd_tag d_val')
class Elf_Sym(object):
__slots__ = (
'st_name', 'st_value', 'st_size', 'st_info', 'st_other', 'st_shndx'
)
def __init__(self, st_name, st_value, st_size, st_info, st_other, st_shndx):
self.st_name = st_name
self.st_value = st_value
self.st_size = st_size
self.st_info = st_info
self.st_other = st_other
self.st_shndx = st_shndx
def __str__(self):
return ('Elf_Sym(' +
'st_name=' + repr(self.st_name) + ', '
'st_value=' + repr(self.st_value) + ', '
'st_size=' + repr(self.st_size) + ', '
'st_info=' + repr(self.st_info) + ', '
'st_other=' + repr(self.st_other) + ', '
'st_shndx=' + repr(self.st_shndx) + ')')
@staticmethod
def _make(p):
return Elf_Sym(*p)
@property
def st_bind(self):
return (self.st_info >> 4)
def _get_elf_class_name(ei_class):
if ei_class == ELFCLASS32:
return '32'
if ei_class == ELFCLASS64:
return '64'
return 'None'
def _get_elf_data_name(ei_data):
if ei_data == ELFDATA2LSB:
return 'Little-Endian'
if ei_data == ELFDATA2MSB:
return 'Big-Endian'
return 'None'
_ELF_MACHINE_ID_TABLE = {
0: 'EM_NONE',
3: 'EM_386',
8: 'EM_MIPS',
40: 'EM_ARM',
62: 'EM_X86_64',
183: 'EM_AARCH64',
}
def _get_elf_machine_name(e_machine):
return _ELF_MACHINE_ID_TABLE.get(e_machine, str(e_machine))
def _extract_zero_end_slice(buf, offset):
end = offset
try:
while buf[end] != 0:
end += 1
except IndexError:
pass
return buf[offset:end]
if sys.version_info >= (3, 0):
def _extract_zero_end_str(buf, offset):
return intern(_extract_zero_end_slice(buf, offset).decode('utf-8'))
else:
def _extract_zero_end_str(buf, offset):
return intern(_extract_zero_end_slice(buf, offset))
class ELFError(ValueError):
pass
class ELF(object):
def __init__(self, ei_class=ELFCLASSNONE, ei_data=ELFDATANONE, e_machine=0,
dt_rpath=None, dt_runpath=None, dt_needed=None,
exported_symbols=None):
self.ei_class = ei_class
self.ei_data = ei_data
self.e_machine = e_machine
self.dt_rpath = dt_rpath
self.dt_runpath = dt_runpath
self.dt_needed = dt_needed if dt_needed is not None else []
self.exported_symbols = \
exported_symbols if exported_symbols is not None else []
def __str__(self):
return ('ELF(' +
'ei_class=' + repr(self.ei_class) + ', ' +
'ei_data=' + repr(self.ei_data) + ', ' +
'e_machine=' + repr(self.e_machine) + ', ' +
'dt_rpath=' + repr(self.dt_rpath) + ', ' +
'dt_runpath=' + repr(self.dt_runpath) + ', ' +
'dt_needed=' + repr(self.dt_needed) + ')')
def dump(self, file=None):
file = file if file is not None else sys.stdout
print('EI_CLASS\t' + _get_elf_class_name(self.ei_class), file=file)
print('EI_DATA\t\t' + _get_elf_data_name(self.ei_data), file=file)
print('E_MACHINE\t' + _get_elf_machine_name(self.e_machine), file=file)
if self.dt_rpath:
print('DT_RPATH\t' + self.dt_rpath, file=file)
if self.dt_runpath:
print('DT_RUNPATH\t' + self.dt_runpath, file=file)
for dt_needed in self.dt_needed:
print('DT_NEEDED\t' + dt_needed, file=file)
for symbol in self.exported_symbols:
print('SYMBOL\t\t' + symbol, file=file)
def dump_exported_symbols(self, file=None):
file = file if file is not None else sys.stdout
for symbol in self.exported_symbols:
print(symbol, file=file)
def _parse_from_buf_internal(self, buf):
# Check ELF ident.
if buf.size() < 8:
raise ELFError('bad ident')
if buf[0:4] != b'\x7fELF':
raise ELFError('bad magic')
self.ei_class = buf[EI_CLASS]
if self.ei_class != ELFCLASS32 and self.ei_class != ELFCLASS64:
raise ELFError('unknown word size')
self.ei_data = buf[EI_DATA]
if self.ei_data != ELFDATA2LSB and self.ei_data != ELFDATA2MSB:
raise ELFError('unknown endianness')
# ELF structure definitions.
endian_fmt = '<' if self.ei_data == ELFDATA2LSB else '>'
if self.ei_class == ELFCLASS32:
elf_hdr_fmt = endian_fmt + '4x4B8xHHLLLLLHHHHHH'
elf_shdr_fmt = endian_fmt + 'LLLLLLLLLL'
elf_dyn_fmt = endian_fmt + 'lL'
elf_sym_fmt = endian_fmt + 'LLLBBH'
else:
elf_hdr_fmt = endian_fmt + '4x4B8xHHLQQQLHHHHHH'
elf_shdr_fmt = endian_fmt + 'LLQQQQLLQQ'
elf_dyn_fmt = endian_fmt + 'QQ'
elf_sym_fmt = endian_fmt + 'LBBHQQ'
def parse_struct(cls, fmt, offset, error_msg):
try:
return cls._make(struct.unpack_from(fmt, buf, offset))
except struct.error:
raise ELFError(error_msg)
def parse_elf_hdr(offset):
return parse_struct(Elf_Hdr, elf_hdr_fmt, offset, 'bad elf header')
def parse_elf_shdr(offset):
return parse_struct(Elf_Shdr, elf_shdr_fmt, offset,
'bad section header')
def parse_elf_dyn(offset):
return parse_struct(Elf_Dyn, elf_dyn_fmt, offset,
'bad .dynamic entry')
if self.ei_class == ELFCLASS32:
def parse_elf_sym(offset):
return parse_struct(Elf_Sym, elf_sym_fmt, offset, 'bad elf sym')
else:
def parse_elf_sym(offset):
try:
p = struct.unpack_from(elf_sym_fmt, buf, offset)
return Elf_Sym(p[0], p[4], p[5], p[1], p[2], p[3])
except struct.error:
raise ELFError('bad elf sym')
def extract_str(offset):
return _extract_zero_end_str(buf, offset)
# Parse ELF header.
header = parse_elf_hdr(0)
self.e_machine = header.e_machine
# Check section header size.
if header.e_shentsize == 0:
raise ELFError('no section header')
# Find .shstrtab section.
shstrtab_shdr_off = \
header.e_shoff + header.e_shstridx * header.e_shentsize
shstrtab_shdr = parse_elf_shdr(shstrtab_shdr_off)
shstrtab_off = shstrtab_shdr.sh_offset
# Parse ELF section header.
sections = dict()
header_end = header.e_shoff + header.e_shnum * header.e_shentsize
for shdr_off in range(header.e_shoff, header_end, header.e_shentsize):
shdr = parse_elf_shdr(shdr_off)
name = extract_str(shstrtab_off + shdr.sh_name)
sections[name] = shdr
# Find .dynamic and .dynstr section header.
dynamic_shdr = sections.get('.dynamic')
if not dynamic_shdr:
raise ELFError('no .dynamic section')
dynstr_shdr = sections.get('.dynstr')
if not dynstr_shdr:
raise ELFError('no .dynstr section')
dynamic_off = dynamic_shdr.sh_offset
dynstr_off = dynstr_shdr.sh_offset
# Parse entries in .dynamic section.
assert struct.calcsize(elf_dyn_fmt) == dynamic_shdr.sh_entsize
dynamic_end = dynamic_off + dynamic_shdr.sh_size
for ent_off in range(dynamic_off, dynamic_end, dynamic_shdr.sh_entsize):
ent = parse_elf_dyn(ent_off)
if ent.d_tag == DT_NEEDED:
self.dt_needed.append(extract_str(dynstr_off + ent.d_val))
elif ent.d_tag == DT_RPATH:
self.dt_rpath = extract_str(dynstr_off + ent.d_val)
elif ent.d_tag == DT_RUNPATH:
self.dt_runpath = extract_str(dynstr_off + ent.d_val)
# Parse exported symbols in .dynsym section.
dynsym_shdr = sections.get('.dynsym')
if dynsym_shdr:
exported_symbols = []
dynsym_off = dynsym_shdr.sh_offset
dynsym_end = dynsym_off + dynsym_shdr.sh_size
dynsym_entsize = dynsym_shdr.sh_entsize
for ent_off in range(dynsym_off, dynsym_end, dynsym_entsize):
ent = parse_elf_sym(ent_off)
if ent.st_bind != STB_LOCAL and ent.st_shndx != SHN_UNDEF:
exported_symbols.append(
extract_str(dynstr_off + ent.st_name))
exported_symbols.sort()
self.exported_symbols = exported_symbols
def _parse_from_buf(self, buf):
try:
self._parse_from_buf_internal(buf)
except IndexError:
raise ELFError('bad offset')
def _parse_from_file(self, path):
with open(path, 'rb') as f:
st = os.fstat(f.fileno())
if not st.st_size:
raise ELFError('empty file')
with mmap(f.fileno(), st.st_size, access=ACCESS_READ) as image:
self._parse_from_buf(image)
@staticmethod
def load(path):
elf = ELF()
elf._parse_from_file(path)
return elf
@staticmethod
def loads(buf):
elf = ELF()
elf._parse_from_buf(buf)
return elf
PT_SYSTEM = 0
PT_VENDOR = 1
NUM_PARTITIONS = 2
NDK_LOW_LEVEL = {
'libc.so', 'libstdc++.so', 'libdl.so', 'liblog.so', 'libm.so', 'libz.so',
}
NDK_HIGH_LEVEL = {
'libandroid.so', 'libcamera2ndk.so', 'libEGL.so', 'libGLESv1_CM.so',
'libGLESv2.so', 'libGLESv3.so', 'libjnigraphics.so', 'libmediandk.so',
'libOpenMAXAL.so', 'libOpenSLES.so', 'libvulkan.so',
}
def _is_ndk_lib(path):
lib_name = os.path.basename(path)
return lib_name in NDK_LOW_LEVEL or lib_name in NDK_HIGH_LEVEL
BANNED_LIBS = {
'libbinder.so',
}
def is_accessible(path):
try:
mode = os.stat(path).st_mode
return (mode & (stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)) != 0
except FileNotFoundError:
return False
def scan_executables(root):
for base, dirs, files in os.walk(root):
for filename in files:
path = os.path.join(base, filename)
if is_accessible(path):
yield path
class GraphNode(object):
def __init__(self, partition, path, elf):
self.partition = partition
self.path = path
self.elf = elf
self.deps = set()
self.users = set()
self.is_ndk = _is_ndk_lib(path)
def add_dep(self, dst):
self.deps.add(dst)
dst.users.add(self)
def sorted_lib_path_list(libs):
libs = [lib.path for lib in libs]
libs.sort()
return libs
class Graph(object):
def __init__(self):
self.lib32 = dict()
self.lib64 = dict()
self.lib_pt = [dict() for i in range(NUM_PARTITIONS)]
def add(self, partition, path, elf):
node = GraphNode(partition, path, elf)
if elf.ei_class == ELFCLASS32:
self.lib32[path] = node
else:
self.lib64[path] = node
self.lib_pt[partition][path] = node
def add_dep(self, src_path, dst_path):
for lib_set in (self.lib32, self.lib64):
src = lib_set.get(src_path)
dst = lib_set.get(dst_path)
if src and dst:
src.add_dep(dst)
@staticmethod
def _compile_path_matcher(root, subdirs):
dirs = [os.path.normpath(os.path.join(root, i)) for i in subdirs]
patts = ['(?:' + re.escape(i) + ')' for i in dirs]
return re.compile('|'.join(patts))
def add_executables_in_dir(self, partition_name, partition, root,
alter_partition, alter_subdirs):
root = os.path.abspath(root)
prefix_len = len(root) + 1
if alter_subdirs:
alter_patt = Graph._compile_path_matcher(root, alter_subdirs)
for path in scan_executables(root):
try:
elf = ELF.load(path)
except ELFError as e:
continue
short_path = os.path.join('/', partition_name, path[prefix_len:])
if alter_subdirs and alter_patt.match(path):
self.add(alter_partition, short_path, elf)
else:
self.add(partition, short_path, elf)
def load_extra_deps(self, path):
patt = re.compile('([^:]*):\\s*(.*)')
with open(path, 'r') as f:
for line in f:
match = patt.match(line)
if match:
self.add_dep(match.group(1), match.group(2))
def _resolve_deps_lib_set(self, lib_set, system_lib, vendor_lib):
for lib in lib_set.values():
for dt_needed in lib.elf.dt_needed:
candidates = [
dt_needed,
os.path.join(system_lib, dt_needed),
os.path.join(vendor_lib, dt_needed),
]
for candidate in candidates:
dep = lib_set.get(candidate)
if dep:
break
if not dep:
print('warning: {}: Missing needed library: {} Tried: {}'
.format(lib.path, dt_needed, candidates),
file=sys.stderr)
continue
lib.add_dep(dep)
def resolve_deps(self):
self._resolve_deps_lib_set(self.lib32, '/system/lib', '/vendor/lib')
self._resolve_deps_lib_set(self.lib64, '/system/lib64',
'/vendor/lib64')
def compute_vndk_libs(self, generic_refs):
vndk_core = set()
vndk_ext = set()
def collect_lib_with_partition_user(result, lib_set, partition):
for lib in lib_set.values():
for user in lib.users:
if user.partition == partition:
result.add(lib)
break
# Check library usages from vendor to system.
collect_lib_with_partition_user(
vndk_core, self.lib_pt[PT_SYSTEM], PT_VENDOR)
# Check library usages from system to vendor.
collect_lib_with_partition_user(
vndk_ext, self.lib_pt[PT_VENDOR], PT_SYSTEM)
# Remove NDK libraries.
def remove_ndk_libs(libs):
return set(lib for lib in libs if not lib.is_ndk)
vndk_core = remove_ndk_libs(vndk_core)
vndk_ext = remove_ndk_libs(vndk_ext)
# Compute transitive closure.
def get_transitive_closure(root, boundary):
closure = set(root)
stack = list(root)
while stack:
lib = stack.pop()
for dep in lib.deps:
if dep.is_ndk:
continue
if dep not in closure and dep not in boundary:
closure.add(dep)
stack.append(dep)
return closure
vndk_indirect = get_transitive_closure(vndk_core, vndk_ext) - vndk_core
vndk_ext = get_transitive_closure(vndk_ext, vndk_core)
# Move extended libraries from vndk_core to vndk_ext.
if generic_refs:
stack = list(vndk_core)
stacked = vndk_core
vndk_core = set()
while stack:
lib = stack.pop()
if generic_refs.is_equivalent_lib(lib):
vndk_core.add(lib)
continue
print('warning: {}: This is a VNDK extension and must be '
'moved to vendor partition.'.format(lib.path),
file=sys.stderr)
# Move the library from vndk_core to vndk_ext.
vndk_ext.add(lib)
for dep in lib.deps:
# Skip all NDK dependencies. They are not VNDK.
if dep.is_ndk:
continue
# Skip vndk_ext and possibly vndk_core.
if dep in vndk_ext or dep in stacked:
continue
# Promote the dependency from vndk_indirect to vndk_core.
assert dep in vndk_indirect
vndk_indirect.remove(dep)
stack.append(dep)
stacked.add(dep)
return (vndk_core, vndk_indirect, vndk_ext)
@staticmethod
def create(system_dirs=None, system_dirs_as_vendor=None, vendor_dirs=None,
vendor_dirs_as_system=None, extra_deps=None):
graph = Graph()
if system_dirs:
for path in system_dirs:
graph.add_executables_in_dir('system', PT_SYSTEM, path,
PT_VENDOR, system_dirs_as_vendor)
if vendor_dirs:
for path in vendor_dirs:
graph.add_executables_in_dir('vendor', PT_VENDOR, path,
PT_SYSTEM, vendor_dirs_as_system)
if extra_deps:
for path in extra_deps:
graph.load_extra_deps(path)
graph.resolve_deps()
return graph
class GenericRefs(object):
def __init__(self):
self.refs = dict()
def _load_from_dir(self, root):
root = os.path.abspath(root)
prefix_len = len(root) + 1
for base, dirnames, filenames in os.walk(root):
for filename in filenames:
if not filename.endswith('.sym'):
continue
path = os.path.join(base, filename)
lib_name = '/' + path[prefix_len:-4]
with open(path, 'r') as f:
self.refs[lib_name] = [line.strip() for line in f]
@staticmethod
def create_from_dir(root):
result = GenericRefs()
result._load_from_dir(root)
return result
def is_equivalent_lib(self, lib):
return self.refs.get(lib.path) == lib.elf.exported_symbols
class Command(object):
def __init__(self, name, help):
self.name = name
self.help = help
def add_argparser_options(self, parser):
pass
def main(self, args):
return 0
class ELFDumpCommand(Command):
def __init__(self):
super(ELFDumpCommand, self).__init__(
'elfdump', help='Dump ELF .dynamic section')
def add_argparser_options(self, parser):
parser.add_argument('path', help='path to an ELF file')
def main(self, args):
try:
ELF.load(args.path).dump()
except ELFError as e:
print('error: {}: Bad ELF file ({})'.format(args.path, e),
file=sys.stderr)
sys.exit(1)
return 0
class CreateGenericRefCommand(Command):
def __init__(self):
super(CreateGenericRefCommand, self).__init__(
'create-generic-ref', help='Create generic references')
def add_argparser_options(self, parser):
parser.add_argument('dir')
parser.add_argument(
'--output', '-o', metavar='PATH', required=True,
help='output directory')
def main(self, args):
root = os.path.abspath(args.dir)
print(root)
prefix_len = len(root) + 1
for path in scan_executables(root):
name = path[prefix_len:]
try:
print('Processing:', name, file=sys.stderr)
elf = ELF.load(path)
out = os.path.join(args.output, name) + '.sym'
makedirs(os.path.dirname(out), exist_ok=True)
with open(out, 'w') as f:
elf.dump_exported_symbols(f)
except ELFError:
pass
return 0
class ELFGraphCommand(Command):
def add_argparser_options(self, parser):
parser.add_argument(
'--load-extra-deps', action='append',
help='load extra module dependencies')
parser.add_argument(
'--system', action='append',
help='path to system partition contents')
parser.add_argument(
'--vendor', action='append',
help='path to vendor partition contents')
parser.add_argument(
'--system-dir-as-vendor', action='append',
help='sub directory of system partition that has vendor files')
parser.add_argument(
'--vendor-dir-as-system', action='append',
help='sub directory of vendor partition that has system files')
class VNDKCommand(ELFGraphCommand):
def __init__(self):
super(VNDKCommand, self).__init__(
'vndk', help='Compute VNDK libraries set')
def add_argparser_options(self, parser):
super(VNDKCommand, self).add_argparser_options(parser)
parser.add_argument(
'--load-generic-refs',
help='compare with generic reference symbols')
parser.add_argument(
'--warn-incorrect-partition', action='store_true',
help='warn about libraries only have cross partition linkages')
parser.add_argument(
'--warn-high-level-ndk-deps', action='store_true',
help='warn about VNDK depends on high-level NDK')
parser.add_argument(
'--warn-banned-vendor-lib-deps', action='store_true',
help='warn when a vendor binaries depends on banned lib')
parser.add_argument(
'--ban-vendor-lib-dep', action='append',
help='library that must not be used by vendor binaries')
def _warn_incorrect_partition_lib_set(self, lib_set, partition, error_msg):
for lib in lib_set.values():
if not lib.users:
continue
if all((user.partition != partition for user in lib.users)):
print(error_msg.format(lib.path), file=sys.stderr)
def _warn_incorrect_partition(self, graph):
self._warn_incorrect_partition_lib_set(
graph.lib_pt[PT_VENDOR], PT_VENDOR,
'warning: {}: This is a vendor library with framework-only '
'usages.')
self._warn_incorrect_partition_lib_set(
graph.lib_pt[PT_SYSTEM], PT_SYSTEM,
'warning: {}: This is a framework library with vendor-only '
'usages.')
def _warn_high_level_ndk_deps(self, lib_sets):
for lib_set in lib_sets:
for lib in lib_set:
for dep in lib.deps:
dep_name = os.path.basename(dep.path)
if dep_name in NDK_HIGH_LEVEL:
print('warning: {}: VNDK is using high-level NDK {}.'
.format(lib.path, dep.path), file=sys.stderr)
def _warn_banned_vendor_lib_deps(self, graph, banned_libs):
for lib in graph.lib_pt[PT_VENDOR].values():
for dep in lib.deps:
dep_name = os.path.basename(dep.path)
if dep_name in banned_libs:
print('warning: {}: Vendor binary depends on banned {}.'
.format(lib.path, dep.path), file=sys.stderr)
def _check_ndk_extensions(self, graph, generic_refs):
for lib_set in (graph.lib32, graph.lib64):
for lib in lib_set.values():
if lib.is_ndk and not generic_refs.is_equivalent_lib(lib):
print('warning: {}: NDK library should not be extended.'
.format(lib.path), file=sys.stderr)
def main(self, args):
graph = Graph.create(args.system, args.system_dir_as_vendor,
args.vendor, args.vendor_dir_as_system,
args.load_extra_deps)
generic_refs = None
if args.load_generic_refs:
generic_refs = GenericRefs.create_from_dir(args.load_generic_refs)
self._check_ndk_extensions(graph, generic_refs)
if args.warn_incorrect_partition:
self._warn_incorrect_partition(graph)
vndk_core, vndk_indirect, vndk_ext = \
graph.compute_vndk_libs(generic_refs)
if args.warn_high_level_ndk_deps:
self._warn_high_level_ndk_deps((vndk_core, vndk_indirect, vndk_ext))
if args.warn_banned_vendor_lib_deps:
if args.ban_vendor_lib_dep:
banned_libs = set(args.ban_vendor_lib_dep)
else:
banned_libs = BANNED_LIBS
self._warn_banned_vendor_lib_deps(graph, banned_libs)
for lib in sorted_lib_path_list(vndk_core):
print('vndk-core:', lib)
for lib in sorted_lib_path_list(vndk_indirect):
print('vndk-indirect:', lib)
for lib in sorted_lib_path_list(vndk_ext):
print('vndk-ext:', lib)
return 0
class DepsCommand(ELFGraphCommand):
def __init__(self):
super(DepsCommand, self).__init__(
'deps', help='Print binary dependencies for debugging')
def add_argparser_options(self, parser):
super(DepsCommand, self).add_argparser_options(parser)
parser.add_argument(
'--revert', action='store_true',
help='print usage dependency')
parser.add_argument(
'--leaf', action='store_true',
help='print binaries without dependencies or usages')
def main(self, args):
graph = Graph.create(args.system, args.system_dir_as_vendor,
args.vendor, args.vendor_dir_as_system,
args.load_extra_deps)
results = []
for partition in range(NUM_PARTITIONS):
for name, lib in graph.lib_pt[partition].items():
assoc_libs = lib.users if args.revert else lib.deps
results.append((name, sorted_lib_path_list(assoc_libs)))
results.sort()
if args.leaf:
for name, deps in results:
if not deps:
print(name)
else:
for name, deps in results:
print(name)
for dep in deps:
print('\t' + dep)
return 0
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(dest='subcmd')
subcmds = dict()
def register_subcmd(cmd):
subcmds[cmd.name] = cmd
cmd.add_argparser_options(
subparsers.add_parser(cmd.name, help=cmd.help))
register_subcmd(ELFDumpCommand())
register_subcmd(CreateGenericRefCommand())
register_subcmd(VNDKCommand())
register_subcmd(DepsCommand())
args = parser.parse_args()
if not args.subcmd:
parser.print_help()
sys.exit(1)
return subcmds[args.subcmd].main(args)
if __name__ == '__main__':
sys.exit(main())