vndk-def: Hide system-to-vendor deps instead

This commit replaces remove_dep() with hide_dep() so that the
dependencies can be kept in deps-insight command.

Before this commit, system-to-vendor dependencies will be removed and
won't be present in the output of deps-insight command.

Test: ./tests/test_vndk.py
Test: ./tests/test_command_deps_insight.py
Change-Id: I377a81f98a726219e241e79ae6274e9b124de035
This commit is contained in:
Logan Chien
2017-11-09 13:48:12 +08:00
parent 34e64c41e0
commit ce5a714b70
7 changed files with 447 additions and 169 deletions

View File

@@ -39,3 +39,19 @@ if sys.version_info >= (3, 0):
from io import StringIO from io import StringIO
else: else:
from StringIO import StringIO from StringIO import StringIO
try:
from unittest.mock import patch
except ImportError:
import contextlib
@contextlib.contextmanager
def patch(target, mock):
obj, attr = target.rsplit('.')
obj = __import__(obj)
original_value = getattr(obj, attr)
setattr(obj, attr, mock)
try:
yield
finally:
setattr(obj, attr, original_value)

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env python3
from __future__ import print_function
import os
import sys
import unittest
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from compat import StringIO, patch
from vndk_definition_tool import (
DepsInsightCommand, ModuleInfo, PT_SYSTEM, PT_VENDOR)
from utils import GraphBuilder
class DepsInsightCommandTest(unittest.TestCase):
_PATH_FIELD = 0
_ELF_CLASS_FIELD = 1
_TAGS_FIELD = 2
_DEPS_FIELD = 3
_USERS_FIELD = 4
_SOURCE_DIRS_FIELD = 5
@classmethod
def _get_module(cls, strs, mods, path):
for mod in mods:
if strs[mod[cls._PATH_FIELD]] == path:
return mod
return None
@classmethod
def _get_module_deps(cls, strs, mods, path):
mod = cls._get_module(strs, mods, path)
result = set()
for deps in mod[cls._DEPS_FIELD]:
result.update(strs[mods[x][cls._PATH_FIELD]] for x in deps)
return result
@classmethod
def _get_module_users(cls, strs, mods, path):
mod = cls._get_module(strs, mods, path)
users = mod[cls._USERS_FIELD]
return set(strs[mods[x][cls._PATH_FIELD]] for x in users)
def test_serialize_data_with_all_deps(self):
"""compute_degenerated_vndk() should not remove bad dependencies from
the output of deps-insight. This test checks the existance of bad
dependencies."""
gb = GraphBuilder()
libfwk = gb.add_lib32(PT_SYSTEM, 'libfwk')
libvndk = gb.add_lib32(PT_SYSTEM, 'libvndk',
dt_needed=['libvnd_bad.so'], extra_dir='vndk')
libvndk_sp = gb.add_lib32(PT_SYSTEM, 'libutils',
dt_needed=['libvnd_bad.so'],
extra_dir='vndk-sp')
libvnd = gb.add_lib32(PT_VENDOR, 'libvnd',
dt_needed=['libvndk.so', 'libutils.so'])
libvnd_bad = gb.add_lib32(PT_VENDOR, 'libvnd_bad', extra_dir='vndk-sp')
gb.resolve()
with patch('sys.stderr', StringIO()):
vndk_sets = gb.graph.compute_degenerated_vndk(set(), None)
self.assertNotIn(libvnd_bad, libvndk.deps_good)
self.assertNotIn(libvnd_bad, libvndk_sp.deps_good)
strs, mods = DepsInsightCommand.serialize_data(
list(gb.graph.all_libs()), vndk_sets, ModuleInfo())
deps = self._get_module_deps(strs, mods, libvndk.path)
self.assertIn(libvnd_bad.path, deps)
deps = self._get_module_deps(strs, mods, libvndk_sp.path)
self.assertIn(libvnd_bad.path, deps)
users = self._get_module_users(strs, mods, libvnd_bad.path)
self.assertIn(libvndk.path, users)
self.assertIn(libvndk_sp.path, users)
if __name__ == '__main__':
unittest.main()

View File

@@ -19,35 +19,37 @@ class ELFLinkDataTest(unittest.TestCase):
self.w = ELFLinkData(PT_SYSTEM, '/system/lib/libw.so', None, 0) self.w = ELFLinkData(PT_SYSTEM, '/system/lib/libw.so', None, 0)
self.v = ELFLinkData(PT_VENDOR, '/vendor/lib/libv.so', None, 0) self.v = ELFLinkData(PT_VENDOR, '/vendor/lib/libv.so', None, 0)
self.x.add_dep(self.y, ELFLinkData.NEEDED) self.x.add_needed_dep(self.y)
self.x.add_dep(self.z, ELFLinkData.DLOPEN) self.x.add_dlopen_dep(self.z)
self.z.add_dep(self.w, ELFLinkData.NEEDED) self.z.add_needed_dep(self.w)
self.z.add_dep(self.w, ELFLinkData.DLOPEN) self.z.add_dlopen_dep(self.w)
def test_add_dep_and_accessors(self): def test_add_dep_and_accessors(self):
self.assertIn(self.y, self.x.dt_deps) self.assertIn(self.y, self.x.deps_needed_all)
self.assertIn(self.x, self.y.dt_users) self.assertIn(self.x, self.y.users_needed_all)
self.assertNotIn(self.y, self.x.dl_deps) self.assertNotIn(self.y, self.x.deps_dlopen_all)
self.assertNotIn(self.x, self.y.dl_users) self.assertNotIn(self.x, self.y.users_dlopen_all)
self.assertIn(self.z, self.x.dl_deps) self.assertIn(self.z, self.x.deps_dlopen_all)
self.assertIn(self.x, self.z.dl_users) self.assertIn(self.x, self.z.users_dlopen_all)
self.assertNotIn(self.z, self.x.dt_deps) self.assertNotIn(self.z, self.x.deps_needed_all)
self.assertNotIn(self.x, self.z.dt_users) self.assertNotIn(self.x, self.z.users_needed_all)
def test_remove_dep(self): def test_remove_dep(self):
self.assertIn(self.y, self.x.dt_deps) self.assertIn(self.y, self.x.deps_needed_all)
self.assertIn(self.x, self.y.dt_users) self.assertIn(self.x, self.y.users_needed_all)
with self.assertRaises(KeyError): with self.assertRaises(KeyError):
self.x.remove_dep(self.y, ELFLinkData.DLOPEN) self.x.hide_dlopen_dep(self.y)
self.assertIn(self.y, self.x.dt_deps) self.assertIn(self.y, self.x.deps_needed_all)
self.assertIn(self.x, self.y.dt_users) self.assertIn(self.x, self.y.users_needed_all)
self.x.remove_dep(self.y, ELFLinkData.NEEDED) self.x.hide_needed_dep(self.y)
self.assertNotIn(self.y, self.x.dt_deps) self.assertIn(self.y, self.x.deps_needed_hidden)
self.assertNotIn(self.x, self.y.dt_users) self.assertIn(self.x, self.y.users_needed_hidden)
self.assertNotIn(self.y, self.x.deps_needed)
self.assertNotIn(self.x, self.y.users_needed)
def test_num_deps(self): def test_num_deps(self):
self.assertEqual(2, self.x.num_deps) self.assertEqual(2, self.x.num_deps)

