Merge changes I3073efe9,I3b0f06e3,I4ca80033,I3bc23fd4,I327a085d, ...

* changes:
  vndk-def: Add GenericRefs.classify_lib().
  vndk-def: Link imported symbols.
  vndk-def: Add vndk-stable finder.
  vndk-def: Extract sp-hal finder.
  vndk-def: Return ELFLinkData for unit test.
  vndk-def: Refactor BannedLibDict and add unittests.
  vndk-def: Separate SP-NDK from HL-NDK.
This commit is contained in:
Treehugger Robot
2017-03-08 01:53:40 +00:00
committed by Gerrit Code Review
5 changed files with 681 additions and 130 deletions

View File

@@ -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()

View File

@@ -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()

View File

@@ -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)

View File

@@ -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()

View File

@@ -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: