def GetSharedLibraries(binary_path): """Gets the shared libraries used by a binary. Gets the shared libraries used by the binary. Based on GetSharedLibraries from src/tools/code_coverage/coverage_utils.py in Chromium. Args: binary_path: The path to the binary we want to find the shared libraries of. Returns: The shared libraries used by |binary_path|. """ logging.info('Finding shared libraries for targets (if any).') shared_libraries = [] elf_dict = lddtree.ParseELF(binary_path.chroot, root=SysrootPath.path_to_sysroot) for shared_library in elf_dict['libs'].values(): shared_library_path = shared_library['path'] if shared_library_path in shared_libraries: continue assert os.path.exists(shared_library_path), ( 'Shared library "%s" used by ' 'the given target(s) does not ' 'exist.' % shared_library_path) if IsInstrumentedWithClangCoverage(shared_library_path): # Do not add non-instrumented libraries. Otherwise, llvm-cov errors out. shared_libraries.append(shared_library_path) logging.debug('Found shared libraries (%d): %s.', len(shared_libraries), shared_libraries) logging.info('Finished finding shared libraries for targets.') return shared_libraries
def TestLinkage(self): """Find main executable binaries and check their linkage.""" binaries = [ 'bin/sed', ] if self._IsPackageMerged('chromeos-base/chromeos-login'): binaries.append('sbin/session_manager') if self._IsPackageMerged('x11-base/xorg-server'): binaries.append('usr/bin/Xorg') # When chrome is built with USE="pgo_generate", rootfs chrome is actually a # symlink to a real binary which is in the stateful partition. So we do not # check for a valid chrome binary in that case. if not self._IsPackageMerged( 'chromeos-base/chromeos-chrome[pgo_generate]'): if self._IsPackageMerged( 'chromeos-base/chromeos-chrome[app_shell]'): binaries.append('opt/google/chrome/app_shell') elif self._IsPackageMerged('chromeos-base/chromeos-chrome'): binaries.append('opt/google/chrome/chrome') binaries = [os.path.join(image_test_lib.ROOT_A, x) for x in binaries] # Grab all .so files libraries = [] for root, _, files in os.walk(image_test_lib.ROOT_A): for name in files: filename = os.path.join(root, name) if '.so' in filename: libraries.append(filename) ldpaths = lddtree.LoadLdpaths(image_test_lib.ROOT_A) for to_test in itertools.chain(binaries, libraries): # to_test could be a symlink, we need to resolve it relative to ROOT_A. while os.path.islink(to_test): link = os.readlink(to_test) if link.startswith('/'): to_test = os.path.join(image_test_lib.ROOT_A, link[1:]) else: to_test = os.path.join(os.path.dirname(to_test), link) try: lddtree.ParseELF(to_test, root=image_test_lib.ROOT_A, ldpaths=ldpaths) except lddtree.exceptions.ELFError: continue except IOError as e: self.fail('Fail linkage test for %s: %s' % (to_test, e))
def Rewrite(self, used_charsets, dry_run=False): """Rewrite gconv-modules file with only the used charsets. Args: used_charsets: A list of used charsets. This should be a subset of the list returned by Load(). dry_run: Whether this function should not change any file. """ # Compute the used modules. used_modules = set() for charset in used_charsets: while charset in self._alias: charset = self._alias[charset] used_modules.update(self._modules[charset]) unused_modules = ( functools.reduce(set.union, list(self._modules.values())) - used_modules) modules_dir = os.path.dirname(self._filename) all_modules = set.union(used_modules, unused_modules) # The list of charsets that depend on a given library. For example, # libdeps['libCNS.so'] is the set of all the modules that require that # library. These libraries live in the same directory as the modules. libdeps = {} for module in all_modules: deps = lddtree.ParseELF( os.path.join(modules_dir, '%s.so' % module), modules_dir, []) if 'needed' not in deps: continue for lib in deps['needed']: # Ignore the libs without a path defined (outside the modules_dir). if deps['libs'][lib]['path']: libdeps[lib] = libdeps.get(lib, set()).union([module]) used_libdeps = set(lib for lib, deps in libdeps.items() if deps.intersection(used_modules)) unused_libdeps = set(libdeps).difference(used_libdeps) logging.debug('Used modules: %s', ', '.join(sorted(used_modules))) logging.debug('Used dependency libs: %s, '.join(sorted(used_libdeps))) unused_size = 0 for module in sorted(unused_modules): module_path = os.path.join(modules_dir, '%s.so' % module) unused_size += os.lstat(module_path).st_size logging.debug('rm %s', module_path) if not dry_run: os.unlink(module_path) unused_libdeps_size = 0 for lib in sorted(unused_libdeps): lib_path = os.path.join(modules_dir, lib) unused_libdeps_size += os.lstat(lib_path).st_size logging.debug('rm %s', lib_path) if not dry_run: os.unlink(lib_path) logging.info( 'Done. Using %d gconv modules. Removed %d unused modules' ' (%.1f KiB) and %d unused dependencies (%.1f KiB)', len(used_modules), len(unused_modules), unused_size / 1024., len(unused_libdeps), unused_libdeps_size / 1024.) # Recompute the gconv-modules file with only the included gconv modules. result = [] with open(self._filename) as fp: for line in fp: lst = line.split('#', 1)[0].strip().split() if not lst: # Keep comments and copyright headers. result.append(line) elif lst[0] == 'module': _, _, _, filename = lst[:4] if filename in used_modules: # Used module result.append(line) elif lst[0] == 'alias': _, charset, _ = lst charset = charset.rstrip('/') while charset in self._alias: charset = self._alias[charset] if used_modules.intersection(self._modules[charset]): # Alias to an used module result.append(line) else: cros_build_lib.Die('Unknown line: %s', line) if not dry_run: osutils.WriteFile(self._filename, ''.join(result))
def _BuildInitialPackageRoot(output_dir, paths, elfs, ldpaths, path_rewrite_func=lambda x: x, root='/'): """Link in all packable files and their runtime dependencies This also wraps up executable ELFs with helper scripts. Args: output_dir: The output directory to store files paths: All the files to include elfs: All the files which are ELFs (a subset of |paths|) ldpaths: A dict of static ldpath information path_rewrite_func: User callback to rewrite paths in output_dir root: The root path to pull all packages/files from """ # Link in all the files. sym_paths = [] for path in paths: new_path = path_rewrite_func(path) dst = output_dir + new_path osutils.SafeMakedirs(os.path.dirname(dst)) # Is this a symlink which we have to rewrite or wrap? # Delay wrap check until after we have created all paths. src = root + path if os.path.islink(src): tgt = os.readlink(src) if os.path.sep in tgt: sym_paths.append((new_path, lddtree.normpath(ReadlinkRoot(src, root)))) # Rewrite absolute links to relative and then generate the symlink # ourselves. All other symlinks can be hardlinked below. if tgt[0] == '/': tgt = os.path.relpath(tgt, os.path.dirname(new_path)) os.symlink(tgt, dst) continue os.link(src, dst) # Now see if any of the symlinks need to be wrapped. for sym, tgt in sym_paths: if tgt in elfs: GeneratePathWrapper(output_dir, sym, tgt) # Locate all the dependencies for all the ELFs. Stick them all in the # top level "lib" dir to make the wrapper simpler. This exact path does # not matter since we execute ldso directly, and we tell the ldso the # exact path to search for its libraries. libdir = os.path.join(output_dir, 'lib') osutils.SafeMakedirs(libdir) donelibs = set() for elf in elfs: e = lddtree.ParseELF(elf, root=root, ldpaths=ldpaths) interp = e['interp'] if interp: # Generate a wrapper if it is executable. interp = os.path.join('/lib', os.path.basename(interp)) lddtree.GenerateLdsoWrapper(output_dir, path_rewrite_func(elf), interp, libpaths=e['rpath'] + e['runpath']) FixClangXXWrapper(output_dir, path_rewrite_func(elf)) for lib, lib_data in e['libs'].iteritems(): if lib in donelibs: continue src = path = lib_data['path'] if path is None: logging.warning('%s: could not locate %s', elf, lib) continue donelibs.add(lib) # Needed libs are the SONAME, but that is usually a symlink, not a # real file. So link in the target rather than the symlink itself. # We have to walk all the possible symlinks (SONAME could point to a # symlink which points to a symlink), and we have to handle absolute # ourselves (since we have a "root" argument). dst = os.path.join(libdir, os.path.basename(path)) src = ReadlinkRoot(src, root) os.link(root + src, dst)