View File

@@ -1,65 +1,14 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
from __future__ import print_function
import os import os
import sys import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import unittest import unittest
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from compat import StringIO from compat import StringIO
from vndk_definition_tool import (ELF, ELFLinker, GenericRefs, PT_SYSTEM, from utils import GraphBuilder
PT_VENDOR) from vndk_definition_tool import (ELF, GenericRefs, PT_SYSTEM, PT_VENDOR)
class GraphBuilder(object):
_PARTITION_NAMES = {
PT_SYSTEM: 'system',
PT_VENDOR: 'vendor',
}
_LIB_DIRS = {
ELF.ELFCLASS32: 'lib',
ELF.ELFCLASS64: 'lib64',
}
def __init__(self):
self.graph = ELFLinker()
def add_lib(self, partition, klass, name, dt_needed=[],
exported_symbols=set(), imported_symbols=set(),
extra_dir=None):
"""Create and add a shared library to ELFLinker."""
lib_dir = os.path.join('/', self._PARTITION_NAMES[partition],
self._LIB_DIRS[klass])
if extra_dir:
lib_dir = os.path.join(lib_dir, extra_dir)
path = os.path.join(lib_dir, name + '.so')
elf = ELF(klass, ELF.ELFDATA2LSB, dt_needed=dt_needed,
exported_symbols=exported_symbols,
imported_symbols=imported_symbols)
node = self.graph.add_lib(partition, path, elf)
setattr(self, name + '_' + elf.elf_class_name, node)
return node
def add_multilib(self, partition, name, dt_needed=[],
exported_symbols=set(), imported_symbols=set(),
extra_dir=None):
"""Add 32-bit / 64-bit shared libraries to ELFLinker."""
return (
self.add_lib(partition, ELF.ELFCLASS32, name, dt_needed,
exported_symbols, imported_symbols, extra_dir),
self.add_lib(partition, ELF.ELFCLASS64, name, dt_needed,
exported_symbols, imported_symbols, extra_dir)
)
def resolve(self):
self.graph.resolve_deps()
class ELFLinkerTest(unittest.TestCase): class ELFLinkerTest(unittest.TestCase):
@@ -147,18 +96,18 @@ class ELFLinkerTest(unittest.TestCase):
# Check the dependencies of libc.so. # Check the dependencies of libc.so.
node = gb.graph.get_lib('/system/lib/libc.so') node = gb.graph.get_lib('/system/lib/libc.so')
self.assertEqual(['/system/lib/libdl.so', '/system/lib/libm.so'], self.assertEqual(['/system/lib/libdl.so', '/system/lib/libm.so'],
self._get_paths_from_nodes(node.deps)) self._get_paths_from_nodes(node.deps_all))
# Check the dependencies of libRS.so. # Check the dependencies of libRS.so.
node = gb.graph.get_lib('/system/lib64/libRS.so') node = gb.graph.get_lib('/system/lib64/libRS.so')
self.assertEqual(['/system/lib64/libdl.so'], self.assertEqual(['/system/lib64/libdl.so'],
self._get_paths_from_nodes(node.deps)) self._get_paths_from_nodes(node.deps_all))
# Check the dependencies of libEGL.so. # Check the dependencies of libEGL.so.
node = gb.graph.get_lib('/vendor/lib64/libEGL.so') node = gb.graph.get_lib('/vendor/lib64/libEGL.so')
self.assertEqual(['/system/lib64/libc.so', '/system/lib64/libcutils.so', self.assertEqual(['/system/lib64/libc.so', '/system/lib64/libcutils.so',
'/system/lib64/libdl.so'], '/system/lib64/libdl.so'],
self._get_paths_from_nodes(node.deps)) self._get_paths_from_nodes(node.deps_all))
def test_linked_symbols(self): def test_linked_symbols(self):
gb = self._create_normal_graph() gb = self._create_normal_graph()
@@ -217,21 +166,21 @@ class ELFLinkerTest(unittest.TestCase):
# Check the users of libc.so. # Check the users of libc.so.
node = graph.get_lib('/system/lib/libc.so') node = graph.get_lib('/system/lib/libc.so')
self.assertEqual(['/system/lib/libcutils.so', '/vendor/lib/libEGL.so'], self.assertEqual(['/system/lib/libcutils.so', '/vendor/lib/libEGL.so'],
self._get_paths_from_nodes(node.users)) self._get_paths_from_nodes(node.users_all))
# Check the users of libdl.so. # Check the users of libdl.so.
node = graph.get_lib('/system/lib/libdl.so') node = graph.get_lib('/system/lib/libdl.so')
self.assertEqual(['/system/lib/libRS.so', '/system/lib/libc.so', self.assertEqual(['/system/lib/libRS.so', '/system/lib/libc.so',
'/system/lib/libcutils.so', '/vendor/lib/libEGL.so'], '/system/lib/libcutils.so', '/vendor/lib/libEGL.so'],
self._get_paths_from_nodes(node.users)) self._get_paths_from_nodes(node.users_all))
# Check the users of libRS.so. # Check the users of libRS.so.
node = graph.get_lib('/system/lib64/libRS.so') node = graph.get_lib('/system/lib64/libRS.so')
self.assertEqual([], self._get_paths_from_nodes(node.users)) self.assertEqual([], self._get_paths_from_nodes(node.users_all))
# Check the users of libEGL.so. # Check the users of libEGL.so.
node = graph.get_lib('/vendor/lib64/libEGL.so') node = graph.get_lib('/vendor/lib64/libEGL.so')
self.assertEqual([], self._get_paths_from_nodes(node.users)) self.assertEqual([], self._get_paths_from_nodes(node.users_all))
def test_compute_predefined_sp_hal(self): def test_compute_predefined_sp_hal(self):
gb = GraphBuilder() gb = GraphBuilder()

