Merge changes I865fb1a2,I6b10b606,Id2e3295d,Id547c963,I66e8ecd5

* changes:
  def-tool: Hoist loop invariant
  def-tool: Add path filter for deps output
  def-tool: Scan ELF files in zip files
  def-tool: Add app-specific search path
  def-tool: Replace buf.size() with len(buf).
This commit is contained in:
Treehugger Robot
2018-12-06 04:13:19 +00:00
committed by Gerrit Code Review
2 changed files with 143 additions and 65 deletions

View File

@@ -29,43 +29,69 @@ class ELFResolverTest(unittest.TestCase):
self.assertEqual(
['/system/lib/libx.so', '/vendor/lib/libx.so'],
list(r.get_candidates('libx.so')))
list(r.get_candidates('/system/lib/libreq.so', 'libx.so')))
self.assertEqual(
['/C/libx.so', '/system/lib/libx.so', '/vendor/lib/libx.so'],
list(r.get_candidates('libx.so', ['/C'])))
list(r.get_candidates('/system/lib/libreq.so', 'libx.so',
['/C'])))
self.assertEqual(
['/C/libx.so', '/D/libx.so', '/system/lib/libx.so',
'/vendor/lib/libx.so'],
list(r.get_candidates('libx.so', ['/C', '/D'])))
list(r.get_candidates('/system/lib/libreq.so', 'libx.so',
['/C', '/D'])))
self.assertEqual(
['/E/libx.so', '/system/lib/libx.so', '/vendor/lib/libx.so'],
list(r.get_candidates('libx.so', None, ['/E'])))
list(r.get_candidates('/system/lib/libreq.so', 'libx.so', None,
['/E'])))
self.assertEqual(
['/E/libx.so', '/F/libx.so', '/system/lib/libx.so',
'/vendor/lib/libx.so'],
list(r.get_candidates('libx.so', None, ['/E', '/F'])))
list(r.get_candidates('/system/lib/libreq.so', 'libx.so', None,
['/E', '/F'])))
self.assertEqual(
['/C/libx.so', '/D/libx.so', '/E/libx.so', '/F/libx.so',
'/system/lib/libx.so', '/vendor/lib/libx.so'],
list(r.get_candidates('libx.so', ['/C', '/D'], ['/E', '/F'])))
list(r.get_candidates('/system/lib/libreq.so', 'libx.so',
['/C', '/D'], ['/E', '/F'])))
# Test app-specific search paths.
self.assertEqual(
['/system/app/example/lib/armeabi-v7a/libx.so',
'/C/libx.so', '/D/libx.so', '/E/libx.so', '/F/libx.so',
'/system/lib/libx.so', '/vendor/lib/libx.so'],
list(r.get_candidates(
'/system/app/example/lib/armeabi-v7a/libreq.so',
'libx.so',
['/C', '/D'], ['/E', '/F'])))
def test_resolve(self):
r = self.resolver
self.assertEqual('a', r.resolve('liba.so'))
self.assertEqual('c', r.resolve('libc.so'))
self.assertEqual('a', r.resolve('/system/lib/libreq.so', 'liba.so'))
self.assertEqual('c', r.resolve('/system/lib/libreq.so', 'libc.so'))
self.assertEqual(None, r.resolve('libe.so'))
self.assertEqual('e', r.resolve('libe.so', dt_rpath=['/system/lib/hw']))
self.assertEqual(None, r.resolve('/system/lib/libreq.so', 'libe.so'))
self.assertEqual(
'e', r.resolve('libe.so', dt_runpath=['/system/lib/hw']))
'e',
r.resolve('/system/lib/libreq.so', 'libe.so',
dt_rpath=['/system/lib/hw']))
self.assertEqual(
'e',
r.resolve('/system/lib/libreq.so', 'libe.so',
dt_runpath=['/system/lib/hw']))
self.assertEqual('a2', r.resolve('liba.so', dt_rpath=['/vendor/lib']))
self.assertEqual('a2', r.resolve('liba.so', dt_runpath=['/vendor/lib']))
self.assertEqual(
'a2',
r.resolve('/system/lib/libreq.so', 'liba.so',
dt_rpath=['/vendor/lib']))
self.assertEqual(
'a2',
r.resolve('/system/lib/libreq.so', 'liba.so',
dt_runpath=['/vendor/lib']))
if __name__ == '__main__':

View File

@@ -519,7 +519,7 @@ class ELF(object):
"""Parse ELF image resides in the buffer"""
# Check ELF ident.
if buf.size() < 8:
if len(buf) < 8:
raise ELFError('bad ident')
if buf[0:4] != ELF.ELF_MAGIC:
@@ -533,7 +533,7 @@ class ELF(object):
if self.ei_data not in (ELF.ELFDATA2LSB, ELF.ELFDATA2MSB):
raise ELFError('unknown endianness')
self.file_size = buf.size()
self.file_size = len(buf)
# ELF structure definitions.
endian_fmt = '<' if self.ei_data == ELF.ELFDATA2LSB else '>'
@@ -897,21 +897,6 @@ class DexFileReader(object):
yield cls.extract_dex_string(buf, offset)
@classmethod
def _read_first_bytes(cls, apk_file, num_bytes):
try:
with open(apk_file, 'rb') as fp:
return fp.read(num_bytes)
except IOError:
return b''
@classmethod
def is_zipfile(cls, apk_file_path):
magic = cls._read_first_bytes(apk_file_path, 2)
return magic == b'PK' and zipfile.is_zipfile(apk_file_path)
@classmethod
def enumerate_dex_strings_apk(cls, apk_file_path):
with zipfile.ZipFile(apk_file_path, 'r') as zip_file:
@@ -1053,7 +1038,7 @@ class DexFileReader(object):
@classmethod
def enumerate_dex_strings(cls, path):
if cls.is_zipfile(path):
if is_zipfile(path):
return DexFileReader.enumerate_dex_strings_apk(path)
if cls.is_vdex_file(path):
return DexFileReader.enumerate_dex_strings_vdex(path)
@@ -1380,21 +1365,49 @@ def scan_accessible_files(root):
yield path
def scan_elf_files(root):
def is_zipfile(path):
# zipfile.is_zipfile() tries to find the zip header in the file. But we
# only want to scan the zip file that starts with the magic word. Thus,
# we read the magic word by ourselves.
try:
with open(path, 'rb') as fp:
if fp.read(2) != b'PK':
return False
except IOError:
return False
# Check whether this is a valid zip file.
return zipfile.is_zipfile(path)
def scan_zip_file(zip_file_path):
"""Scan all ELF files in a zip archive."""
with zipfile.ZipFile(zip_file_path, 'r') as zip_file:
for name in zip_file.namelist():
yield (os.path.join(zip_file_path, name),
zip_file.open(name, 'r').read())
def scan_elf_files(root, unzip_files=True):
"""Scan all ELF files under a directory."""
for path in scan_accessible_files(root):
# If this is a zip file and unzip_file is true, scan the ELF files in
# the zip file.
if unzip_files and is_zipfile(path):
for path, content in scan_zip_file(path):
try:
yield (path, ELF.loads(content))
except ELFError:
pass
continue
# Load ELF from the path.
try:
yield (path, ELF.load(path))
except ELFError:
pass
def scan_elf_dump_files(root):
for path in scan_accessible_files(root):
if not path.endswith('.sym'):
continue
yield (path[0:-4], ELF.load_dump(path))
PT_SYSTEM = 0
PT_VENDOR = 1
NUM_PARTITIONS = 2
@@ -1646,12 +1659,21 @@ class VNDKLibDir(list):
return self.sorted_version(self)[0]
# File path patterns for Android apps
_APP_DIR_PATTERNS = re.compile('^(?:/[^/]+){1,2}/(?:priv-)?app/')
class ELFResolver(object):
def __init__(self, lib_set, default_search_path):
self.lib_set = lib_set
self.default_search_path = default_search_path
def get_candidates(self, name, dt_rpath=None, dt_runpath=None):
def get_candidates(self, requester, name, dt_rpath=None, dt_runpath=None):
# Search app-specific search paths.
if _APP_DIR_PATTERNS.match(requester):
yield os.path.join(os.path.dirname(requester), name)
# Search default search paths.
if dt_rpath:
for d in dt_rpath:
yield os.path.join(d, name)
@@ -1661,8 +1683,8 @@ class ELFResolver(object):
for d in self.default_search_path:
yield os.path.join(d, name)
def resolve(self, name, dt_rpath=None, dt_runpath=None):
for path in self.get_candidates(name, dt_rpath, dt_runpath):
def resolve(self, requester, name, dt_rpath=None, dt_runpath=None):
for path in self.get_candidates(requester, name, dt_rpath, dt_runpath):
try:
return self.lib_set[path]
except KeyError:
@@ -1959,7 +1981,7 @@ class ELFLinker(object):
def add_executables_in_dir(self, partition_name, partition, root,
alter_partition, alter_subdirs, ignored_subdirs,
scan_elf_files):
scan_elf_files, unzip_files):
root = os.path.abspath(root)
prefix_len = len(root) + 1
@@ -1968,7 +1990,7 @@ class ELFLinker(object):
if ignored_subdirs:
ignored_patt = ELFLinker._compile_path_matcher(root, ignored_subdirs)
for path, elf in scan_elf_files(root):
for path, elf in scan_elf_files(root, unzip_files):
# Ignore ELF files with unknown machine ID (eg. DSP).
if elf.e_machine not in ELF.ELF_MACHINES:
continue
@@ -2019,11 +2041,11 @@ class ELFLinker(object):
def _resolve_lib_dt_needed(self, lib, resolver):
imported_libs = []
for dt_needed in lib.elf.dt_needed:
dep = resolver.resolve(dt_needed, lib.elf.dt_rpath,
dep = resolver.resolve(lib.path, dt_needed, lib.elf.dt_rpath,
lib.elf.dt_runpath)
if not dep:
candidates = list(resolver.get_candidates(
dt_needed, lib.elf.dt_rpath, lib.elf.dt_runpath))
lib.path, dt_needed, lib.elf.dt_rpath, lib.elf.dt_runpath))
print('warning: {}: Missing needed library: {} Tried: {}'
.format(lib.path, dt_needed, candidates), file=sys.stderr)
lib.unresolved_dt_needed.append(dt_needed)
@@ -2532,7 +2554,7 @@ class ELFLinker(object):
system_dirs_ignored, vendor_dirs,
vendor_dirs_as_system, vendor_dirs_ignored,
extra_deps, generic_refs, tagged_paths,
vndk_lib_dirs):
vndk_lib_dirs, unzip_files):
if vndk_lib_dirs is None:
vndk_lib_dirs = VNDKLibDir.create_from_dirs(
system_dirs, vendor_dirs)
@@ -2541,17 +2563,17 @@ class ELFLinker(object):
if system_dirs:
for path in system_dirs:
graph.add_executables_in_dir('system', PT_SYSTEM, path,
PT_VENDOR, system_dirs_as_vendor,
system_dirs_ignored,
scan_elf_files)
graph.add_executables_in_dir(
'system', PT_SYSTEM, path, PT_VENDOR,
system_dirs_as_vendor, system_dirs_ignored,
scan_elf_files, unzip_files)
if vendor_dirs:
for path in vendor_dirs:
graph.add_executables_in_dir('vendor', PT_VENDOR, path,
PT_SYSTEM, vendor_dirs_as_system,
vendor_dirs_ignored,
scan_elf_files)
graph.add_executables_in_dir(
'vendor', PT_VENDOR, path, PT_SYSTEM,
vendor_dirs_as_system, vendor_dirs_ignored,
scan_elf_files, unzip_files)
if extra_deps:
for path in extra_deps:
@@ -2566,12 +2588,12 @@ class ELFLinker(object):
system_dirs_ignored=None, vendor_dirs=None,
vendor_dirs_as_system=None, vendor_dirs_ignored=None,
extra_deps=None, generic_refs=None, tagged_paths=None,
vndk_lib_dirs=None):
vndk_lib_dirs=None, unzip_files=True):
return ELFLinker._create_internal(
scan_elf_files, system_dirs, system_dirs_as_vendor,
system_dirs_ignored, vendor_dirs, vendor_dirs_as_system,
vendor_dirs_ignored, extra_deps, generic_refs, tagged_paths,
vndk_lib_dirs)
vndk_lib_dirs, unzip_files)
#------------------------------------------------------------------------------
@@ -2728,7 +2750,19 @@ def scan_apk_dep(graph, system_dirs, vendor_dirs):
libs = set()
for string in strings:
try:
libs.update(libnames[string])
for dep_file in libnames[string]:
match = _APP_DIR_PATTERNS.match(dep_file.path)
# List the lib if it is not embedded in the app.
if not match:
libs.add(dep_file)
continue
# Only list the embedded lib if it is in the same app.
common = os.path.commonprefix([ap, dep_file.path])
if len(common) > len(match.group(0)):
libs.add(dep_file)
continue
except KeyError:
pass
@@ -2874,6 +2908,14 @@ class ELFGraphCommand(Command):
'--aosp-system',
help='compare with AOSP generic system image directory')
parser.add_argument(
'--unzip-files', action='store_true', default=True,
help='scan ELF files in zip files')
parser.add_argument(
'--no-unzip-files', action='store_false', dest='unzip_files',
help='do not scan ELF files in zip files')
parser.add_argument('--tag-file', help='lib tag file')
def get_generic_refs_from_args(self, args):
@@ -2918,7 +2960,8 @@ class ELFGraphCommand(Command):
args.vendor_dir_ignored,
args.load_extra_deps,
generic_refs=generic_refs,
tagged_paths=tagged_paths)
tagged_paths=tagged_paths,
unzip_files=args.unzip_files)
return (generic_refs, graph, tagged_paths, vndk_lib_dirs)
@@ -3282,6 +3325,10 @@ class DepsCommand(ELFGraphCommand):
'--symbols', action='store_true',
help='print symbols')
parser.add_argument(
'--path-filter',
help='filter paths by a regular expression')
parser.add_argument('--module-info')
def main(self, args):
@@ -3290,15 +3337,20 @@ class DepsCommand(ELFGraphCommand):
module_info = ModuleInfo.load_from_path_or_default(args.module_info)
path_filter = re.compile(args.path_filter) if args.path_filter else None
if args.symbols:
def collect_symbols(user, definer):
return user.get_dep_linked_symbols(definer)
else:
def collect_symbols(user, definer):
return ()
results = []
for partition in range(NUM_PARTITIONS):
for name, lib in graph.lib_pt[partition].items():
if args.symbols:
def collect_symbols(user, definer):
return user.get_dep_linked_symbols(definer)
else:
def collect_symbols(user, definer):
return ()
if path_filter and not path_filter.match(name):
continue
data = []
if args.revert: