vndk-def: Split DT_NEEDED and dlopen() into two layers.

Test: ./tests/test_elf_link_data.py

Change-Id: I746b07a422ea53c38e3f05d259814edf120ded22
This commit is contained in:
Logan Chien
2017-03-06 17:21:07 +08:00
parent 984bea8f00
commit e7764858a8
2 changed files with 152 additions and 10 deletions

View File

@@ -0,0 +1,88 @@
#!/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 ELFLinkData, PT_SYSTEM, PT_VENDOR
class ELFLinkDataTest(unittest.TestCase):
def setUp(self):
self.x = ELFLinkData(PT_SYSTEM, '/system/lib/libx.so', None)
self.y = ELFLinkData(PT_SYSTEM, '/system/lib/liby.so', None)
self.z = ELFLinkData(PT_SYSTEM, '/system/lib/libz.so', None)
self.w = ELFLinkData(PT_SYSTEM, '/system/lib/libw.so', None)
self.v = ELFLinkData(PT_VENDOR, '/vendor/lib/libv.so', None)
self.x.add_dep(self.y, ELFLinkData.NEEDED)
self.x.add_dep(self.z, ELFLinkData.DLOPEN)
self.z.add_dep(self.w, ELFLinkData.NEEDED)
self.z.add_dep(self.w, ELFLinkData.DLOPEN)
def test_add_dep_and_accessors(self):
self.assertIn(self.y, self.x.dt_deps)
self.assertIn(self.x, self.y.dt_users)
self.assertNotIn(self.y, self.x.dl_deps)
self.assertNotIn(self.x, self.y.dl_users)
self.assertIn(self.z, self.x.dl_deps)
self.assertIn(self.x, self.z.dl_users)
self.assertNotIn(self.z, self.x.dt_deps)
self.assertNotIn(self.x, self.z.dt_users)
def test_remove_dep(self):
self.assertIn(self.y, self.x.dt_deps)
self.assertIn(self.x, self.y.dt_users)
with self.assertRaises(KeyError):
self.x.remove_dep(self.y, ELFLinkData.DLOPEN)
self.assertIn(self.y, self.x.dt_deps)
self.assertIn(self.x, self.y.dt_users)
self.x.remove_dep(self.y, ELFLinkData.NEEDED)
self.assertNotIn(self.y, self.x.dt_deps)
self.assertNotIn(self.x, self.y.dt_users)
def test_num_deps(self):
self.assertEqual(2, self.x.num_deps)
self.assertEqual(0, self.y.num_deps)
self.assertEqual(0, self.w.num_deps)
self.assertEqual(0, self.v.num_deps)
# NEEDED and DLOPEN are counted twice.
self.assertEqual(2, self.z.num_deps)
def test_num_users(self):
self.assertEqual(0, self.x.num_users)
self.assertEqual(1, self.y.num_users)
self.assertEqual(1, self.z.num_users)
self.assertEqual(0, self.v.num_users)
# NEEDED and DLOPEN are counted twice.
self.assertEqual(2, self.w.num_users)
def test_has_dep(self):
self.assertTrue(self.x.has_dep(self.y))
self.assertTrue(self.x.has_dep(self.z))
self.assertFalse(self.x.has_dep(self.x))
self.assertFalse(self.x.has_dep(self.w))
def test_has_user(self):
self.assertTrue(self.y.has_user(self.x))
self.assertTrue(self.z.has_user(self.x))
self.assertFalse(self.x.has_user(self.x))
self.assertFalse(self.w.has_user(self.x))
def test_is_system_lib(self):
self.assertTrue(self.x.is_system_lib())
self.assertFalse(self.v.is_system_lib())
if __name__ == '__main__':
unittest.main()

View File

@@ -4,6 +4,7 @@ from __future__ import print_function
import argparse
import collections
import itertools
import os
import re
import stat
@@ -539,19 +540,71 @@ class ELFResolver(object):
class ELFLinkData(object):
NEEDED = 0 # Dependencies recorded in DT_NEEDED entries.
DLOPEN = 1 # Dependencies introduced by dlopen().
def __init__(self, partition, path, elf):
self.partition = partition
self.path = path
self.elf = elf
self.deps = set()
self.users = set()
self._deps = (set(), set())
self._users = (set(), set())
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)
dst.users.add(self)
def add_dep(self, dst, ty):
self._deps[ty].add(dst)
dst._users[ty].add(self)
def remove_dep(self, dst, ty):
self._deps[ty].remove(dst)
dst._users[ty].remove(self)
@property
def num_deps(self):
"""Get the number of dependencies. If a library is linked by both
NEEDED and DLOPEN relationship, then it will be counted twice."""
return sum(len(deps) for deps in self._deps)
@property
def deps(self):
return itertools.chain.from_iterable(self._deps)
@property
def dt_deps(self):
return self._deps[self.NEEDED]
@property
def dl_deps(self):
return self._deps[self.DLOPEN]
@property
def num_users(self):
"""Get the number of users. If a library is linked by both NEEDED and
DLOPEN relationship, then it will be counted twice."""
return sum(len(users) for users in self._users)
@property
def users(self):
return itertools.chain.from_iterable(self._users)
@property
def dt_users(self):
return self._users[self.NEEDED]
@property
def dl_users(self):
return self._users[self.DLOPEN]
def has_dep(self, dst):
return any(dst in deps for deps in self._deps)
def has_user(self, dst):
return any(dst in users for users in self._users)
def is_system_lib(self):
return self.partition == PT_SYSTEM
def sorted_lib_path_list(libs):
@@ -575,12 +628,12 @@ class ELFLinker(object):
self.lib_pt[partition][path] = node
return node
def add_dep(self, src_path, dst_path):
def add_dep(self, src_path, dst_path, ty):
for lib_set in (self.lib32, self.lib64):
src = lib_set.get(src_path)
dst = lib_set.get(dst_path)
if src and dst:
src.add_dep(dst)
src.add_dep(dst, ty)
def map_path_to_lib(self, path):
for lib_set in (self.lib32, self.lib64):
@@ -631,7 +684,8 @@ class ELFLinker(object):
for line in f:
match = patt.match(line)
if match:
self.add_dep(match.group(1), match.group(2))
self.add_dep(match.group(1), match.group(2),
ELFLinkData.DLOPEN)
def _find_exported_symbol(self, symbol, libs):
"""Find the shared library with the exported symbol."""
@@ -660,7 +714,7 @@ class ELFLinker(object):
print('warning: {}: Missing needed library: {} Tried: {}'
.format(lib.path, dt_needed, candidates), file=sys.stderr)
continue
lib.add_dep(dep)
lib.add_dep(dep, ELFLinkData.NEEDED)
imported_libs.append(dep)
return imported_libs
@@ -1025,7 +1079,7 @@ class VNDKCommand(ELFGraphCommand):
def _warn_incorrect_partition_lib_set(self, lib_set, partition, error_msg):
for lib in lib_set.values():
if not lib.users:
if not lib.num_users:
continue
if all((user.partition != partition for user in lib.users)):
print(error_msg.format(lib.path), file=sys.stderr)