View File

@@ -0,0 +1,119 @@
#!/usr/bin/env python3
from __future__ import print_function
import os
import sys
import unittest
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from compat import StringIO, patch
from vndk_definition_tool import (ELF, ELFLinker, PT_SYSTEM, PT_VENDOR)
from utils import GraphBuilder
class ELFLinkerVNDKTest(unittest.TestCase):
def test_normalize_partition_tags_bad_vendor_deps(self):
"""Check whether normalize_partition_tags() hides the dependencies from
the system partition to the vendor partition if the dependencies are
not SP-HAL libraries."""
gb = GraphBuilder()
libfwk = gb.add_lib32(PT_SYSTEM, 'libfwk', dt_needed=['libvnd.so'])
libvnd = gb.add_lib32(PT_VENDOR, 'libvnd')
gb.resolve()
self.assertIn(libvnd, libfwk.deps_needed)
self.assertIn(libfwk, libvnd.users_needed)
stderr = StringIO()
with patch('sys.stderr', stderr):
gb.graph.normalize_partition_tags(set(), None)
self.assertRegex(
stderr.getvalue(),
'error: .*: system exe/lib must not depend on vendor lib .*. '
'Assume such dependency does not exist.')
self.assertNotIn(libvnd, libfwk.deps_needed)
self.assertNotIn(libfwk, libvnd.users_needed)
self.assertIn(libvnd, libfwk.deps_needed_hidden)
self.assertIn(libfwk, libvnd.users_needed_hidden)
self.assertIn(libvnd, libfwk.deps_all)
self.assertIn(libvnd, libfwk.deps_needed_all)
self.assertNotIn(libvnd, libfwk.deps_good)
self.assertIn(libfwk, libvnd.users_all)
self.assertIn(libfwk, libvnd.users_needed_all)
self.assertNotIn(libfwk, libvnd.users_good)
def test_normalize_partition_tags_sp_hal(self):
"""Check whether normalize_partition_tags() keep dependencies to SP-HAL
libraries as-is."""
gb = GraphBuilder()
libfwk = gb.add_lib32(PT_SYSTEM, 'libfwk', dt_needed=['libsphal.so'])
libsphal = gb.add_lib32(PT_VENDOR, 'libsphal')
gb.resolve()
self.assertIn(libsphal, libfwk.deps_needed)
self.assertIn(libfwk, libsphal.users_needed)
gb.graph.normalize_partition_tags({libsphal}, None)
# SP-HALs should be kept as-is.
self.assertIn(libsphal, libfwk.deps_needed)
self.assertIn(libfwk, libsphal.users_needed)
def test_vndk(self):
"""Check the computation of vndk without generic references."""
gb = GraphBuilder()
libfwk = gb.add_lib32(PT_SYSTEM, 'libfwk')
libvndk = gb.add_lib32(PT_SYSTEM, 'libvndk', extra_dir='vndk')
libvndk_sp = gb.add_lib32(PT_SYSTEM, 'libutils', extra_dir='vndk-sp')
libvnd = gb.add_lib32(PT_VENDOR, 'libvnd',
dt_needed=['libvndk.so', 'libutils.so'])
gb.resolve()
self.assertIn(libvndk, libvnd.deps_all)
self.assertIn(libvndk_sp, libvnd.deps_all)
vndk_sets = gb.graph.compute_degenerated_vndk(None)
self.assertIn(libvndk, vndk_sets.vndk)
self.assertIn(libvndk_sp, vndk_sets.vndk_sp)
def test_vndk_bad_vendor_deps(self):
"""Check the computation of vndk without generic references."""
gb = GraphBuilder()
libfwk = gb.add_lib32(PT_SYSTEM, 'libfwk')
libvndk = gb.add_lib32(PT_SYSTEM, 'libvndk',
dt_needed=['libvnd_bad.so'], extra_dir='vndk')
libvndk_sp = gb.add_lib32(PT_SYSTEM, 'libutils',
dt_needed=['libvnd_bad.so'],
extra_dir='vndk-sp')
libvnd = gb.add_lib32(PT_VENDOR, 'libvnd',
dt_needed=['libvndk.so', 'libutils.so'])
libvnd_bad = gb.add_lib32(PT_VENDOR, 'libvnd_bad', extra_dir='vndk-sp')
gb.resolve()
self.assertIn(libvnd_bad, libvndk.deps_all)
self.assertIn(libvnd_bad, libvndk_sp.deps_all)
with patch('sys.stderr', StringIO()):
vndk_sets = gb.graph.compute_degenerated_vndk(None)
self.assertNotIn(libvnd_bad, vndk_sets.vndk)
self.assertNotIn(libvnd_bad, vndk_sets.vndk_sp)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,70 @@
#!/usr/bin/env python3
import os
import sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from compat import StringIO
from vndk_definition_tool import (ELF, ELFLinker, PT_SYSTEM, PT_VENDOR)
class GraphBuilder(object):
_PARTITION_NAMES = {
PT_SYSTEM: 'system',
PT_VENDOR: 'vendor',
}
_LIB_DIRS = {
ELF.ELFCLASS32: 'lib',
ELF.ELFCLASS64: 'lib64',
}
def __init__(self):
self.graph = ELFLinker()
def add_lib(self, partition, klass, name, dt_needed=[],
exported_symbols=set(), imported_symbols=set(),
extra_dir=None):
"""Create and add a shared library to ELFLinker."""
lib_dir = os.path.join('/', self._PARTITION_NAMES[partition],
self._LIB_DIRS[klass])
if extra_dir:
lib_dir = os.path.join(lib_dir, extra_dir)
path = os.path.join(lib_dir, name + '.so')
elf = ELF(klass, ELF.ELFDATA2LSB, dt_needed=dt_needed,
exported_symbols=exported_symbols,
imported_symbols=imported_symbols)
lib = self.graph.add_lib(partition, path, elf)
setattr(self, name + '_' + elf.elf_class_name, lib)
return lib
def add_lib32(self, partition, name, dt_needed=[],
exported_symbols=set(), imported_symbols=set(),
extra_dir=None):
return self.add_lib(partition, ELF.ELFCLASS32, name, dt_needed,
exported_symbols, imported_symbols, extra_dir)
def add_lib64(self, partition, name, dt_needed=[],
exported_symbols=set(), imported_symbols=set(),
extra_dir=None):
return self.add_lib(partition, ELF.ELFCLASS64, name, dt_needed,
exported_symbols, imported_symbols, extra_dir)
def add_multilib(self, partition, name, dt_needed=[],
exported_symbols=set(), imported_symbols=set(),
extra_dir=None):
"""Add 32-bit / 64-bit shared libraries to ELFLinker."""
return (
self.add_lib(partition, ELF.ELFCLASS32, name, dt_needed,
exported_symbols, imported_symbols, extra_dir),
self.add_lib(partition, ELF.ELFCLASS64, name, dt_needed,
exported_symbols, imported_symbols, extra_dir)
)
def resolve(self):
self.graph.resolve_deps()

