Example #1
0
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
Example #2
0
    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))
Example #4
0
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)