457 lines
18 KiB
Python
Executable File
457 lines
18 KiB
Python
Executable File
#!/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 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)
|
|
|
|
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=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):
|
|
def _create_normal_graph(self):
|
|
gb = GraphBuilder()
|
|
|
|
gb.add_multilib(PT_SYSTEM, 'libdl',
|
|
exported_symbols={'dlclose', 'dlopen', 'dlsym'})
|
|
|
|
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'},
|
|
imported_symbols={'dlclose', 'dlopen', 'cos', 'sin'})
|
|
|
|
gb.add_multilib(PT_SYSTEM, 'libRS', dt_needed=['libdl.so'],
|
|
exported_symbols={'rsContextCreate'},
|
|
imported_symbols={'dlclose', 'dlopen', 'dlsym'})
|
|
|
|
gb.add_multilib(PT_SYSTEM, 'libcutils',
|
|
dt_needed=['libc.so', 'libdl.so'],
|
|
imported_symbols={'dlclose', 'dlopen', 'fclose',
|
|
'fopen'})
|
|
|
|
gb.add_multilib(PT_VENDOR, 'libEGL',
|
|
dt_needed=['libc.so', 'libcutils.so', 'libdl.so'],
|
|
exported_symbols={'eglGetDisplay'},
|
|
imported_symbols={'fclose', 'fopen'})
|
|
|
|
gb.resolve()
|
|
return gb
|
|
|
|
def _get_paths_from_nodes(self, nodes):
|
|
return sorted([node.path for node in nodes])
|
|
|
|
def test_map_path_to_lib(self):
|
|
gb = self._create_normal_graph()
|
|
graph = gb.graph
|
|
|
|
node = graph.map_path_to_lib('/system/lib/libc.so')
|
|
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.libdl_64, node)
|
|
self.assertEqual('/system/lib64/libdl.so', node.path)
|
|
|
|
node = graph.map_path_to_lib('/vendor/lib64/libEGL.so')
|
|
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'))
|
|
|
|
def test_map_paths_to_libs(self):
|
|
gb = self._create_normal_graph()
|
|
graph = gb.graph
|
|
|
|
bad = []
|
|
paths = ['/system/lib/libc.so', '/system/lib/libdl.so']
|
|
nodes = graph.map_paths_to_libs(paths, bad.append)
|
|
|
|
self.assertEqual([], bad)
|
|
self.assertEqual(2, len(nodes))
|
|
self.assertEqual(paths, self._get_paths_from_nodes(nodes))
|
|
|
|
bad = []
|
|
paths = ['/no/such/path.so', '/system/lib64/libdl.so']
|
|
nodes = graph.map_paths_to_libs(paths, bad.append)
|
|
self.assertEqual(['/no/such/path.so'], bad)
|
|
self.assertEqual(['/system/lib64/libdl.so'],
|
|
self._get_paths_from_nodes(nodes))
|
|
|
|
def test_elf_class(self):
|
|
gb = self._create_normal_graph()
|
|
graph = gb.graph
|
|
self.assertEqual(6, len(graph.lib32))
|
|
self.assertEqual(6, len(graph.lib64))
|
|
|
|
def test_partitions(self):
|
|
gb = self._create_normal_graph()
|
|
graph = gb.graph
|
|
self.assertEqual(10, len(gb.graph.lib_pt[PT_SYSTEM]))
|
|
self.assertEqual(2, len(gb.graph.lib_pt[PT_VENDOR]))
|
|
|
|
def test_deps(self):
|
|
gb = self._create_normal_graph()
|
|
graph = gb.graph
|
|
|
|
# Check the dependencies of libc.so.
|
|
node = gb.graph.map_path_to_lib('/system/lib/libc.so')
|
|
self.assertEqual(['/system/lib/libdl.so', '/system/lib/libm.so'],
|
|
self._get_paths_from_nodes(node.deps))
|
|
|
|
# Check the dependencies of libRS.so.
|
|
node = gb.graph.map_path_to_lib('/system/lib64/libRS.so')
|
|
self.assertEqual(['/system/lib64/libdl.so'],
|
|
self._get_paths_from_nodes(node.deps))
|
|
|
|
# Check the dependencies of libEGL.so.
|
|
node = gb.graph.map_path_to_lib('/vendor/lib64/libEGL.so')
|
|
self.assertEqual(['/system/lib64/libc.so', '/system/lib64/libcutils.so',
|
|
'/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
|
|
|
|
# Check the users of libc.so.
|
|
node = graph.map_path_to_lib('/system/lib/libc.so')
|
|
self.assertEqual(['/system/lib/libcutils.so', '/vendor/lib/libEGL.so'],
|
|
self._get_paths_from_nodes(node.users))
|
|
|
|
# Check the users of libdl.so.
|
|
node = graph.map_path_to_lib('/system/lib/libdl.so')
|
|
self.assertEqual(['/system/lib/libRS.so', '/system/lib/libc.so',
|
|
'/system/lib/libcutils.so', '/vendor/lib/libEGL.so'],
|
|
self._get_paths_from_nodes(node.users))
|
|
|
|
# Check the users of libRS.so.
|
|
node = graph.map_path_to_lib('/system/lib64/libRS.so')
|
|
self.assertEqual([], self._get_paths_from_nodes(node.users))
|
|
|
|
# Check the users of libEGL.so.
|
|
node = graph.map_path_to_lib('/vendor/lib64/libEGL.so')
|
|
self.assertEqual([], self._get_paths_from_nodes(node.users))
|
|
|
|
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)
|
|
|
|
def test_find_existing_vndk(self):
|
|
gb = GraphBuilder()
|
|
|
|
libpng32_core, libpng64_core = \
|
|
gb.add_multilib(PT_SYSTEM, 'libpng', extra_dir='vndk-26')
|
|
libpng32_fwk, libpng64_fwk = \
|
|
gb.add_multilib(PT_SYSTEM, 'libpng', extra_dir='vndk-26-ext')
|
|
|
|
libjpeg32_core, libjpeg64_core = \
|
|
gb.add_multilib(PT_SYSTEM, 'libjpeg', extra_dir='vndk-26')
|
|
libjpeg32_vnd, libjpeg64_vnd = \
|
|
gb.add_multilib(PT_VENDOR, 'libjpeg', extra_dir='vndk-26-ext')
|
|
|
|
gb.resolve()
|
|
|
|
vndk_core, vndk_fwk_ext, vndk_vnd_ext = gb.graph.find_existing_vndk()
|
|
|
|
expected_vndk_core = {
|
|
libpng32_core, libpng64_core, libjpeg32_core, libjpeg64_core}
|
|
expected_vndk_fwk_ext = {libpng32_fwk, libpng64_fwk}
|
|
expected_vndk_vnd_ext = {libjpeg32_vnd, libjpeg64_vnd}
|
|
|
|
self.assertSetEqual(expected_vndk_core, vndk_core)
|
|
self.assertSetEqual(expected_vndk_fwk_ext, vndk_fwk_ext)
|
|
self.assertSetEqual(expected_vndk_vnd_ext, vndk_vnd_ext)
|
|
|
|
def test_find_existing_vndk_without_version(self):
|
|
gb = GraphBuilder()
|
|
|
|
libpng32_core, libpng64_core = \
|
|
gb.add_multilib(PT_SYSTEM, 'libpng', extra_dir='vndk')
|
|
libpng32_fwk, libpng64_fwk = \
|
|
gb.add_multilib(PT_SYSTEM, 'libpng', extra_dir='vndk-ext')
|
|
|
|
libjpeg32_core, libjpeg64_core = \
|
|
gb.add_multilib(PT_SYSTEM, 'libjpeg', extra_dir='vndk')
|
|
libjpeg32_vnd, libjpeg64_vnd = \
|
|
gb.add_multilib(PT_VENDOR, 'libjpeg', extra_dir='vndk-ext')
|
|
|
|
gb.resolve()
|
|
|
|
vndk_core, vndk_fwk_ext, vndk_vnd_ext = gb.graph.find_existing_vndk()
|
|
|
|
expected_vndk_core = {
|
|
libpng32_core, libpng64_core, libjpeg32_core, libjpeg64_core}
|
|
expected_vndk_fwk_ext = {libpng32_fwk, libpng64_fwk}
|
|
expected_vndk_vnd_ext = {libjpeg32_vnd, libjpeg64_vnd}
|
|
|
|
self.assertSetEqual(expected_vndk_core, vndk_core)
|
|
self.assertSetEqual(expected_vndk_fwk_ext, vndk_fwk_ext)
|
|
self.assertSetEqual(expected_vndk_vnd_ext, vndk_vnd_ext)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|