diff --git a/vndk/tools/definition-tool/tests/test_banned_libs.py b/vndk/tools/definition-tool/tests/test_banned_libs.py new file mode 100755 index 000000000..6c7dd536c --- /dev/null +++ b/vndk/tools/definition-tool/tests/test_banned_libs.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +from __future__ import print_function + +import os +import sys +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +import unittest + +from vndk_definition_tool import BA_WARN, BannedLibDict + + +class BannedLibDictTest(unittest.TestCase): + def test_add(self): + d = BannedLibDict() + d.add('libfoo.so', 'test', BA_WARN) + x = d.get('libfoo.so') + + self.assertIsNotNone(x) + self.assertEqual('libfoo.so', x.name) + self.assertEqual('test', x.reason) + self.assertEqual(BA_WARN, x.action) + + def test_get(self): + d = BannedLibDict.create_default() + self.assertIsNotNone(d.get('libbinder.so')) + self.assertIsNotNone(d.get('libselinux.so')) + self.assertIsNone(d.get('libc.so')) + + def test_is_banned(self): + d = BannedLibDict.create_default() + self.assertTrue(d.is_banned('/system/lib/libbinder.so')) + self.assertTrue(d.is_banned('/system/lib/libselinux.so')) + self.assertTrue(d.is_banned('/system/lib64/libbinder.so')) + self.assertTrue(d.is_banned('/system/lib64/libselinux.so')) + self.assertFalse(d.is_banned('/system/lib64/libc.so')) + + +if __name__ == '__main__': + unittest.main() + diff --git a/vndk/tools/definition-tool/tests/test_elf_linker.py b/vndk/tools/definition-tool/tests/test_elf_linker.py index 73fecce47..dad176b49 100755 --- a/vndk/tools/definition-tool/tests/test_elf_linker.py +++ b/vndk/tools/definition-tool/tests/test_elf_linker.py @@ -26,26 +26,36 @@ class GraphBuilder(object): def __init__(self): self.graph = ELFLinker() - def add_lib(self, partition, klass, name, dt_needed, exported_symbols, - imported_symbols): + 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) - setattr(self, 'elf' + elf.elf_class_name + '_' + name, elf) - path = os.path.join('/', self._PARTITION_NAMES[partition], - self._LIB_DIRS[klass], name + '.so') - self.graph.add(partition, path, elf) + node = self.graph.add(partition, path, elf) + setattr(self, name + '_' + elf.elf_class_name, node) + return node - def add_multilib(self, partition, name, dt_needed, exported_symbols, - imported_symbols): + 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.""" - - for klass in (ELF.ELFCLASS32, ELF.ELFCLASS64): - self.add_lib(partition, klass, name, dt_needed, - exported_symbols, imported_symbols) + 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() @@ -55,13 +65,10 @@ class ELFLinkerTest(unittest.TestCase): def _create_normal_graph(self): gb = GraphBuilder() - gb.add_multilib(PT_SYSTEM, 'libdl', dt_needed=[], - exported_symbols={'dlclose', 'dlopen', 'dlsym'}, - imported_symbols={}) + gb.add_multilib(PT_SYSTEM, 'libdl', + exported_symbols={'dlclose', 'dlopen', 'dlsym'}) - gb.add_multilib(PT_SYSTEM, 'libm', dt_needed=[], - exported_symbols={'cos', 'sin'}, - imported_symbols={}) + gb.add_multilib(PT_SYSTEM, 'libm', exported_symbols={'cos', 'sin'}) gb.add_multilib(PT_SYSTEM, 'libc', dt_needed=['libdl.so', 'libm.so'], exported_symbols={'fclose', 'fopen', 'fread'}, @@ -73,7 +80,6 @@ class ELFLinkerTest(unittest.TestCase): gb.add_multilib(PT_SYSTEM, 'libcutils', dt_needed=['libc.so', 'libdl.so'], - exported_symbols={}, imported_symbols={'dlclose', 'dlopen', 'fclose', 'fopen'}) @@ -93,15 +99,15 @@ class ELFLinkerTest(unittest.TestCase): graph = gb.graph node = graph.map_path_to_lib('/system/lib/libc.so') - self.assertEqual(gb.elf32_libc, node.elf) + self.assertEqual(gb.libc_32, node) self.assertEqual('/system/lib/libc.so', node.path) node = graph.map_path_to_lib('/system/lib64/libdl.so') - self.assertEqual(gb.elf64_libdl, node.elf) + self.assertEqual(gb.libdl_64, node) self.assertEqual('/system/lib64/libdl.so', node.path) node = graph.map_path_to_lib('/vendor/lib64/libEGL.so') - self.assertEqual(gb.elf64_libEGL, node.elf) + self.assertEqual(gb.libEGL_64, node) self.assertEqual('/vendor/lib64/libEGL.so', node.path) self.assertEqual(None, graph.map_path_to_lib('/no/such/path.so')) @@ -157,6 +163,56 @@ class ELFLinkerTest(unittest.TestCase): '/system/lib64/libdl.so'], self._get_paths_from_nodes(node.deps)) + def test_linked_symbols(self): + gb = self._create_normal_graph() + graph = gb.graph + + # Check the unresolved symbols. + for lib_set in (graph.lib32, graph.lib64): + for lib in lib_set.values(): + self.assertEqual(set(), lib.unresolved_symbols) + + # Check the linked symbols. + for lib in ('lib', 'lib64'): + libdl = graph.map_path_to_lib('/system/' + lib + '/libdl.so') + libm = graph.map_path_to_lib('/system/' + lib + '/libm.so') + libc = graph.map_path_to_lib('/system/' + lib + '/libc.so') + libRS = graph.map_path_to_lib('/system/' + lib + '/libRS.so') + libcutils = \ + graph.map_path_to_lib('/system/' + lib + '/libcutils.so') + libEGL = graph.map_path_to_lib('/vendor/' + lib + '/libEGL.so') + + # Check the linked symbols for libc.so. + self.assertIs(libdl, libc.linked_symbols['dlclose']) + self.assertIs(libdl, libc.linked_symbols['dlopen']) + self.assertIs(libm, libc.linked_symbols['cos']) + self.assertIs(libm, libc.linked_symbols['sin']) + + # Check the linked symbols for libRS.so. + self.assertIs(libdl, libRS.linked_symbols['dlclose']) + self.assertIs(libdl, libRS.linked_symbols['dlopen']) + self.assertIs(libdl, libRS.linked_symbols['dlsym']) + + # Check the linked symbols for libcutils.so. + self.assertIs(libdl, libcutils.linked_symbols['dlclose']) + self.assertIs(libdl, libcutils.linked_symbols['dlopen']) + self.assertIs(libc, libcutils.linked_symbols['fclose']) + self.assertIs(libc, libcutils.linked_symbols['fopen']) + + # Check the linked symbols for libEGL.so. + self.assertIs(libc, libEGL.linked_symbols['fclose']) + self.assertIs(libc, libEGL.linked_symbols['fopen']) + + def test_unresolved_symbols(self): + gb = GraphBuilder() + gb.add_lib(PT_SYSTEM, ELF.ELFCLASS64, 'libfoo', dt_needed=[], + exported_symbols={'foo', 'bar'}, + imported_symbols={'__does_not_exist'}) + gb.resolve() + + lib = gb.graph.map_path_to_lib('/system/lib64/libfoo.so') + self.assertEqual({'__does_not_exist'}, lib.unresolved_symbols) + def test_users(self): gb = self._create_normal_graph() graph = gb.graph @@ -185,8 +241,8 @@ class ELFLinkerTest(unittest.TestCase): graph = gb.graph class MockBannedLibs(object): - def get(self, name): - return None + def is_banned(self, name): + return False vndk_core, vndk_indirect, vndk_ext = \ graph.compute_vndk_libs(None, MockBannedLibs()) @@ -197,6 +253,169 @@ class ELFLinkerTest(unittest.TestCase): self.assertEqual([], self._get_paths_from_nodes(vndk_indirect)) self.assertEqual([], self._get_paths_from_nodes(vndk_ext)) + def test_compute_vndk_stable(self): + gb = GraphBuilder() + + # HIDL libraries. + gb.add_multilib(PT_SYSTEM, 'libhidlbase', extra_dir='vndk-stable') + gb.add_multilib(PT_SYSTEM, 'libhidltransport', extra_dir='vndk-stable') + gb.add_multilib(PT_SYSTEM, 'libhidlmemory', extra_dir='vndk-stable') + gb.add_multilib(PT_SYSTEM, 'libfmp', extra_dir='vndk-stable') + gb.add_multilib(PT_SYSTEM, 'libhwbinder', extra_dir='vndk-stable') + + # UI libraries. + # TODO: Add libui.so here. + + gb.resolve() + + # Compute VNDK-stable. + vndk_stable = set( + lib.path for lib in gb.graph.compute_vndk_stable(False)) + + for lib in ('lib', 'lib64'): + # Check HIDL libraries. + self.assertIn('/system/' + lib + '/vndk-stable/libhidlbase.so', + vndk_stable) + self.assertIn('/system/' + lib + '/vndk-stable/libhidltransport.so', + vndk_stable) + self.assertIn('/system/' + lib + '/vndk-stable/libhidlmemory.so', + vndk_stable) + self.assertIn('/system/' + lib + '/vndk-stable/libfmp.so', + vndk_stable) + self.assertIn('/system/' + lib + '/vndk-stable/libhwbinder.so', + vndk_stable) + + # TODO: Check libui.so here. + + def test_compute_vndk_stable_closure(self): + gb = GraphBuilder() + + libc = gb.add_lib(PT_SYSTEM, ELF.ELFCLASS64, 'libc') + + libhidlbase = gb.add_lib(PT_SYSTEM, ELF.ELFCLASS64, 'libhidlbase', + dt_needed=['libfoo.so'], + extra_dir='vndk-stable') + + libfoo = gb.add_lib(PT_SYSTEM, ELF.ELFCLASS64, 'libfoo') + + gb.resolve() + + # Compute VNDK-stable. + vndk_stable = gb.graph.compute_vndk_stable(False) + vndk_stable_closure = gb.graph.compute_vndk_stable(True) + + self.assertSetEqual({libhidlbase}, vndk_stable) + self.assertSetEqual({libhidlbase, libfoo}, vndk_stable_closure) + self.assertNotIn(libc, vndk_stable) + self.assertNotIn(libc, vndk_stable_closure) + + def test_compute_sp_hal(self): + gb = GraphBuilder() + + # HIDL SP-HAL implementation. + gb.add_multilib(PT_SYSTEM, 'gralloc.default', extra_dir='hw') + gb.add_multilib(PT_SYSTEM, 'gralloc.chipset', extra_dir='hw') + gb.add_multilib(PT_SYSTEM, 'android.hardware.graphics.mapper@2.0-impl', + extra_dir='hw') + + # NDK loader libraries should not be considered as SP-HALs. + gb.add_multilib(PT_SYSTEM, 'libvulkan') + gb.add_multilib(PT_SYSTEM, 'libEGL') + gb.add_multilib(PT_SYSTEM, 'libGLESv1_CM') + gb.add_multilib(PT_SYSTEM, 'libGLESv2') + gb.add_multilib(PT_SYSTEM, 'libGLESv3') + + # OpenGL implementation. + gb.add_multilib(PT_VENDOR, 'libEGL_chipset', extra_dir='egl') + gb.add_multilib(PT_VENDOR, 'libGLESv1_CM_chipset', extra_dir='egl') + gb.add_multilib(PT_VENDOR, 'libGLESv2_chipset', extra_dir='egl') + gb.add_multilib(PT_VENDOR, 'libGLESv3_chipset', extra_dir='egl') + + # Renderscript implementation. + gb.add_multilib(PT_VENDOR, 'libRSDriver_chipset') + gb.add_multilib(PT_VENDOR, 'libPVRRS') + + # Vulkan implementation. + gb.add_multilib(PT_VENDOR, 'vulkan.chipset', extra_dir='hw') + + # Some un-related libraries. + gb.add_multilib(PT_SYSTEM, 'libfoo') + gb.add_multilib(PT_VENDOR, 'libfoo') + + gb.resolve() + + # Compute SP-HAL. + sp_hals = set(lib.path for lib in gb.graph.compute_sp_hal(set(), False)) + + for lib in ('lib', 'lib64'): + # Check HIDL SP-HAL implementation. + self.assertIn('/system/' + lib + '/hw/gralloc.default.so', sp_hals) + self.assertIn('/system/' + lib + '/hw/gralloc.chipset.so', sp_hals) + self.assertIn('/system/' + lib + '/hw/' + 'android.hardware.graphics.mapper@2.0-impl.so', + sp_hals) + + + # Check that NDK loaders are not SP-HALs. + self.assertNotIn('/system/' + lib + '/libvulkan.so', sp_hals) + self.assertNotIn('/system/' + lib + '/libEGL.so', sp_hals) + self.assertNotIn('/system/' + lib + '/libGLESv1_CM.so', sp_hals) + self.assertNotIn('/system/' + lib + '/libGLESv2.so', sp_hals) + self.assertNotIn('/system/' + lib + '/libGLESv3.so', sp_hals) + + # Check that OpenGL implementations are SP-HALs. + self.assertIn('/vendor/' + lib + '/egl/libEGL_chipset.so', sp_hals) + self.assertIn('/vendor/' + lib + '/egl/libGLESv1_CM_chipset.so', + sp_hals) + self.assertIn('/vendor/' + lib + '/egl/libGLESv2_chipset.so', + sp_hals) + self.assertIn('/vendor/' + lib + '/egl/libGLESv3_chipset.so', + sp_hals) + + # Check that Renderscript implementations are SP-HALs. + self.assertIn('/vendor/' + lib + '/libRSDriver_chipset.so', sp_hals) + self.assertIn('/vendor/' + lib + '/libPVRRS.so', sp_hals) + + # Check that vulkan implementation are SP-HALs. + self.assertIn('/vendor/' + lib + '/libPVRRS.so', sp_hals) + + # Check that un-related libraries are not SP-HALs. + self.assertNotIn('/system/' + lib + '/libfoo.so', sp_hals) + self.assertNotIn('/vendor/' + lib + '/libfoo.so', sp_hals) + + def test_compute_sp_hal_closure(self): + gb = GraphBuilder() + + libc = gb.add_lib(PT_SYSTEM, ELF.ELFCLASS64, 'libc') + + libhidlbase = gb.add_lib(PT_SYSTEM, ELF.ELFCLASS64, 'libhidlbase') + + libhidltransport = gb.add_lib(PT_SYSTEM, ELF.ELFCLASS64, + 'libhidltransport') + + gralloc_mapper = gb.add_lib( + PT_VENDOR, ELF.ELFCLASS64, + name='android.hardware.graphics.mapper@2.0-impl', + dt_needed=['libhidlbase.so', 'libhidltransport.so', + 'libc.so', 'gralloc_vnd.so'], + extra_dir='sameprocess') + + gralloc_vnd = gb.add_lib(PT_VENDOR, ELF.ELFCLASS64, 'gralloc_vnd') + + gb.resolve() + + vndk_stable = {libhidlbase, libhidltransport} + + sp_hal = gb.graph.compute_sp_hal(vndk_stable, closure=False) + sp_hal_closure = gb.graph.compute_sp_hal(vndk_stable, closure=True) + + self.assertSetEqual({gralloc_mapper}, sp_hal) + + self.assertSetEqual({gralloc_mapper, gralloc_vnd}, sp_hal_closure) + self.assertNotIn(libhidlbase, sp_hal_closure) + self.assertNotIn(libhidltransport, sp_hal_closure) + self.assertNotIn(libc, sp_hal_closure) + if __name__ == '__main__': unittest.main() diff --git a/vndk/tools/definition-tool/tests/test_generic_refs.py b/vndk/tools/definition-tool/tests/test_generic_refs.py index 77f20bd67..dd59f8163 100755 --- a/vndk/tools/definition-tool/tests/test_generic_refs.py +++ b/vndk/tools/definition-tool/tests/test_generic_refs.py @@ -13,72 +13,91 @@ from compat import TemporaryDirectory, makedirs from vndk_definition_tool import GenericRefs -test_dir = None +test_dir_base = None + + +class MockELF(object): + def __init__(self, exported_symbols): + self.exported_symbols = exported_symbols + + +class MockLib(object): + def __init__(self, path, exported_symbols): + self.path = path + self.elf = MockELF(exported_symbols) + class GenericRefsTest(unittest.TestCase): - @classmethod - def setUpClass(cls): - if test_dir: - cls.test_dir = test_dir - else: - cls.tmp_dir = TemporaryDirectory() - cls.test_dir = cls.tmp_dir.name - - cls._build_fixtures() - - @classmethod - def tearDownClass(cls): - if not test_dir: - cls.tmp_dir.cleanup() - - @classmethod - def _build_fixture(cls, path, content): + def _build_file_fixture(self, path, content): makedirs(os.path.dirname(path), exist_ok=True) with open(path, 'w') as f: f.write(content) - @classmethod - def _build_fixtures(cls): - lib32 = os.path.join(cls.test_dir, 'system', 'lib') - lib64 = os.path.join(cls.test_dir, 'system', 'lib64') + def _build_dir_fixtures(self, test_dir): + lib32 = os.path.join(test_dir, 'system', 'lib') + lib64 = os.path.join(test_dir, 'system', 'lib64') for lib_dir in (lib32, lib64): - cls._build_fixture(os.path.join(lib_dir, 'libc.so.sym'), - 'fclose\nfopen\nfread\nfwrite\n') - cls._build_fixture(os.path.join(lib_dir, 'libm.so.sym'), - 'cos\nsin\ntan\n') + self._build_file_fixture(os.path.join(lib_dir, 'libc.so.sym'), + 'fclose\nfopen\nfread\nfwrite\n') + self._build_file_fixture(os.path.join(lib_dir, 'libm.so.sym'), + 'cos\nsin\ntan\n') + + def _build_fixture(self): + res = GenericRefs() + res.add('/system/lib/libc.so', {'fclose', 'fopen', 'fread', 'fwrite'}) + res.add('/system/lib/libm.so', {'cos', 'sin', 'tan'}) + res.add('/system/lib64/libc.so', {'fclose', 'fopen', 'fread', 'fwrite'}) + res.add('/system/lib64/libm.so', {'cos', 'sin', 'tan'}) + return res def test_create_from_dir(self): - g = GenericRefs.create_from_dir(self.test_dir) - self.assertEqual(4, len(g.refs)) + try: + if test_dir_base: + test_dir = test_dir_base + else: + tmp_dir = TemporaryDirectory() + test_dir = tmp_dir.name - self.assertIn('/system/lib/libc.so', g.refs) - self.assertIn('/system/lib/libm.so', g.refs) - self.assertIn('/system/lib64/libc.so', g.refs) - self.assertIn('/system/lib64/libm.so', g.refs) + self._build_dir_fixtures(test_dir) + g = GenericRefs.create_from_dir(test_dir) + self.assertEqual(4, len(g.refs)) - self.assertEqual({'fclose', 'fopen', 'fread', 'fwrite'}, - g.refs['/system/lib/libc.so']) - self.assertEqual({'fclose', 'fopen', 'fread', 'fwrite'}, - g.refs['/system/lib64/libc.so']) + self.assertIn('/system/lib/libc.so', g.refs) + self.assertIn('/system/lib/libm.so', g.refs) + self.assertIn('/system/lib64/libc.so', g.refs) + self.assertIn('/system/lib64/libm.so', g.refs) - self.assertEqual({'cos', 'sin', 'tan'}, - g.refs['/system/lib/libm.so']) - self.assertEqual({'cos', 'sin', 'tan'}, - g.refs['/system/lib64/libm.so']) + self.assertEqual({'fclose', 'fopen', 'fread', 'fwrite'}, + g.refs['/system/lib/libc.so']) + self.assertEqual({'fclose', 'fopen', 'fread', 'fwrite'}, + g.refs['/system/lib64/libc.so']) + self.assertEqual({'cos', 'sin', 'tan'}, + g.refs['/system/lib/libm.so']) + self.assertEqual({'cos', 'sin', 'tan'}, + g.refs['/system/lib64/libm.so']) + finally: + if not test_dir_base: + tmp_dir.cleanup() + + def test_classify_lib(self): + g = self._build_fixture() + + libc_sub = MockLib('/system/lib/libc.so', {'fclose', 'fopen', 'fread'}) + libc_sup = MockLib('/system/lib/libc.so', + {'fclose', 'fopen', 'fread', 'fwrite', 'open'}) + libc_eq = MockLib('/system/lib/libc.so', + {'fclose', 'fopen', 'fread', 'fwrite'}) + libfoo = MockLib('/system/lib/libfoo.so', {}) + + self.assertEqual(GenericRefs.MODIFIED, g.classify_lib(libc_sub)) + self.assertEqual(GenericRefs.EXPORT_SUPER_SET, g.classify_lib(libc_sup)) + self.assertEqual(GenericRefs.EXPORT_EQUAL, g.classify_lib(libc_eq)) + self.assertEqual(GenericRefs.NEW_LIB, g.classify_lib(libfoo)) def test_is_equivalent_lib(self): - g = GenericRefs.create_from_dir(self.test_dir) - - class MockELF(object): - def __init__(self, exported_symbols): - self.exported_symbols = exported_symbols - - class MockLib(object): - def __init__(self, path, exported_symbols): - self.path = path - self.elf = MockELF(exported_symbols) + g = self._build_fixture() libc_sub = MockLib('/system/lib/libc.so', {'fclose', 'fopen', 'fread'}) libc_sup = MockLib('/system/lib/libc.so', @@ -99,10 +118,10 @@ def main(): args, unittest_args = parser.parse_known_args() # Convert command line options. - global test_dir + global test_dir_base if args.test_dir: - test_dir = args.test_dir + test_dir_base = args.test_dir # Run unit test. unittest.main(argv=[sys.argv[0]] + unittest_args) diff --git a/vndk/tools/definition-tool/tests/test_ndk_libs.py b/vndk/tools/definition-tool/tests/test_ndk_libs.py new file mode 100755 index 000000000..7eada1654 --- /dev/null +++ b/vndk/tools/definition-tool/tests/test_ndk_libs.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +from __future__ import print_function + +import os +import sys +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +import unittest + +from vndk_definition_tool import NDK_LIBS + + +class NDKLibDictTest(unittest.TestCase): + def test_is_llndk(self): + self.assertTrue(NDK_LIBS.is_llndk('/system/lib/libc.so')) + self.assertTrue(NDK_LIBS.is_llndk('/system/lib/libdl.so')) + self.assertTrue(NDK_LIBS.is_llndk('/system/lib/liblog.so')) + self.assertTrue(NDK_LIBS.is_llndk('/system/lib/libm.so')) + self.assertTrue(NDK_LIBS.is_llndk('/system/lib/libstdc++.so')) + self.assertTrue(NDK_LIBS.is_llndk('/system/lib/libz.so')) + + self.assertTrue(NDK_LIBS.is_llndk('/system/lib64/libc.so')) + self.assertTrue(NDK_LIBS.is_llndk('/system/lib64/libdl.so')) + self.assertTrue(NDK_LIBS.is_llndk('/system/lib64/liblog.so')) + self.assertTrue(NDK_LIBS.is_llndk('/system/lib64/libm.so')) + self.assertTrue(NDK_LIBS.is_llndk('/system/lib64/libstdc++.so')) + self.assertTrue(NDK_LIBS.is_llndk('/system/lib64/libz.so')) + + self.assertFalse(NDK_LIBS.is_llndk('/system/lib/libz')) + self.assertFalse(NDK_LIBS.is_llndk('/system/lib/libzz.so')) + + def test_is_spndk(self): + self.assertTrue(NDK_LIBS.is_spndk('/system/lib/libEGL.so')) + self.assertTrue(NDK_LIBS.is_spndk('/system/lib/libGLESv1_CM.so')) + self.assertTrue(NDK_LIBS.is_spndk('/system/lib/libGLESv2.so')) + self.assertTrue(NDK_LIBS.is_spndk('/system/lib/libGLESv3.so')) + + self.assertTrue(NDK_LIBS.is_spndk('/system/lib64/libEGL.so')) + self.assertTrue(NDK_LIBS.is_spndk('/system/lib64/libGLESv1_CM.so')) + self.assertTrue(NDK_LIBS.is_spndk('/system/lib64/libGLESv2.so')) + self.assertTrue(NDK_LIBS.is_spndk('/system/lib64/libGLESv3.so')) + + # Vendor libraries with the same name are still not SP-NDK. + self.assertFalse(NDK_LIBS.is_spndk('/vendor/lib64/libEGL.so')) + self.assertFalse(NDK_LIBS.is_spndk('/vendor/lib64/libGLESv1_CM.so')) + self.assertFalse(NDK_LIBS.is_spndk('/vendor/lib64/libGLESv2.so')) + self.assertFalse(NDK_LIBS.is_spndk('/vendor/lib64/libGLESv3.so')) + + self.assertFalse(NDK_LIBS.is_spndk('/vendor/lib64/egl/libEGL.so')) + self.assertFalse(NDK_LIBS.is_spndk('/vendor/lib64/egl/libGLESv1_CM.so')) + self.assertFalse(NDK_LIBS.is_spndk('/vendor/lib64/egl/libGLESv2.so')) + self.assertFalse(NDK_LIBS.is_spndk('/vendor/lib64/egl/libGLESv3.so')) + + # LL-NDK is not SP-NDK. + self.assertFalse(NDK_LIBS.is_spndk('/system/lib/libc.so')) + + def test_is_hlndk(self): + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib/libOpenMAXAL.so')) + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib/libOpenSLES.so')) + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib/libandroid.so')) + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib/libcamera2ndk.so')) + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib/libjnigraphics.so')) + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib/libmediandk.so')) + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib/libvulkan.so')) + + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib64/libOpenMAXAL.so')) + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib64/libOpenSLES.so')) + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib64/libandroid.so')) + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib64/libcamera2ndk.so')) + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib64/libjnigraphics.so')) + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib64/libmediandk.so')) + self.assertTrue(NDK_LIBS.is_hlndk('/system/lib64/libvulkan.so')) + + # LL-NDK and SP-NDK are not HL-NDK. + self.assertFalse(NDK_LIBS.is_hlndk('/system/lib/libc.so')) + self.assertFalse(NDK_LIBS.is_hlndk('/system/lib/libEGL.so')) + self.assertFalse(NDK_LIBS.is_hlndk('/system/lib/libGLESv1_CM.so')) + self.assertFalse(NDK_LIBS.is_hlndk('/system/lib/libGLESv2.so')) + self.assertFalse(NDK_LIBS.is_hlndk('/system/lib/libGLESv3.so')) + + def test_is_ndk(self): + # LL-NDK + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libc.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libdl.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/liblog.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libm.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libstdc++.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libz.so')) + + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libc.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libdl.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/liblog.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libm.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libstdc++.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libz.so')) + + # SP-NDK + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libEGL.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libGLESv1_CM.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libGLESv2.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libGLESv3.so')) + + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libEGL.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libGLESv1_CM.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libGLESv2.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libGLESv3.so')) + + # HL-NDK + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libOpenMAXAL.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libOpenSLES.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libandroid.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libcamera2ndk.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libjnigraphics.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libmediandk.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib/libvulkan.so')) + + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libOpenMAXAL.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libOpenSLES.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libandroid.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libcamera2ndk.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libjnigraphics.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libmediandk.so')) + self.assertTrue(NDK_LIBS.is_ndk('/system/lib64/libvulkan.so')) + + +if __name__ == '__main__': + unittest.main() diff --git a/vndk/tools/definition-tool/vndk_definition_tool.py b/vndk/tools/definition-tool/vndk_definition_tool.py index f6895f093..1eeca88bc 100755 --- a/vndk/tools/definition-tool/vndk_definition_tool.py +++ b/vndk/tools/definition-tool/vndk_definition_tool.py @@ -406,26 +406,66 @@ class ELF(object): # NDK and Banned Libraries #------------------------------------------------------------------------------ -NDK_LOW_LEVEL = { - 'libc.so', 'libstdc++.so', 'libdl.so', 'liblog.so', 'libm.so', 'libz.so', -} +class NDKLibDict(object): + LLNDK_LIB_NAMES = ( + 'libc.so', + 'libdl.so', + 'liblog.so', + 'libm.so', + 'libstdc++.so', + 'libz.so', + ) + SPNDK_LIB_NAMES = ( + 'libEGL.so', + 'libGLESv1_CM.so', + 'libGLESv2.so', + 'libGLESv3.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', -} + HLNDK_LIB_NAMES = ( + 'libOpenMAXAL.so', + 'libOpenSLES.so', + 'libandroid.so', + 'libcamera2ndk.so', + 'libjnigraphics.so', + 'libmediandk.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 + @staticmethod + def _compile_path_matcher(names): + patts = '|'.join('(?:^\\/system\\/lib(?:64)?\\/' + re.escape(i) + '$)' + for i in names) + return re.compile(patts) + + def __init__(self): + self.llndk_patterns = self._compile_path_matcher(self.LLNDK_LIB_NAMES) + self.spndk_patterns = self._compile_path_matcher(self.SPNDK_LIB_NAMES) + self.hlndk_patterns = self._compile_path_matcher(self.HLNDK_LIB_NAMES) + self.ndk_patterns = self._compile_path_matcher( + self.LLNDK_LIB_NAMES + self.SPNDK_LIB_NAMES + + self.HLNDK_LIB_NAMES) + + def is_ndk(self, path): + return self.ndk_patterns.match(path) + + def is_llndk(self, path): + return self.llndk_patterns.match(path) + + def is_spndk(self, path): + return self.spndk_patterns.match(path) + + def is_hlndk(self, path): + return self.hlndk_patterns.match(path) + +NDK_LIBS = NDKLibDict() BannedLib = collections.namedtuple( 'BannedLib', ('name', 'reason', 'action',)) -BA_WARN = 0 +BA_WARN = 0 BA_EXCLUDE = 1 class BannedLibDict(object): @@ -436,7 +476,10 @@ class BannedLibDict(object): self.banned_libs[name] = BannedLib(name, reason, action) def get(self, name): - return self.banned_libs.get(name, None) + return self.banned_libs.get(name) + + def is_banned(self, path): + return self.get(os.path.basename(path)) @staticmethod def create_default(): @@ -502,7 +545,9 @@ class ELFLinkData(object): self.elf = elf self.deps = set() self.users = set() - self.is_ndk = _is_ndk_lib(path) + self.is_ndk = NDK_LIBS.is_ndk(path) + self.unresolved_symbols = set() + self.linked_symbols = dict() def add_dep(self, dst): self.deps.add(dst) @@ -528,6 +573,7 @@ class ELFLinker(object): else: self.lib64[path] = node self.lib_pt[partition][path] = node + return node def add_dep(self, src_path, dst_path): for lib_set in (self.lib32, self.lib64): @@ -587,7 +633,24 @@ class ELFLinker(object): if match: self.add_dep(match.group(1), match.group(2)) + def _find_exported_symbol(self, symbol, libs): + """Find the shared library with the exported symbol.""" + for lib in libs: + if symbol in lib.elf.exported_symbols: + return lib + return None + + def _resolve_lib_imported_symbols(self, lib, imported_libs): + """Resolve the imported symbols in a library.""" + for symbol in lib.elf.imported_symbols: + imported_lib = self._find_exported_symbol(symbol, imported_libs) + if imported_lib: + lib.linked_symbols[symbol] = imported_lib + else: + lib.unresolved_symbols.add(symbol) + 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, lib.elf.dt_runpath) @@ -598,9 +661,12 @@ class ELFLinker(object): .format(lib.path, dt_needed, candidates), file=sys.stderr) continue lib.add_dep(dep) + imported_libs.append(dep) + return imported_libs def _resolve_lib_deps(self, lib, resolver): - self._resolve_lib_dt_needed(lib, resolver) + imported_libs = self._resolve_lib_dt_needed(lib, resolver) + self._resolve_lib_imported_symbols(lib, imported_libs) def _resolve_lib_set_deps(self, lib_set, resolver): for lib in lib_set.values(): @@ -615,6 +681,73 @@ class ELFLinker(object): self.lib64, ELFResolver(self.lib64, ['/system/lib64', '/vendor/lib64'])) + def compute_matched_libs(self, path_patterns, closure=False, + is_excluded_libs=None): + patt = re.compile('|'.join('(?:' + p + ')' for p in path_patterns)) + + # Find libraries with matching paths. + libs = set() + for lib_set in self.lib_pt: + for lib in lib_set.values(): + if patt.match(lib.path): + libs.add(lib) + + if closure: + # Compute transitive closure. + if not is_excluded_libs: + def is_excluded_libs(lib): + return False + libs = self.compute_closure(libs, is_excluded_libs) + + return libs + + def compute_vndk_stable(self, closure): + """Find all vndk stable libraries.""" + + path_patterns = ( + # HIDL libraries used by android.hardware.graphics.mapper@2.0-impl. + '^.*/libhidlbase\\.so$', + '^.*/libhidltransport\\.so$', + '^.*/libhidlmemory\\.so$', + '^.*/libfmp\\.so$', + '^.*/libhwbinder\\.so$', + + # UI libraries used by libEGL. + #'^.*/libui\\.so$', + #'^.*/libnativewindow\\.so$', + ) + + def is_excluded_libs(lib): + return lib.is_ndk + + return self.compute_matched_libs(path_patterns, closure, + is_excluded_libs) + + def compute_sp_hal(self, vndk_stable, closure): + """Find all same-process HALs.""" + + path_patterns = ( + # OpenGL-related + '^/vendor/.*/libEGL_.*\\.so$', + '^/vendor/.*/libGLESv1_CM_.*\\.so$', + '^/vendor/.*/libGLESv2_.*\\.so$', + '^/vendor/.*/libGLESv3_.*\\.so$', + # Vulkan + '^/vendor/.*/vulkan.*\\.so$', + # libRSDriver + '^/vendor/.*/libRSDriver.*\\.so$', + '^/vendor/.*/libPVRRS\\.so$', + # Gralloc mapper + '^.*/gralloc\\..*\\.so$', + '^.*/android\\.hardware\\.graphics\\.mapper@\\d+\\.\\d+-impl\\.so$', + ) + + def is_excluded_libs(lib): + return lib.is_ndk or lib in vndk_stable + + return self.compute_matched_libs(path_patterns, closure, + is_excluded_libs) + def compute_vndk_libs(self, generic_refs, banned_libs): vndk_core = set() vndk_ext = set() @@ -636,7 +769,7 @@ class ELFLinker(object): # Remove NDK libraries and banned libraries. def is_not_vndk(lib): - return lib.is_ndk or banned_libs.get(os.path.basename(lib.path)) + return lib.is_ndk or banned_libs.is_banned(lib.path) def remove_ndk_libs(libs): return set(lib for lib in libs if not is_not_vndk(lib)) @@ -731,9 +864,17 @@ class ELFLinker(object): #------------------------------------------------------------------------------ class GenericRefs(object): + NEW_LIB = 0 + EXPORT_EQUAL = 1 + EXPORT_SUPER_SET = 2 + MODIFIED = 3 + def __init__(self): self.refs = dict() + def add(self, name, symbols): + self.refs[name] = symbols + def _load_from_dir(self, root): root = os.path.abspath(root) prefix_len = len(root) + 1 @@ -744,7 +885,7 @@ class GenericRefs(object): path = os.path.join(base, filename) lib_name = '/' + path[prefix_len:-4] with open(path, 'r') as f: - self.refs[lib_name] = set(line.strip() for line in f) + self.add(lib_name, set(line.strip() for line in f)) @staticmethod def create_from_dir(root): @@ -752,8 +893,19 @@ class GenericRefs(object): result._load_from_dir(root) return result + def classify_lib(self, lib): + ref_lib_symbols = self.refs.get(lib.path) + if not ref_lib_symbols: + return GenericRefs.NEW_LIB + exported_symbols = lib.elf.exported_symbols + if exported_symbols == ref_lib_symbols: + return GenericRefs.EXPORT_EQUAL + if exported_symbols > ref_lib_symbols: + return GenericRefs.EXPORT_SUPER_SET + return GenericRefs.MODIFIED + def is_equivalent_lib(self, lib): - return self.refs.get(lib.path) == lib.elf.exported_symbols + return self.classify_lib(lib) == GenericRefs.EXPORT_EQUAL #------------------------------------------------------------------------------ @@ -893,15 +1045,14 @@ class VNDKCommand(ELFGraphCommand): 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: + if NDK_LIBS.is_hlndk(dep.path): 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: - banned = banned_libs.get(os.path.basename(dep.path)) + banned = banned_libs.is_banned(dep.path) if banned: print('warning: {}: Vendor binary depends on banned {} ' '(reason: {})'.format( @@ -1039,6 +1190,28 @@ class DepsClosureCommand(ELFGraphCommand): return 0 +class VNDKStableCommand(ELFGraphCommand): + def __init__(self): + super(VNDKStableCommand, self).__init__( + 'vndk-stable', help='Find transitive closure of VNDK stable') + + def add_argparser_options(self, parser): + super(VNDKStableCommand, self).add_argparser_options(parser) + + parser.add_argument('--closure', action='store_true', + help='show the closure') + + def main(self, args): + graph = ELFLinker.create(args.system, args.system_dir_as_vendor, + args.vendor, args.vendor_dir_as_system, + args.load_extra_deps) + + vndk_stable = graph.compute_vndk_stable(closure=args.closure) + for lib in sorted_lib_path_list(vndk_stable): + print(lib) + return 0 + + class SpHalCommand(ELFGraphCommand): def __init__(self): super(SpHalCommand, self).__init__( @@ -1055,39 +1228,8 @@ class SpHalCommand(ELFGraphCommand): args.vendor, args.vendor_dir_as_system, args.load_extra_deps) - # Find SP HALs. - name_patterns = ( - # OpenGL-related - '^/vendor/.*/libEGL_.*\\.so$', - '^/vendor/.*/libGLESv1_CM_.*\\.so$', - '^/vendor/.*/libGLESv2_.*\\.so$', - '^/vendor/.*/libGLESv3_.*\\.so$', - # Vulkan - '^/vendor/.*/vulkan.*\\.so$', - # libRSDriver - '^/vendor/.*/libRSDriver.*\\.so$', - '^/vendor/.*/libPVRRS\\.so$', - # Gralloc mapper - '^.*/gralloc\\..*\\.so$', - '^.*/android\\.hardware\\.graphics\\.mapper@\\d+\\.\\d+-impl\\.so$', - ) - - patt = re.compile('|'.join('(?:' + p + ')' for p in name_patterns)) - - # Find root/excluded libraries by their paths. - sp_hals = set() - for lib_set in graph.lib_pt: - for lib in lib_set.values(): - if patt.match(lib.path): - sp_hals.add(lib) - - # Compute the closure (if specified). - if args.closure: - def is_excluded_libs(lib): - return lib.is_ndk - sp_hals = graph.compute_closure(sp_hals, is_excluded_libs) - - # Print the result. + vndk_stable = graph.compute_vndk_stable(closure=True) + sp_hals = graph.compute_sp_hal(vndk_stable, closure=args.closure) for lib in sorted_lib_path_list(sp_hals): print(lib) return 0 @@ -1109,6 +1251,7 @@ def main(): register_subcmd(DepsCommand()) register_subcmd(DepsClosureCommand()) register_subcmd(SpHalCommand()) + register_subcmd(VNDKStableCommand()) args = parser.parse_args() if not args.subcmd: