1#!/usr/bin/env python 2# 3# Copyright (C) 2017 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import logging 19import os 20import re 21import shutil 22import tempfile 23 24from vts.runners.host import asserts 25from vts.runners.host import base_test 26from vts.runners.host import test_runner 27from vts.runners.host import utils 28from vts.utils.python.controllers import android_device 29from vts.utils.python.file import file_utils 30from vts.utils.python.os import path_utils 31from vts.testcases.vndk.dependency import elf_parser 32 33 34class VtsVndkDependencyTest(base_test.BaseTestClass): 35 """A test case to verify vendor library dependency. 36 37 Attributes: 38 _dut: The AndroidDevice under test. 39 _shell: The ShellMirrorObject to execute commands 40 _temp_dir: The temporary directory to which the vendor partition is 41 copied. 42 _LOW_LEVEL_NDK: List of strings. The names of low-level NDK libraries in 43 /system/lib[64]. 44 _SAME_PROCESS_HAL: List of patterns. The names of same-process HAL 45 libraries expected to be in /vendor/lib[64]. 46 _SAME_PROCESS_NDK: List if strings. The names of same-process NDK 47 libraries in /system/lib[64]. 48 """ 49 _TARGET_VENDOR_DIR = "/vendor" 50 _TARGET_VNDK_SP_DIR_32 = "/system/lib/vndk-sp" 51 _TARGET_VNDK_SP_DIR_64 = "/system/lib64/vndk-sp" 52 53 # copied from development/vndk/tools/definition-tool/vndk_definition_tool.py 54 _LOW_LEVEL_NDK = [ 55 "libandroid_net.so", 56 "libc.so", 57 "libdl.so", 58 "liblog.so", 59 "libm.so", 60 "libstdc++.so", 61 "libvndksupport.so", 62 "libz.so" 63 ] 64 _SAME_PROCESS_HAL = [re.compile(p) for p in [ 65 "android\\.hardware\\.graphics\\.mapper@\\d+\\.\\d+-impl\\.so$", 66 "gralloc\\..*\\.so$", 67 "libEGL_.*\\.so$", 68 "libGLES_.*\\.so$", 69 "libGLESv1_CM_.*\\.so$", 70 "libGLESv2_.*\\.so$", 71 "libGLESv3_.*\\.so$", 72 "libPVRRS\\.so$", 73 "libRSDriver.*\\.so$", 74 "vulkan.*\\.so$" 75 ]] 76 _SAME_PROCESS_NDK = [ 77 "libEGL.so", 78 "libGLESv1_CM.so", 79 "libGLESv2.so", 80 "libGLESv3.so", 81 "libnativewindow.so", 82 "libsync.so", 83 "libvulkan.so" 84 ] 85 _SP_HAL_LINK_PATHS_32 = [ 86 "/vendor/lib/egl", 87 "/vendor/lib/hw", 88 "/vendor/lib" 89 ] 90 _SP_HAL_LINK_PATHS_64 = [ 91 "/vendor/lib64/egl", 92 "/vendor/lib64/hw", 93 "/vendor/lib64" 94 ] 95 96 class ElfObject(object): 97 """Contains dependencies of an ELF file on target device. 98 99 Attributes: 100 target_path: String. The path to the ELF file on target. 101 name: String. File name of the ELF. 102 target_dir: String. The directory containing the ELF file on target. 103 bitness: Integer. Bitness of the ELF. 104 deps: List of strings. The names of the depended libraries. 105 """ 106 def __init__(self, target_path, bitness, deps): 107 self.target_path = target_path 108 self.name = path_utils.TargetBaseName(target_path) 109 self.target_dir = path_utils.TargetDirName(target_path) 110 self.bitness = bitness 111 self.deps = deps 112 113 def setUpClass(self): 114 """Initializes device and temporary directory.""" 115 self._dut = self.registerController(android_device)[0] 116 self._dut.shell.InvokeTerminal("one") 117 self._shell = self._dut.shell.one 118 self._temp_dir = tempfile.mkdtemp() 119 logging.info("adb pull %s %s", self._TARGET_VENDOR_DIR, self._temp_dir) 120 pull_output = self._dut.adb.pull( 121 self._TARGET_VENDOR_DIR, self._temp_dir) 122 logging.debug(pull_output) 123 124 def tearDownClass(self): 125 """Deletes the temporary directory.""" 126 logging.info("Delete %s", self._temp_dir) 127 shutil.rmtree(self._temp_dir) 128 129 def _loadElfObjects(self, host_dir, target_dir, elf_error_handler): 130 """Scans a host directory recursively and loads all ELF files in it. 131 132 Args: 133 host_dir: The host directory to scan. 134 target_dir: The path from which host_dir is copied. 135 elf_error_handler: A function that takes 2 arguments 136 (target_path, exception). It is called when 137 the parser fails to read an ELF file. 138 139 Returns: 140 List of ElfObject. 141 """ 142 objs = [] 143 for root_dir, file_name in utils.iterate_files(host_dir): 144 full_path = os.path.join(root_dir, file_name) 145 rel_path = os.path.relpath(full_path, host_dir) 146 target_path = path_utils.JoinTargetPath( 147 target_dir, *rel_path.split(os.path.sep)); 148 try: 149 elf = elf_parser.ElfParser(full_path) 150 except elf_parser.ElfError: 151 logging.debug("%s is not an ELF file", target_path) 152 continue 153 try: 154 deps = elf.listDependencies() 155 except elf_parser.ElfError as e: 156 elf_error_handler(target_path, e) 157 continue 158 finally: 159 elf.close() 160 161 logging.info("%s depends on: %s", target_path, ", ".join(deps)) 162 objs.append(self.ElfObject(target_path, elf.bitness, deps)) 163 return objs 164 165 def _isAllowedSpHalDependency(self, lib_name, vndk_sp_names, linkable_libs): 166 """Checks whether a same-process HAL library dependency is allowed. 167 168 A same-process HAL library is allowed to depend on 169 - Low-level NDK 170 - Same-process NDK 171 - vndk-sp 172 - Other libraries in vendor/lib[64] 173 174 Args: 175 lib_name: String. The name of the depended library. 176 vndk_sp_names: Set of strings. The names of the libraries in 177 vndk-sp directory. 178 linkable_libs: Dictionary. The keys are the names of the libraries 179 which can be linked to same-process HAL. 180 181 Returns: 182 A boolean representing whether the dependency is allowed. 183 """ 184 if (lib_name in self._LOW_LEVEL_NDK or 185 lib_name in self._SAME_PROCESS_NDK or 186 lib_name in vndk_sp_names or 187 lib_name in linkable_libs): 188 return True 189 return False 190 191 def _getTargetVndkSpDir(self, bitness): 192 """Returns 32/64-bit vndk-sp directory path on target device.""" 193 return getattr(self, "_TARGET_VNDK_SP_DIR_" + str(bitness)) 194 195 def _getSpHalLinkPaths(self, bitness): 196 """Returns 32/64-bit same-process HAL link paths""" 197 return getattr(self, "_SP_HAL_LINK_PATHS_" + str(bitness)) 198 199 def _isInSpHalLinkPaths(self, lib): 200 """Checks whether a library can be linked to same-process HAL. 201 202 Args: 203 lib: ElfObject. The library to check. 204 205 Returns: 206 True if can be linked to same-process HAL; False otherwise. 207 """ 208 return lib.target_dir in self._getSpHalLinkPaths(lib.bitness) 209 210 def _spHalLinkOrder(self, lib): 211 """Returns the key for sorting libraries in linker search order. 212 213 Args: 214 lib: ElfObject. 215 216 Returns: 217 An integer representing linker search order. 218 """ 219 link_paths = self._getSpHalLinkPaths(lib.bitness) 220 for order in range(len(link_paths)): 221 if lib.target_dir == link_paths[order]: 222 return order 223 order = len(link_paths) 224 if lib.name in self._LOW_LEVEL_NDK: 225 return order 226 order += 1 227 if lib.name in self._SAME_PROCESS_NDK: 228 return order 229 order += 1 230 return order 231 232 def _dfsDependencies(self, lib, searched, searchable): 233 """Depth-first-search for library dependencies. 234 235 Args: 236 lib: ElfObject. The library to search dependencies. 237 searched: The set of searched libraries. 238 searchable: The dictionary that maps file names to libraries. 239 """ 240 if lib in searched: 241 return 242 searched.add(lib) 243 for dep_name in lib.deps: 244 if dep_name in searchable: 245 self._dfsDependencies( 246 searchable[dep_name], searched, searchable) 247 248 def _testSpHalDependency(self, bitness, objs): 249 """Scans same-process HAL dependency on vendor partition. 250 251 Returns: 252 List of tuples (path, dependency_names). The library with 253 disallowed dependencies and list of the dependencies. 254 """ 255 vndk_sp_dir = self._getTargetVndkSpDir(bitness) 256 vndk_sp_paths = file_utils.FindFiles(self._shell, vndk_sp_dir, "*.so") 257 vndk_sp_names = set(path_utils.TargetBaseName(x) for x in vndk_sp_paths) 258 logging.info("%s libraries: %s" % ( 259 vndk_sp_dir, ", ".join(vndk_sp_names))) 260 # map file names to libraries which can be linked to same-process HAL 261 linkable_libs = dict() 262 for obj in [x for x in objs 263 if x.bitness == bitness and self._isInSpHalLinkPaths(x)]: 264 if obj.name not in linkable_libs: 265 linkable_libs[obj.name] = obj 266 else: 267 linkable_libs[obj.name] = min(linkable_libs[obj.name], obj, 268 key=self._spHalLinkOrder) 269 # find same-process HAL and dependencies 270 sp_hal_libs = set() 271 for file_name, obj in linkable_libs.iteritems(): 272 if any([x.match(file_name) for x in self._SAME_PROCESS_HAL]): 273 self._dfsDependencies(obj, sp_hal_libs, linkable_libs) 274 logging.info("%d-bit SP HAL libraries: %s" % ( 275 bitness, ", ".join([x.name for x in sp_hal_libs]))) 276 # check disallowed dependencies 277 dep_errors = [] 278 for obj in sp_hal_libs: 279 disallowed_libs = [x for x in obj.deps 280 if not self._isAllowedSpHalDependency(x, vndk_sp_names, 281 linkable_libs)] 282 if disallowed_libs: 283 dep_errors.append((obj.target_path, disallowed_libs)) 284 return dep_errors 285 286 def testElfDependency(self): 287 """Scans library/executable dependency on vendor partition.""" 288 read_errors = [] 289 objs = self._loadElfObjects( 290 self._temp_dir, 291 path_utils.TargetDirName(self._TARGET_VENDOR_DIR), 292 lambda p, e: read_errors.append((p, str(e)))) 293 294 dep_errors = self._testSpHalDependency(32, objs) 295 if self._dut.is64Bit: 296 dep_errors.extend(self._testSpHalDependency(64, objs)) 297 # TODO(hsinyichen): check other vendor libraries 298 299 if read_errors: 300 logging.error("%d read errors:", len(read_errors)) 301 for x in read_errors: 302 logging.error("%s: %s", x[0], x[1]) 303 if dep_errors: 304 logging.error("%d disallowed dependencies:", len(dep_errors)) 305 for x in dep_errors: 306 logging.error("%s: %s", x[0], ", ".join(x[1])) 307 error_count = len(read_errors) + len(dep_errors) 308 asserts.assertEqual(error_count, 0, 309 "Total number of errors: " + str(error_count)) 310 311 312if __name__ == "__main__": 313 test_runner.main() 314