From 48549b385f257615aff6f3e15f4630ba6119fbac Mon Sep 17 00:00:00 2001 From: Logan Chien Date: Mon, 20 Nov 2017 15:33:43 +0800 Subject: [PATCH] vndk-def: Add regex support to extra deps format This commit adds regular expression matching and ${LIB} substitution to extra deps file format (`--load-extra-deps`). Test: ./tests/test_elf_linker.py Test: ./tests/run.py Change-Id: I9393d3de50a80adc0d0a947ada8c7197d364e48e --- .../datasets/minimum_dlopen_deps.txt | 18 +-- .../definition-tool/tests/test_elf_linker.py | 111 +++++++++++++++++- vndk/tools/definition-tool/tests/test_vndk.py | 2 +- .../definition-tool/vndk_definition_tool.py | 45 ++++--- 4 files changed, 147 insertions(+), 29 deletions(-) diff --git a/vndk/tools/definition-tool/datasets/minimum_dlopen_deps.txt b/vndk/tools/definition-tool/datasets/minimum_dlopen_deps.txt index b1f057cae..d850c1319 100644 --- a/vndk/tools/definition-tool/datasets/minimum_dlopen_deps.txt +++ b/vndk/tools/definition-tool/datasets/minimum_dlopen_deps.txt @@ -1,12 +1,6 @@ -/system/lib/libEGL.so:/system/lib/libEGL.so -/system/lib/libEGL.so:/system/lib/libGLESv1_CM.so -/system/lib/libEGL.so:/system/lib/libGLESv2.so -/system/lib/libc.so:/system/lib/libc_malloc_debug.so -/system/lib/libc.so:/system/lib/libicuuc.so -/system/lib/libc.so:/system/lib/libnetd_client.so -/system/lib64/libEGL.so:/system/lib64/libEGL.so -/system/lib64/libEGL.so:/system/lib64/libGLESv1_CM.so -/system/lib64/libEGL.so:/system/lib64/libGLESv2.so -/system/lib64/libc.so:/system/lib64/libc_malloc_debug.so -/system/lib64/libc.so:/system/lib64/libicuuc.so -/system/lib64/libc.so:/system/lib64/libnetd_client.so +/system/${LIB}/libEGL.so:/system/${LIB}/libEGL.so +/system/${LIB}/libEGL.so:/system/${LIB}/libGLESv1_CM.so +/system/${LIB}/libEGL.so:/system/${LIB}/libGLESv2.so +/system/${LIB}/libc.so:/system/${LIB}/libc_malloc_debug.so +/system/${LIB}/libc.so:/system/${LIB}/libicuuc.so +/system/${LIB}/libc.so:/system/${LIB}/libnetd_client.so diff --git a/vndk/tools/definition-tool/tests/test_elf_linker.py b/vndk/tools/definition-tool/tests/test_elf_linker.py index 10d9bdd04..d2dbe2409 100755 --- a/vndk/tools/definition-tool/tests/test_elf_linker.py +++ b/vndk/tools/definition-tool/tests/test_elf_linker.py @@ -2,11 +2,12 @@ import os import sys +import tempfile import unittest sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from compat import StringIO +from compat import StringIO, patch from utils import GraphBuilder from vndk_definition_tool import (ELF, GenericRefs, PT_SYSTEM, PT_VENDOR) @@ -41,9 +42,11 @@ class ELFLinkerTest(unittest.TestCase): gb.resolve() return gb + def _get_paths_from_nodes(self, nodes): return sorted([node.path for node in nodes]) + def test_get_lib(self): gb = self._create_normal_graph() graph = gb.graph @@ -62,6 +65,7 @@ class ELFLinkerTest(unittest.TestCase): self.assertEqual(None, graph.get_lib('/no/such/path.so')) + def test_map_paths_to_libs(self): gb = self._create_normal_graph() graph = gb.graph @@ -81,6 +85,7 @@ class ELFLinkerTest(unittest.TestCase): self.assertEqual(['/system/lib64/libdl.so'], self._get_paths_from_nodes(nodes)) + def test_elf_class_and_partitions(self): gb = self._create_normal_graph() graph = gb.graph @@ -89,6 +94,7 @@ class ELFLinkerTest(unittest.TestCase): self.assertEqual(1, len(graph.lib_pt[PT_VENDOR].lib32)) self.assertEqual(1, len(graph.lib_pt[PT_VENDOR].lib64)) + def test_deps(self): gb = self._create_normal_graph() graph = gb.graph @@ -109,6 +115,7 @@ class ELFLinkerTest(unittest.TestCase): '/system/lib64/libdl.so'], self._get_paths_from_nodes(node.deps_all)) + def test_linked_symbols(self): gb = self._create_normal_graph() graph = gb.graph @@ -149,6 +156,7 @@ class ELFLinkerTest(unittest.TestCase): 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=[], @@ -159,6 +167,7 @@ class ELFLinkerTest(unittest.TestCase): lib = gb.graph.get_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 @@ -182,6 +191,7 @@ class ELFLinkerTest(unittest.TestCase): node = graph.get_lib('/vendor/lib64/libEGL.so') self.assertEqual([], self._get_paths_from_nodes(node.users_all)) + def test_compute_predefined_sp_hal(self): gb = GraphBuilder() @@ -259,6 +269,7 @@ class ELFLinkerTest(unittest.TestCase): self.assertNotIn('/system/' + lib + '/libfoo.so', sp_hals) self.assertNotIn('/vendor/' + lib + '/libfoo.so', sp_hals) + def test_compute_sp_lib(self): # Create graph. gb = GraphBuilder() @@ -366,5 +377,103 @@ class ELFLinkerTest(unittest.TestCase): self.assertNotIn(libc_path, sp_ndk_indirect) +class ELFLinkerDlopenDepsTest(unittest.TestCase): + def test_add_dlopen_deps(self): + gb = GraphBuilder() + liba = gb.add_lib32(PT_SYSTEM, 'liba') + libb = gb.add_lib32(PT_SYSTEM, 'libb') + gb.resolve() + + with tempfile.NamedTemporaryFile(mode='w') as tmp_file: + tmp_file.write('/system/lib/liba.so: /system/lib/libb.so') + tmp_file.seek(0) + gb.graph.add_dlopen_deps(tmp_file.name) + + self.assertIn(libb, liba.deps_dlopen) + self.assertIn(liba, libb.users_dlopen) + + self.assertNotIn(libb, liba.deps_needed) + self.assertNotIn(liba, libb.users_needed) + + + def test_add_dlopen_deps_lib_subst(self): + gb = GraphBuilder() + liba_32, liba_64 = gb.add_multilib(PT_SYSTEM, 'liba') + libb_32, libb_64 = gb.add_multilib(PT_SYSTEM, 'libb') + gb.resolve() + + with tempfile.NamedTemporaryFile(mode='w') as tmp_file: + tmp_file.write('/system/${LIB}/liba.so: /system/${LIB}/libb.so') + tmp_file.seek(0) + gb.graph.add_dlopen_deps(tmp_file.name) + + self.assertIn(libb_32, liba_32.deps_dlopen) + self.assertIn(liba_32, libb_32.users_dlopen) + + self.assertIn(libb_64, liba_64.deps_dlopen) + self.assertIn(liba_64, libb_64.users_dlopen) + + + def test_add_dlopen_deps_lib_subset_single_bitness(self): + gb = GraphBuilder() + liba_32, liba_64 = gb.add_multilib(PT_SYSTEM, 'liba') + libb_32 = gb.add_lib32(PT_SYSTEM, 'libb') + gb.resolve() + + with tempfile.NamedTemporaryFile(mode='w') as tmp_file: + tmp_file.write('/system/${LIB}/libb.so: /system/${LIB}/liba.so') + tmp_file.seek(0) + + stderr = StringIO() + with patch('sys.stderr', stderr): + gb.graph.add_dlopen_deps(tmp_file.name) + + self.assertEqual('', stderr.getvalue()) + + self.assertIn(liba_32, libb_32.deps_dlopen) + self.assertIn(libb_32, liba_32.users_dlopen) + + self.assertEqual(0, len(liba_64.users_dlopen)) + + + def test_add_dlopen_deps_regex(self): + gb = GraphBuilder() + liba = gb.add_lib32(PT_SYSTEM, 'liba') + libb = gb.add_lib32(PT_SYSTEM, 'libb') + gb.resolve() + + with tempfile.NamedTemporaryFile(mode='w') as tmp_file: + tmp_file.write('[regex].*libb\\.so: [regex].*/${LIB}/liba\\.so') + tmp_file.seek(0) + + stderr = StringIO() + with patch('sys.stderr', stderr): + gb.graph.add_dlopen_deps(tmp_file.name) + + self.assertEqual('', stderr.getvalue()) + + self.assertIn(liba, libb.deps_dlopen) + self.assertIn(libb, liba.users_dlopen) + + + def test_add_dlopen_deps_error(self): + gb = GraphBuilder() + liba = gb.add_lib32(PT_SYSTEM, 'liba') + libb = gb.add_lib32(PT_SYSTEM, 'libb') + gb.resolve() + + with tempfile.NamedTemporaryFile(mode='w') as tmp_file: + tmp_file.write('/system/lib/libc.so: /system/lib/libd.so') + tmp_file.seek(0) + + stderr = StringIO() + with patch('sys.stderr', stderr): + gb.graph.add_dlopen_deps(tmp_file.name) + + self.assertRegexpMatches( + stderr.getvalue(), + 'error: Failed to add dlopen dependency from .* to .*\\.\n') + + if __name__ == '__main__': unittest.main() diff --git a/vndk/tools/definition-tool/tests/test_vndk.py b/vndk/tools/definition-tool/tests/test_vndk.py index 29d3ef934..26ce0e1f5 100755 --- a/vndk/tools/definition-tool/tests/test_vndk.py +++ b/vndk/tools/definition-tool/tests/test_vndk.py @@ -31,7 +31,7 @@ class ELFLinkerVNDKTest(unittest.TestCase): with patch('sys.stderr', stderr): gb.graph.normalize_partition_tags(set(), None) - self.assertRegex( + self.assertRegexpMatches( stderr.getvalue(), 'error: .*: system exe/lib must not depend on vendor lib .*. ' 'Assume such dependency does not exist.') diff --git a/vndk/tools/definition-tool/vndk_definition_tool.py b/vndk/tools/definition-tool/vndk_definition_tool.py index 8613e85fd..9841c6957 100755 --- a/vndk/tools/definition-tool/vndk_definition_tool.py +++ b/vndk/tools/definition-tool/vndk_definition_tool.py @@ -1144,21 +1144,36 @@ class ELFLinker(object): return lib def add_dlopen_dep(self, src_path, dst_path): + num_matches = 0 for elf_class in (ELF.ELFCLASS32, ELF.ELFCLASS64): - src = self.get_lib_in_elf_class(elf_class, src_path) - dst = self.get_lib_in_elf_class(elf_class, dst_path) - if src and dst: + srcs = self._get_libs_in_elf_class(elf_class, src_path) + dsts = self._get_libs_in_elf_class(elf_class, dst_path) + for src, dst in itertools.product(srcs, dsts): src.add_dlopen_dep(dst) - return - print('error: cannot add dependency from {} to {}.' - .format(src_path, dst_path), file=sys.stderr) + num_matches += 1 + if num_matches == 0: + print('error: Failed to add dlopen dependency from {} to {}.' + .format(src_path, dst_path), file=sys.stderr) - def get_lib_in_elf_class(self, elf_class, path, default=None): - for partition in range(NUM_PARTITIONS): - res = self.lib_pt[partition].get_lib_dict(elf_class).get(path) - if res: - return res - return default + def _get_libs_in_elf_class(self, elf_class, path): + result = set() + if '${LIB}' in path: + lib_dir = 'lib' if elf_class == ELF.ELFCLASS32 else 'lib64' + path = path.replace('${LIB}', lib_dir) + if path.startswith('[regex]'): + patt = re.compile(path[7:]) + for partition in range(NUM_PARTITIONS): + lib_set = self.lib_pt[partition].get_lib_dict(elf_class) + for path ,lib in lib_set.items(): + if patt.match(path): + result.add(lib) + else: + for partition in range(NUM_PARTITIONS): + lib_set = self.lib_pt[partition].get_lib_dict(elf_class) + lib = lib_set.get(path) + if lib: + result.add(lib) + return result def get_lib(self, path): for lib_set in self.lib_pt: @@ -1222,7 +1237,7 @@ class ELFLinker(object): else: self.add_lib(partition, short_path, elf) - def load_extra_deps(self, path): + def add_dlopen_deps(self, path): patt = re.compile('([^:]*):\\s*(.*)') with open(path, 'r') as f: for line in f: @@ -1812,7 +1827,7 @@ class ELFLinker(object): if extra_deps: for path in extra_deps: - graph.load_extra_deps(path) + graph.add_dlopen_deps(path) graph.resolve_deps(generic_refs) @@ -2112,7 +2127,7 @@ class VNDKCommandBase(ELFGraphCommand): script_dir = os.path.dirname(os.path.abspath(__file__)) minimum_dlopen_deps = os.path.join(script_dir, 'datasets', 'minimum_dlopen_deps.txt') - graph.load_extra_deps(minimum_dlopen_deps) + graph.add_dlopen_deps(minimum_dlopen_deps) return (generic_refs, graph, tagged_paths)