View File

@@ -893,15 +893,18 @@ class ELFResolver(object):
class ELFLinkData(object): class ELFLinkData(object):
NEEDED = 0 # Dependencies recorded in DT_NEEDED entries.
DLOPEN = 1 # Dependencies introduced by dlopen().
def __init__(self, partition, path, elf, tag_bit): def __init__(self, partition, path, elf, tag_bit):
self.partition = partition self.partition = partition
self.path = path self.path = path
self.elf = elf self.elf = elf
self._deps = (set(), set()) self.deps_needed = set()
self._users = (set(), set()) self.deps_needed_hidden = set()
self.deps_dlopen = set()
self.deps_dlopen_hidden = set()
self.users_needed = set()
self.users_needed_hidden = set()
self.users_dlopen = set()
self.users_dlopen_hidden = set()
self.imported_ext_symbols = collections.defaultdict(set) self.imported_ext_symbols = collections.defaultdict(set)
self._tag_bit = tag_bit self._tag_bit = tag_bit
self.unresolved_symbols = set() self.unresolved_symbols = set()
@@ -936,67 +939,85 @@ class ELFLinkData(object):
def is_fwk_only_rs(self): def is_fwk_only_rs(self):
return TaggedDict.is_fwk_only_rs(self._tag_bit) return TaggedDict.is_fwk_only_rs(self._tag_bit)
def add_dep(self, dst, ty): def add_needed_dep(self, dst):
self._deps[ty].add(dst) assert dst not in self.deps_needed_hidden
dst._users[ty].add(self) assert self not in dst.users_needed_hidden
self.deps_needed.add(dst)
dst.users_needed.add(self)
def remove_dep(self, dst, ty): def add_dlopen_dep(self, dst):
self._deps[ty].remove(dst) assert dst not in self.deps_dlopen_hidden
dst._users[ty].remove(self) assert self not in dst.users_dlopen_hidden
self.deps_dlopen.add(dst)
dst.users_dlopen.add(self)
def hide_needed_dep(self, dst):
self.deps_needed.remove(dst)
dst.users_needed.remove(self)
self.deps_needed_hidden.add(dst)
dst.users_needed_hidden.add(self)
def hide_dlopen_dep(self, dst):
self.deps_dlopen.remove(dst)
dst.users_dlopen.remove(self)
self.deps_dlopen_hidden.add(dst)
dst.users_dlopen_hidden.add(self)
@property @property
def num_deps(self): def num_deps(self):
"""Get the number of dependencies. If a library is linked by both """Get the number of dependencies. If a library is linked by both
NEEDED and DLOPEN relationship, then it will be counted twice.""" NEEDED and DLOPEN relationship, then it will be counted twice."""
return sum(len(deps) for deps in self._deps) return (len(self.deps_needed) + len(self.deps_needed_hidden) +
len(self.deps_dlopen) + len(self.deps_dlopen_hidden))
@property @property
def deps(self): def deps_all(self):
return itertools.chain.from_iterable(self._deps) return itertools.chain(self.deps_needed, self.deps_needed_hidden,
self.deps_dlopen, self.deps_dlopen_hidden)
@property @property
def deps_with_type(self): def deps_good(self):
dt_deps = zip(self._deps[self.NEEDED], itertools.repeat(self.NEEDED)) return itertools.chain(self.deps_needed, self.deps_dlopen)
dl_deps = zip(self._deps[self.DLOPEN], itertools.repeat(self.DLOPEN))
return itertools.chain(dt_deps, dl_deps)
@property @property
def dt_deps(self): def deps_needed_all(self):
return self._deps[self.NEEDED] return itertools.chain(self.deps_needed, self.deps_needed_hidden)
@property @property
def dl_deps(self): def deps_dlopen_all(self):
return self._deps[self.DLOPEN] return itertools.chain(self.deps_dlopen, self.deps_dlopen_hidden)
@property @property
def num_users(self): def num_users(self):
"""Get the number of users. If a library is linked by both NEEDED and """Get the number of users. If a library is linked by both NEEDED and
DLOPEN relationship, then it will be counted twice.""" DLOPEN relationship, then it will be counted twice."""
return sum(len(users) for users in self._users) return (len(self.users_needed) + len(self.users_needed_hidden) +
len(self.users_dlopen) + len(self.users_dlopen_hidden))
@property @property
def users(self): def users_all(self):
return itertools.chain.from_iterable(self._users) return itertools.chain(self.users_needed, self.users_needed_hidden,
self.users_dlopen, self.users_dlopen_hidden)
@property @property
def users_with_type(self): def users_good(self):
dt_users = zip(self._users[self.NEEDED], itertools.repeat(self.NEEDED)) return itertools.chain(self.users_needed, self.users_dlopen)
dl_users = zip(self._users[self.DLOPEN], itertools.repeat(self.DLOPEN))
return itertools.chain(dt_users, dl_users)
@property @property
def dt_users(self): def users_needed_all(self):
return self._users[self.NEEDED] return itertools.chain(self.users_needed, self.users_needed_hidden)
@property @property
def dl_users(self): def users_dlopen_all(self):
return self._users[self.DLOPEN] return itertools.chain(self.users_dlopen, self.users_dlopen_hidden)
def has_dep(self, dst): def has_dep(self, dst):
return any(dst in deps for deps in self._deps) return (dst in self.deps_needed or dst in self.deps_needed_hidden or
dst in self.deps_dlopen or dst in self.deps_dlopen_hidden)
def has_user(self, dst): def has_user(self, dst):
return any(dst in users for users in self._users) return (dst in self.users_needed or dst in self.users_needed_hidden or
dst in self.users_dlopen or dst in self.users_dlopen_hidden)
def is_system_lib(self): def is_system_lib(self):
return self.partition == PT_SYSTEM return self.partition == PT_SYSTEM
@@ -1091,12 +1112,12 @@ class ELFLinker(object):
lib.partition = new_partition lib.partition = new_partition
self._add_lib_to_lookup_dict(lib) self._add_lib_to_lookup_dict(lib)
def add_dep(self, src_path, dst_path, ty): def add_dlopen_dep(self, src_path, dst_path):
for elf_class in (ELF.ELFCLASS32, ELF.ELFCLASS64): for elf_class in (ELF.ELFCLASS32, ELF.ELFCLASS64):
src = self.get_lib_in_elf_class(elf_class, src_path) src = self.get_lib_in_elf_class(elf_class, src_path)
dst = self.get_lib_in_elf_class(elf_class, dst_path) dst = self.get_lib_in_elf_class(elf_class, dst_path)
if src and dst: if src and dst:
src.add_dep(dst, ty) src.add_dlopen_dep(dst)
return return
print('error: cannot add dependency from {} to {}.' print('error: cannot add dependency from {} to {}.'
.format(src_path, dst_path), file=sys.stderr) .format(src_path, dst_path), file=sys.stderr)
@@ -1176,8 +1197,7 @@ class ELFLinker(object):
for line in f: for line in f:
match = patt.match(line) match = patt.match(line)
if match: if match:
self.add_dep(match.group(1), match.group(2), self.add_dlopen_dep(match.group(1), match.group(2))
ELFLinkData.DLOPEN)
def _find_exported_symbol(self, symbol, libs): def _find_exported_symbol(self, symbol, libs):
"""Find the shared library with the exported symbol.""" """Find the shared library with the exported symbol."""
@@ -1211,7 +1231,7 @@ class ELFLinker(object):
.format(lib.path, dt_needed, candidates), file=sys.stderr) .format(lib.path, dt_needed, candidates), file=sys.stderr)
lib.unresolved_dt_needed.append(dt_needed) lib.unresolved_dt_needed.append(dt_needed)
continue continue
lib.add_dep(dep, ELFLinkData.NEEDED) lib.add_needed_dep(dep)
imported_libs.append(dep) imported_libs.append(dep)
return imported_libs return imported_libs
@@ -1368,16 +1388,18 @@ class ELFLinker(object):
"""Find all SP-NDK libraries.""" """Find all SP-NDK libraries."""
return set(lib for lib in self.all_libs() if lib.is_sp_ndk) return set(lib for lib in self.all_libs() if lib.is_sp_ndk)
def compute_sp_lib(self, generic_refs): def compute_sp_lib(self, generic_refs, ignore_hidden_deps=False):
def is_ndk(lib): def is_ndk(lib):
return lib.is_ndk return lib.is_ndk
sp_ndk = self.compute_sp_ndk() sp_ndk = self.compute_sp_ndk()
sp_ndk_closure = self.compute_deps_closure(sp_ndk, is_ndk) sp_ndk_closure = self.compute_deps_closure(
sp_ndk, is_ndk, ignore_hidden_deps)
sp_ndk_indirect = sp_ndk_closure - sp_ndk sp_ndk_indirect = sp_ndk_closure - sp_ndk
sp_hal = self.compute_predefined_sp_hal() sp_hal = self.compute_predefined_sp_hal()
sp_hal_closure = self.compute_deps_closure(sp_hal, is_ndk) sp_hal_closure = self.compute_deps_closure(
sp_hal, is_ndk, ignore_hidden_deps)
def is_aosp_lib(lib): def is_aosp_lib(lib):
return (not generic_refs or \ return (not generic_refs or \
@@ -1414,10 +1436,10 @@ class ELFLinker(object):
return result return result
def _deps_po_sorted(self, lib_set): def _deps_po_sorted(self, lib_set):
return self._po_sorted(lib_set, lambda x: x.deps) return self._po_sorted(lib_set, lambda x: x.deps_all)
def _users_po_sorted(self, lib_set): def _users_po_sorted(self, lib_set):
return self._po_sorted(lib_set, lambda x: x.users) return self._po_sorted(lib_set, lambda x: x.users_all)
def normalize_partition_tags(self, sp_hals, generic_refs): def normalize_partition_tags(self, sp_hals, generic_refs):
system_libs = set(self.lib_pt[PT_SYSTEM].values()) system_libs = set(self.lib_pt[PT_SYSTEM].values())
@@ -1427,31 +1449,31 @@ class ELFLinker(object):
return lib.is_system_lib() or lib in sp_hals return lib.is_system_lib() or lib in sp_hals
for lib in system_libs_po: for lib in system_libs_po:
if all(is_system_lib_or_sp_hal(dep) for dep in lib.deps): if all(is_system_lib_or_sp_hal(dep) for dep in lib.deps_all):
# Good system lib. Do nothing. # Good system lib. Do nothing.
continue continue
if not generic_refs or generic_refs.refs.get(lib.path): if not generic_refs or generic_refs.refs.get(lib.path):
# If lib is in AOSP generic reference, then we assume that the # If lib is in AOSP generic reference, then we assume that the
# non-SP-HAL dependencies are errors. Emit errors and remove # non-SP-HAL dependencies are errors. Emit errors and remove
# the dependencies. # the dependencies.
for dep in list(lib.dt_deps): for dep in list(lib.deps_needed_all):
if not is_system_lib_or_sp_hal(dep): if not is_system_lib_or_sp_hal(dep):
print('error: {}: system exe/lib must not depend on ' print('error: {}: system exe/lib must not depend on '
'vendor lib {}. Assume such dependency does ' 'vendor lib {}. Assume such dependency does '
'not exist.'.format(lib.path, dep.path), 'not exist.'.format(lib.path, dep.path),
file=sys.stderr) file=sys.stderr)
lib.remove_dep(dep, ELFLinkData.NEEDED) lib.hide_needed_dep(dep)
for dep in list(lib.dl_deps): for dep in list(lib.deps_dlopen_all):
if not is_system_lib_or_sp_hal(dep): if not is_system_lib_or_sp_hal(dep):
print('error: {}: system exe/lib must not dlopen() ' print('error: {}: system exe/lib must not dlopen() '
'vendor lib {}. Assume such dependency does ' 'vendor lib {}. Assume such dependency does '
'not exist.'.format(lib.path, dep.path), 'not exist.'.format(lib.path, dep.path),
file=sys.stderr) file=sys.stderr)
lib.remove_dep(dep, ELFLinkData.DLOPEN) lib.hide_dlopen_dep(dep)
else: else:
# If lib is not in AOSP generic reference, then we assume that # If lib is not in AOSP generic reference, then we assume that
# lib must be moved to vendor partition. # lib must be moved to vendor partition.
for dep in lib.deps: for dep in lib.deps_all:
if not is_system_lib_or_sp_hal(dep): if not is_system_lib_or_sp_hal(dep):
print('warning: {}: system exe/lib must not depend on ' print('warning: {}: system exe/lib must not depend on '
'vendor lib {}. Assuming {} should be placed in ' 'vendor lib {}. Assuming {} should be placed in '
@@ -1517,7 +1539,7 @@ class ELFLinker(object):
return True return True
return is_aosp_lib(lib) return is_aosp_lib(lib)
sp_hal_dep = self.compute_deps_closure(sp_hal, is_not_sp_hal_dep) sp_hal_dep = self.compute_deps_closure(sp_hal, is_not_sp_hal_dep, True)
sp_hal_dep -= sp_hal sp_hal_dep -= sp_hal
# Find VNDK-SP libs. # Find VNDK-SP libs.
@@ -1529,7 +1551,7 @@ class ELFLinker(object):
self._parse_action_on_ineligible_lib(action_ineligible_vndk_sp) self._parse_action_on_ineligible_lib(action_ineligible_vndk_sp)
vndk_sp = set() vndk_sp = set()
for lib in itertools.chain(sp_hal, sp_hal_dep): for lib in itertools.chain(sp_hal, sp_hal_dep):
for dep in lib.deps: for dep in lib.deps_all:
if is_not_vndk_sp(dep): if is_not_vndk_sp(dep):
continue continue
if dep in predefined_vndk_sp: if dep in predefined_vndk_sp:
@@ -1548,7 +1570,7 @@ class ELFLinker(object):
lib in fwk_only_rs lib in fwk_only_rs
vndk_sp_indirect = self.compute_deps_closure( vndk_sp_indirect = self.compute_deps_closure(
vndk_sp, is_not_vndk_sp_indirect) vndk_sp, is_not_vndk_sp_indirect, True)
vndk_sp_indirect -= vndk_sp vndk_sp_indirect -= vndk_sp
# Find unused predefined VNDK-SP libs. # Find unused predefined VNDK-SP libs.
@@ -1561,7 +1583,7 @@ class ELFLinker(object):
def is_not_vndk_sp_indirect_unused(lib): def is_not_vndk_sp_indirect_unused(lib):
return is_not_vndk_sp_indirect(lib) or lib in vndk_sp_indirect return is_not_vndk_sp_indirect(lib) or lib in vndk_sp_indirect
vndk_sp_unused_deps = self.compute_deps_closure( vndk_sp_unused_deps = self.compute_deps_closure(
vndk_sp_unused, is_not_vndk_sp_indirect_unused) vndk_sp_unused, is_not_vndk_sp_indirect_unused, True)
vndk_sp_unused_deps -= vndk_sp_unused vndk_sp_unused_deps -= vndk_sp_unused
vndk_sp_indirect_unused = set(lib for lib in predefined_vndk_sp_indirect vndk_sp_indirect_unused = set(lib for lib in predefined_vndk_sp_indirect
@@ -1600,7 +1622,7 @@ class ELFLinker(object):
# Add the dependencies to vndk_sp_indirect if they are not vndk_sp. # Add the dependencies to vndk_sp_indirect if they are not vndk_sp.
closure = self.compute_deps_closure( closure = self.compute_deps_closure(
{lib}, lambda lib: lib not in vndk_sp_indirect_unused) {lib}, lambda lib: lib not in vndk_sp_indirect_unused, True)
closure.remove(lib) closure.remove(lib)
vndk_sp_indirect_unused.difference_update(closure) vndk_sp_indirect_unused.difference_update(closure)
vndk_sp_indirect.update(closure) vndk_sp_indirect.update(closure)
@@ -1626,7 +1648,7 @@ class ELFLinker(object):
result = set() result = set()
for lib in libs: for lib in libs:
exts = set(lib.imported_ext_symbols.keys()) exts = set(lib.imported_ext_symbols.keys())
for dep in lib.deps: for dep in lib.deps_all:
if not is_vndk_sp_public(dep): if not is_vndk_sp_public(dep):
continue continue
if dep in vndk_sp_ext or dep in vndk_sp_indirect_ext: if dep in vndk_sp_ext or dep in vndk_sp_indirect_ext:
@@ -1672,7 +1694,7 @@ class ELFLinker(object):
def collect_vndk(vendor_libs): def collect_vndk(vendor_libs):
next_vendor_libs = set() next_vendor_libs = set()
for lib in vendor_libs: for lib in vendor_libs:
for dep in lib.deps: for dep in lib.deps_all:
if is_vndk_sp_unused(dep): if is_vndk_sp_unused(dep):
relabel_vndk_sp_as_used(dep) relabel_vndk_sp_as_used(dep)
continue continue
@@ -1700,7 +1722,7 @@ class ELFLinker(object):
while candidates: while candidates:
candidates = collect_vndk(candidates) candidates = collect_vndk(candidates)
vndk_indirect = self.compute_deps_closure(vndk, is_not_vndk) vndk_indirect = self.compute_deps_closure(vndk, is_not_vndk, True)
vndk_indirect -= vndk vndk_indirect -= vndk
def is_vndk(lib): def is_vndk(lib):
@@ -1733,7 +1755,7 @@ class ELFLinker(object):
return lib.is_ll_ndk or is_vndk_sp(lib) or is_vndk(lib) return lib.is_ll_ndk or is_vndk_sp(lib) or is_vndk(lib)
ll_ndk_indirect = self.compute_deps_closure( ll_ndk_indirect = self.compute_deps_closure(
ll_ndk, is_not_ll_ndk_indirect) ll_ndk, is_not_ll_ndk_indirect, True)
ll_ndk_indirect -= ll_ndk ll_ndk_indirect -= ll_ndk
def is_not_sp_ndk_indirect(lib): def is_not_sp_ndk_indirect(lib):
@@ -1741,7 +1763,7 @@ class ELFLinker(object):
is_vndk_sp(lib) or is_vndk(lib) is_vndk_sp(lib) or is_vndk(lib)
sp_ndk_indirect = self.compute_deps_closure( sp_ndk_indirect = self.compute_deps_closure(
sp_ndk, is_not_sp_ndk_indirect) sp_ndk, is_not_sp_ndk_indirect, True)
sp_ndk_indirect -= sp_ndk sp_ndk_indirect -= sp_ndk
# Return the VNDK classifications. # Return the VNDK classifications.
@@ -1782,12 +1804,18 @@ class ELFLinker(object):
return closure return closure
@classmethod @classmethod
def compute_deps_closure(cls, root_set, is_excluded): def compute_deps_closure(cls, root_set, is_excluded,
return cls._compute_closure(root_set, is_excluded, lambda x: x.deps) ignore_hidden_deps=False):
get_successors = (lambda x: x.deps_good) if ignore_hidden_deps else \
(lambda x: x.deps_all)
return cls._compute_closure(root_set, is_excluded, get_successors)
@classmethod @classmethod
def compute_users_closure(cls, root_set, is_excluded): def compute_users_closure(cls, root_set, is_excluded,
return cls._compute_closure(root_set, is_excluded, lambda x: x.users) ignore_hidden_users=False):
get_successors = (lambda x: x.users_good) if ignore_hidden_users else \
(lambda x: x.users_all)
return cls._compute_closure(root_set, is_excluded, get_successors)
@staticmethod @staticmethod
def _create_internal(scan_elf_files, system_dirs, system_dirs_as_vendor, def _create_internal(scan_elf_files, system_dirs, system_dirs_as_vendor,
@@ -2145,7 +2173,7 @@ class VNDKCommand(VNDKCommandBase):
for lib in lib_set.values(): for lib in lib_set.values():
if not lib.num_users: if not lib.num_users:
continue continue
if all((user.partition != partition for user in lib.users)): if all((user.partition != partition for user in lib.users_all)):
print(error_msg.format(lib.path), file=sys.stderr) print(error_msg.format(lib.path), file=sys.stderr)
def _warn_incorrect_partition(self, graph): def _warn_incorrect_partition(self, graph):
@@ -2325,21 +2353,11 @@ class DepsInsightCommand(VNDKCommandBase):
parser.add_argument( parser.add_argument(
'--output', '-o', help='output directory') '--output', '-o', help='output directory')
def main(self, args): @staticmethod
generic_refs, graph, tagged_paths = self.create_from_args(args) def serialize_data(libs, vndk_lib, module_info):
module_info = ModuleInfo.load_from_path_or_default(args.module_info)
# Compute vndk heuristics.
vndk_lib = graph.compute_degenerated_vndk(
generic_refs, tagged_paths, args.action_ineligible_vndk_sp,
args.action_ineligible_vndk)
# Serialize data.
strs = [] strs = []
strs_dict = dict() strs_dict = dict()
libs = list(graph.all_libs())
libs.sort(key=lambda lib: lib.path) libs.sort(key=lambda lib: lib.path)
libs_dict = {lib: i for i, lib in enumerate(libs)} libs_dict = {lib: i for i, lib in enumerate(libs)}
@@ -2356,7 +2374,7 @@ class DepsInsightCommand(VNDKCommandBase):
return [libs_dict[lib] for lib in sorted(libs)] return [libs_dict[lib] for lib in sorted(libs)]
def collect_deps(lib): def collect_deps(lib):
queue = list(lib.deps) queue = list(lib.deps_all)
visited = set(queue) visited = set(queue)
visited.add(lib) visited.add(lib)
deps = [] deps = []
@@ -2366,7 +2384,7 @@ class DepsInsightCommand(VNDKCommandBase):
# Collect dependencies for next queue. # Collect dependencies for next queue.
next_queue = [] next_queue = []
for lib in queue: for lib in queue:
for dep in lib.deps: for dep in lib.deps_all:
if dep not in visited: if dep not in visited:
next_queue.append(dep) next_queue.append(dep)
visited.add(dep) visited.add(dep)
@@ -2395,9 +2413,25 @@ class DepsInsightCommand(VNDKCommandBase):
32 if lib.elf.is_32bit else 64, 32 if lib.elf.is_32bit else 64,
collect_tags(lib), collect_tags(lib),
collect_deps(lib), collect_deps(lib),
collect_path_sorted_lib_idxs(lib.users), collect_path_sorted_lib_idxs(lib.users_all),
collect_source_dir_paths(lib)]) collect_source_dir_paths(lib)])
return (strs, mods)
def main(self, args):
generic_refs, graph, tagged_paths = self.create_from_args(args)
module_info = ModuleInfo.load_from_path_or_default(args.module_info)
# Compute vndk heuristics.
vndk_lib = graph.compute_degenerated_vndk(
generic_refs, tagged_paths, args.action_ineligible_vndk_sp,
args.action_ineligible_vndk)
# Serialize data.
strs, mods = self.serialize_data(list(graph.all_libs), vndk_lib,
module_info)
# Generate output files. # Generate output files.
makedirs(args.output, exist_ok=True) makedirs(args.output, exist_ok=True)
script_dir = os.path.dirname(os.path.abspath(__file__)) script_dir = os.path.dirname(os.path.abspath(__file__))
@@ -2454,11 +2488,11 @@ class DepsCommand(ELFGraphCommand):
data = [] data = []
if args.revert: if args.revert:
for assoc_lib in sorted(lib.users): for assoc_lib in sorted(lib.users_all):
data.append((assoc_lib.path, data.append((assoc_lib.path,
collect_symbols(assoc_lib, lib))) collect_symbols(assoc_lib, lib)))
else: else:
for assoc_lib in sorted(lib.deps): for assoc_lib in sorted(lib.deps_all):
data.append((assoc_lib.path, data.append((assoc_lib.path,
collect_symbols(lib, assoc_lib))) collect_symbols(lib, assoc_lib)))
results.append((name, data)) results.append((name, data))
@@ -2639,7 +2673,7 @@ class CheckDepCommand(CheckDepCommandBase):
file=sys.stderr) file=sys.stderr)
# Check whether vendor modules depend on ineligible libs. # Check whether vendor modules depend on ineligible libs.
for dep in lib.deps: for dep in lib.deps_all:
if dep not in vendor_libs and dep not in eligible_libs: if dep not in vendor_libs and dep not in eligible_libs:
num_errors += 1 num_errors += 1
bad_deps.add(dep) bad_deps.add(dep)
@@ -2688,7 +2722,7 @@ class CheckEligibleListCommand(CheckDepCommandBase):
delimiter = '' delimiter = ''
for lib in sorted(eligible_libs): for lib in sorted(eligible_libs):
bad_deps = [] bad_deps = []
for dep in lib.deps: for dep in lib.deps_all:
if dep not in eligible_libs and dep not in indirect_libs: if dep not in eligible_libs and dep not in indirect_libs:
print('error: eligible lib "{}" should not depend on ' print('error: eligible lib "{}" should not depend on '
'non-eligible lib "{}".'.format(lib.path, dep.path), 'non-eligible lib "{}".'.format(lib.path, dep.path),
@@ -2702,7 +2736,7 @@ class CheckEligibleListCommand(CheckDepCommandBase):
# Check the libbinder dependencies. # Check the libbinder dependencies.
for lib in sorted(eligible_libs): for lib in sorted(eligible_libs):
bad_deps = [] bad_deps = []
for dep in lib.deps: for dep in lib.deps_all:
if os.path.basename(dep.path) == 'libbinder.so': if os.path.basename(dep.path) == 'libbinder.so':
print('error: eligible lib "{}" should not depend on ' print('error: eligible lib "{}" should not depend on '
'libbinder.so.'.format(lib.path), file=sys.stderr) 'libbinder.so.'.format(lib.path), file=sys.stderr)
@@ -2773,7 +2807,7 @@ class DepGraphCommand(ELFGraphCommand):
'depends': [], 'depends': [],
'violates': [], 'violates': [],
} }
for dep in lib.deps: for dep in lib.deps_all:
if self._check_if_allowed(tag, if self._check_if_allowed(tag,
self._get_tag_from_lib(dep, tagged_paths)): self._get_tag_from_lib(dep, tagged_paths)):
lib_item['depends'].append(dep.path) lib_item['depends'].append(dep.path)