Exemplo n.º 1
0
 def find_debug_file(filename):
     # In the Zircon makefile build, the file to be installed is called
     # foo.strip and the unstripped file is called foo.  In the new Zircon
     # GN build, the file to be installed is called foo and the unstripped
     # file is called foo.debug.  In the Fuchsia GN build, the file to be
     # installed is called foo and the unstripped file has the same name in
     # the exe.unstripped or lib.unstripped subdirectory.
     if filename.endswith('.strip'):
         debugfile = filename[:-6]
     elif os.path.exists(filename + '.debug'):
         debugfile = filename + '.debug'
     elif (lib_dir := next(
         (dir for dir in toolchain_lib_dirs if os.path.realpath(
             filename).startswith(os.path.realpath(dir) + os.sep)), None)):
         build_id_dir = os.path.join(lib_dir, 'debug', '.build-id')
         if not os.path.exists(build_id_dir):
             return None
         info = elfinfo.get_elf_info(filename)
         debugfile = os.path.join(build_id_dir, info.build_id[:2],
                                  info.build_id[2:] + '.debug')
         if not os.path.exists(debugfile):
             return None
         # Pass filename as fallback so we don't fallback to the build-id entry name.
         return binary_info(debugfile,
                            fallback_soname=os.path.basename(filename))
Exemplo n.º 2
0
 def __init__(self, path, size=None, libs=None):
     self.path = path
     self.size = size if size else os.path.getsize(path)
     if libs != None:
         self.libs = set(libs)
     else:
         info = elfinfo.get_elf_info(path)
         self.libs = info.needed if info else set()
Exemplo n.º 3
0
 def find_debug_file(filename):
     # In the Zircon makefile build, the file to be installed is called
     # foo.strip and the unstripped file is called foo.  In the new Zircon
     # GN build, the file to be installed is called foo and the unstripped
     # file is called foo.debug.  In the Fuchsia GN build, the file to be
     # installed is called foo and the unstripped file has the same name in
     # the exe.unstripped or lib.unstripped subdirectory.
     if filename.endswith('.strip'):
         debugfile = filename[:-6]
     elif os.path.exists(filename + '.debug'):
         debugfile = filename + '.debug'
     else:
         # Check for toolchain runtime libraries, which are stored under
         # {toolchain}/lib/.../libfoo.so, and whose unstripped file will
         # be under {toolchain}/lib/debug/.build-id/xx/xxxxxx.debug.
         lib_dir = None
         for dir in toolchain_lib_dirs:
             if os.path.realpath(filename).startswith(
                     os.path.realpath(dir) + os.sep):
                 lib_dir = dir
                 break
         if lib_dir:
             build_id_dir = os.path.join(lib_dir, 'debug', '.build-id')
             if not os.path.exists(build_id_dir):
                 return None
             info = elfinfo.get_elf_info(filename)
             debugfile = os.path.join(build_id_dir, info.build_id[:2],
                                      info.build_id[2:] + '.debug')
             if not os.path.exists(debugfile):
                 return None
             # Pass filename as fallback so we don't fallback to the build-id entry name.
             return binary_info(debugfile,
                                fallback_soname=os.path.basename(filename))
         else:
             dir, file = os.path.split(filename)
             if file.endswith('.so') or '.so.' in file:
                 subdir = 'lib.unstripped'
             else:
                 subdir = 'exe.unstripped'
             debugfile = os.path.join(dir, subdir, file)
             while not os.path.exists(debugfile):
                 # For dir/foo/bar, if dir/foo/exe.unstripped/bar
                 # didn't exist, try dir/exe.unstripped/foo/bar.
                 parent, dir = os.path.split(dir)
                 if not parent or not dir:
                     return None
                 dir, file = parent, os.path.join(dir, file)
                 debugfile = os.path.join(dir, subdir, file)
             if not os.path.exists(debugfile):
                 debugfile = os.path.join(subdir, filename)
                 if not os.path.exists(debugfile):
                     return None
     debug = binary_info(debugfile)
     assert debug, ("Debug file '%s' for '%s' is invalid" %
                    (debugfile, filename))
     examined.add(debugfile)
     return debug
Exemplo n.º 4
0
def _StripBinary(dry_run, bin_path):
    """Creates a stripped copy of the executable at |bin_path| and returns the
  path to the stripped copy."""
    strip_path = bin_path + '.bootfs_stripped'
    if dry_run:
        print "Strip", bin_path, " to ", strip_path
    else:
        info = elfinfo.get_elf_info(bin_path)
        info.strip(strip_path)
    return strip_path
Exemplo n.º 5
0
def _WriteBuildIdsTxt(binary_paths, ids_txt_path):
    """Writes an index text file that maps build IDs to the paths of unstripped
  binaries."""

    with open(ids_txt_path, 'w') as ids_file:
        for binary_path in binary_paths:
            # Paths to the unstripped executables listed in "ids.txt" are specified
            # as relative paths to that file.
            relative_path = os.path.relpath(
                os.path.abspath(binary_path),
                os.path.dirname(os.path.abspath(ids_txt_path)))

            info = elfinfo.get_elf_info(_GetStrippedPath(binary_path))
            ids_file.write(info.build_id + ' ' + relative_path + '\n')
Exemplo n.º 6
0
 def find_debug_file(filename):
     # In the Zircon makefile build, the file to be installed is called
     # foo.strip and the unstripped file is called foo.  In the new Zircon
     # GN build, the file to be installed is called foo and the unstripped
     # file is called foo.debug.  In the Fuchsia GN build, the file to be
     # installed is called foo and the unstripped file has the same name in
     # the exe.unstripped or lib.unstripped subdirectory.
     if filename.endswith('.strip'):
         debugfile = filename[:-6]
     elif os.path.exists(filename + '.debug'):
         debugfile = filename + '.debug'
     elif os.path.realpath(filename).startswith(
             os.path.realpath(clang_lib_dir)):
         build_id_dir = os.path.join(clang_lib_dir, 'debug', '.build-id')
         if not os.path.exists(build_id_dir):
             return None
         info = elfinfo.get_elf_info(filename)
         debugfile = os.path.join(build_id_dir, info.build_id[:2],
                                  info.build_id[2:] + '.debug')
         if not os.path.exists(debugfile):
             return None
         return binary_info(debugfile)
     else:
         dir, file = os.path.split(filename)
         if file.endswith('.so') or '.so.' in file:
             subdir = 'lib.unstripped'
         else:
             subdir = 'exe.unstripped'
         debugfile = os.path.join(dir, subdir, file)
         while not os.path.exists(debugfile):
             # For dir/foo/bar, if dir/foo/exe.unstripped/bar
             # didn't exist, try dir/exe.unstripped/foo/bar.
             parent, dir = os.path.split(dir)
             if not parent or not dir:
                 return None
             dir, file = parent, os.path.join(dir, file)
             debugfile = os.path.join(dir, subdir, file)
         if not os.path.exists(debugfile):
             debugfile = os.path.join(subdir, filename)
             if not os.path.exists(debugfile):
                 return None
     debug = binary_info(debugfile)
     assert debug, ("Debug file '%s' for '%s' is invalid" %
                    (debugfile, filename))
     examined.add(debugfile)
     return debug
Exemplo n.º 7
0
def binary_info(filename, fallback_soname=None):
    info = elfinfo.get_elf_info(filename, [ZIRCON_DRIVER_IDENT])
    if info and not info.soname:
        return info.with_soname(fallback_soname or os.path.basename(filename))
    return info
Exemplo n.º 8
0
def get_sdk_debug_path(binary):
    build_id = elfinfo.get_elf_info(binary).build_id
    return '.build-id/' + build_id[:2] + '/' + build_id[2:] + '.debug'
Exemplo n.º 9
0
def binary_info(filename):
    return elfinfo.get_elf_info(filename, [ZIRCON_DRIVER_IDENT])
def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        '--source-dir',
        default='.',
        help='Root directory for source paths. Default is current directory.')
    parser.add_argument('--check-stripped',
                        action='store_true',
                        help='Verify that ELF binaries are stripped.')
    parser.add_argument(
        '--check-unstripped-files',
        action='store_true',
        help='Verify that each ELF binary has its own unstripped file available.' + \
             'this requires --toolchain-lib-dir to find toolchain-provided runtime debug files')
    parser.add_argument(
        '--toolchain-lib-dir',
        default=[],
        action='append',
        metavar='DIR',
        help=
        'Path to toolchain-provided lib directory. Can be used multiple times.'
    )

    group = parser.add_mutually_exclusive_group()
    group.add_argument('--fini-manifest', help='Input FINI manifest.')
    group.add_argument('--partial-manifest',
                       help='Input partial distribution manifest.')

    parser.add_argument('--stamp', required=True, help='Output stamp file.')
    parser.add_argument('--depfile', help='Output Ninja depfile.')

    args = parser.parse_args()

    depfile_items = set()

    # Read the input manifest into a {target: source} dictionary.
    manifest_entries = {}
    elf_runtime_dir_map = None

    if args.fini_manifest:
        input_manifest = args.fini_manifest
        with open(args.fini_manifest) as f:
            for line in f:
                line = line.rstrip()
                target, _, source = line.partition('=')
                assert source is not None, ('Invalid manifest line: [%s]' %
                                            line)

                source_file = os.path.join(args.source_dir, source)

                # Duplicate entries happen in some manifests, but they will have the
                # same content, so assert otherwise.
                if target in manifest_entries:
                    assert manifest_entries[target] == source_file, (
                        'Duplicate entries for target "%s": %s vs %s' %
                        (target, source_file, manifest_entries[target]))

                assert os.path.exists(source_file), (
                    'Missing source file for manifest line: %s' % line)
                manifest_entries[target] = source_file
    else:
        input = args.input_manifest
        with open(args.partial_manifest) as f:
            partial_entries = json.load(f)

        result = distribution_manifest.expand_partial_manifest_items(
            partial_entries, depfile_items)
        if result.errors:
            print('ERRORS FOUND IN %s:\n%s' %
                  (args.partial_manifest, '\n'.join(result.errors)),
                  file=sys.stderr)

        manifest_entries = {e.destination: e.source for e in result.entries}
        elf_runtime_dir_map = result.elf_runtime_map

    # Filter the input manifest entries to keep only the ELF ones
    # that are not under data/, lib/firmware/ or meta/
    elf_entries = {}
    for target, source_file in manifest_entries.items():
        if target.startswith('data/') or target.startswith(
                'lib/firmware/') or target.startswith('meta/'):
            continue

        depfile_items.add(source_file)
        info = elfinfo.get_elf_info(source_file)
        if info is not None:
            elf_entries[target] = info

    # errors contains a list of error strings corresponding to issues found in
    # the input manifest.
    #
    # extras contains non-error strings that are useful to dump when an error
    # occurs, and give more information about what was found in the manifest.
    # These should only be printed in case of errors, or ignored otherwise.
    errors = []
    extras = []

    # The set of all libraries visited when checking dependencies.
    visited_libraries = set()

    # Verify that libzircon.so or libc.so do not appear inside the package.
    for target, info in elf_entries.items():
        filename = os.path.basename(target)
        if filename in ('libzircon.so', 'libc.so'):
            errors.append('%s should not be in this package (source=%s)!' %
                          (target, info.filename))

    # First verify all executables, since we can find their library directory
    # from their PT_INTERP value, then check their dependencies recursively.
    for target, info in elf_entries.items():

        if elf_runtime_dir_map is not None:
            lib_dir = elf_runtime_dir_map.get(target)
            if not lib_dir:
                continue
        else:
            interp = info.interp
            if interp is None:
                continue

            interp_name = os.path.basename(interp)
            if interp_name != 'ld.so.1':
                errors.append(
                    '%s has invalid or unsupported PT_INTERP value: %s' %
                    (target, interp))
                continue

            lib_dir = os.path.join('lib', os.path.dirname(interp))
            extras.append('Binary %s has interp %s, lib_dir %s' %
                          (target, interp, lib_dir))

        binary_errors, binary_deps = verify_elf_dependencies(
            target, lib_dir, info.needed, elf_entries)

        errors += binary_errors
        visited_libraries.update(binary_deps)

    # Check that all binaries are stripped if necessary.
    if args.check_stripped:
        for target, info in elf_entries.items():
            if not info.stripped:
                errors.append('%s is not stripped: %s' %
                              (target, info.filename))

    # Verify that all ELF files have their unstripped file available.
    if args.check_unstripped_files:
        for target, source_file in manifest_entries.items():
            # Prebuilts are allowed to have missing debug files.
            # See: fxbug.dev/89174
            if "/prebuilt/" in source_file:
                continue
            if target in elf_entries:
                unstripped = find_unstripped_file(source_file, depfile_items,
                                                  args.toolchain_lib_dir)
                if unstripped is None:
                    errors.append('No unstripped file found for ' +
                                  source_file)

    if errors:
        print('ERRORS FOUND IN %s:\n%s' % (input_manifest, '\n'.join(errors)),
              file=sys.stderr)
        if extras:
            print('\n'.join(extras), file=sys.stderr)
        return 1

    if args.depfile:
        with open(args.depfile, 'w') as f:
            f.write('%s: %s\n' %
                    (input_manifest, ' '.join(sorted(depfile_items))))

    # Write the stamp file on success.
    with open(args.stamp, 'w') as f:
        f.write('')

    return 0
def find_unstripped_file(filename: str,
                         depfile_items: Set[str],
                         toolchain_lib_dirs: List[str] = []) -> Optional[str]:
    """Find the unstripped version of a given ELF binary.

        Args:
          filename: input file path in build directory.
          toolchain_lib_dirs: a list of toolchain-specific lib directories
            which will be used to look for debug/.build-id/xx/xxxxxxx.debug
            files corresponding to the input files's build-id value.
          depfile_items: the set of examined files to be updated if needed.

        Returns
          Path to the debug file, if it exists, or None.
        """
    if os.path.exists(filename + '.debug'):
        # Zircon-specific toolchains currently write the unstripped files
        # for .../foo as .../foo.debug.
        debugfile = filename + '.debug'
    else:
        # Check for toolchain runtime libraries, which are stored under
        # {toolchain}/lib/.../libfoo.so, and whose unstripped file will
        # be under {toolchain}/lib/debug/.build-id/xx/xxxxxx.debug.
        lib_dir = None
        for dir in toolchain_lib_dirs:
            if os.path.realpath(filename).startswith(
                    os.path.realpath(dir) + os.sep):
                lib_dir = dir
                break
        if lib_dir:
            build_id_dir = os.path.join(lib_dir, 'debug', '.build-id')
            if not os.path.exists(build_id_dir):
                # Local rust builds don't contain debug in the path, so fallback
                # to a path without debug.
                build_id_dir = os.path.join(lib_dir, '.build-id')
            if not os.path.exists(build_id_dir):
                return None
            build_id = elfinfo.get_elf_info(filename).build_id
            # The build-id value is an hexadecimal string, used to locate the
            # debug file under debug/.build-id/XX/YYYYYY.debug where XX are its
            # first two chars, and YYYYYY is the rest (typically longer than
            # 6 chars).
            debugfile = os.path.join(build_id_dir, build_id[:2],
                                     build_id[2:] + '.debug')
            if not os.path.exists(debugfile):
                return None
            # Pass filename as fallback so we don't fallback to the build-id entry name.
            return debugfile
        else:
            # Otherwise, the Fuchsia build places unstripped files under
            # .../lib.unstripped/foo.so (for shared library or loadable module
            # .../foo.so) or .../exe.unstripped/bar (for executable .../bar).
            dir, file = os.path.split(filename)
            if file.endswith('.so') or '.so.' in file:
                subdir = 'lib.unstripped'
            else:
                subdir = 'exe.unstripped'
            debugfile = os.path.join(dir, subdir, file)
            while not os.path.exists(debugfile):
                # For dir/foo/bar, if dir/foo/exe.unstripped/bar
                # didn't exist, try dir/exe.unstripped/foo/bar.
                parent, dir = os.path.split(dir)
                if not parent or not dir:
                    return None
                dir, file = parent, os.path.join(dir, file)
                debugfile = os.path.join(dir, subdir, file)
            if not os.path.exists(debugfile):
                debugfile = os.path.join(subdir, filename)
                if not os.path.exists(debugfile):
                    return None
    depfile_items.add(debugfile)
    return debugfile