Beispiel #1
0
    def _ElfPathAndToolPrefixForSymbol(self, size_info, elf_path):
        tool_prefix = self._tool_prefix_finder.Tentative()
        orig_tool_prefix = size_info.metadata.get(models.METADATA_TOOL_PREFIX)
        if orig_tool_prefix:
            orig_tool_prefix = path_util.FromSrcRootRelative(orig_tool_prefix)
            if os.path.exists(path_util.GetObjDumpPath(orig_tool_prefix)):
                tool_prefix = orig_tool_prefix

        # TODO(agrieve): Would be even better to use objdump --info to check that
        #     the toolchain is for the correct architecture.
        assert tool_prefix is not None, (
            'Could not determine --tool-prefix. Possible fixes include setting '
            '--tool-prefix, or setting --output-directory')

        def build_id_matches(elf_path):
            found_build_id = archive.BuildIdFromElf(elf_path, tool_prefix)
            expected_build_id = size_info.metadata.get(
                models.METADATA_ELF_BUILD_ID)
            return found_build_id == expected_build_id

        filename = size_info.metadata.get(models.METADATA_ELF_FILENAME)
        paths_to_try = []
        if elf_path:
            paths_to_try.append(elf_path)
        else:
            auto_output_directory_finders = [
                path_util.OutputDirectoryFinder(
                    any_path_within_output_directory=s.size_path)
                for s in self._size_infos
            ] + [self._output_directory_finder]
            for output_directory_finder in auto_output_directory_finders:
                output_dir = output_directory_finder.Tentative()
                if output_dir:
                    # Local build: File is located in output directory.
                    paths_to_try.append(
                        os.path.normpath(os.path.join(output_dir, filename)))
                # Downloaded build: File is located beside .size file.
                paths_to_try.append(
                    os.path.normpath(
                        os.path.join(os.path.dirname(size_info.size_path),
                                     os.path.basename(filename))))

        paths_to_try = [p for p in paths_to_try if os.path.exists(p)]

        for i, elf_path in enumerate(paths_to_try):
            if build_id_matches(elf_path):
                return elf_path, tool_prefix

            # Show an error only once all paths are tried.
            if i + 1 == len(paths_to_try):
                assert False, 'Build ID does not match for %s' % elf_path

        assert False, (
            'Could not locate ELF file. If binary was built locally, ensure '
            '--output-directory is set. If output directory is unavailable, '
            'ensure {} is located beside {}, or pass its path explicitly using '
            'elf_path=').format(os.path.basename(filename),
                                size_info.size_path)
Beispiel #2
0
    def _DisassembleFunc(self,
                         symbol,
                         elf_path=None,
                         use_pager=None,
                         to_file=None):
        """Shows objdump disassembly for the given symbol.

    Args:
      symbol: Must be a .text symbol and not a SymbolGroup.
      elf_path: Path to the executable containing the symbol. Required only
          when auto-detection fails.
    """
        assert not symbol.IsGroup()
        assert symbol.address and symbol.section_name == models.SECTION_TEXT
        assert not symbol.IsDelta(), (
            'Cannot disasseble a Diff\'ed symbol. Try '
            'passing .before_symbol or .after_symbol.')
        size_info = self._SizeInfoForSymbol(symbol)
        tool_prefix = self._ToolPrefixForSymbol(size_info)
        elf_path = self._ElfPathForSymbol(size_info, tool_prefix, elf_path)

        args = [
            path_util.GetObjDumpPath(tool_prefix), '--disassemble', '--source',
            '--line-numbers',
            '--start-address=0x%x' % symbol.address,
            '--stop-address=0x%x' % symbol.end_address, elf_path
        ]
        # llvm-objdump does not support '--demangle' switch.
        if not self._tool_prefix_finder.IsLld():
            args.append('--demangle')
        if self._disassemble_prefix_len is None:
            prefix_len = self._DetectDisassemblePrefixLen(args)
            if prefix_len is not None:
                self._disassemble_prefix_len = prefix_len

        if self._disassemble_prefix_len is not None:
            output_directory = self._output_directory_finder.Tentative()
            # Only matters for non-generated paths, so be lenient here.
            if output_directory is None:
                output_directory = os.path.join(path_util.SRC_ROOT, 'out',
                                                'Release')
                if not os.path.exists(output_directory):
                    os.makedirs(output_directory)

            args += [
                '--prefix-strip',
                str(self._disassemble_prefix_len), '--prefix',
                os.path.normpath(os.path.relpath(output_directory))
            ]

        proc = subprocess.Popen(args, stdout=subprocess.PIPE)
        lines = itertools.chain(('Showing disassembly for %r' % symbol,
                                 'Command: %s' % ' '.join(args)),
                                (l.rstrip() for l in proc.stdout))
        _WriteToStream(lines, use_pager=use_pager, to_file=to_file)
        proc.kill()
Beispiel #3
0
    def _DisassembleFunc(self,
                         symbol,
                         elf_path=None,
                         use_pager=None,
                         to_file=None):
        """Shows objdump disassembly for the given symbol.

    Args:
      symbol: Must be a .text symbol and not a SymbolGroup.
      elf_path: Path to the executable containing the symbol. Required only
          when auto-detection fails.
    """
        assert not symbol.IsGroup()
        assert symbol.address and symbol.section_name == models.SECTION_TEXT
        assert not symbol.IsDelta(), (
            'Cannot disasseble a Diff\'ed symbol. Try '
            'passing .before_symbol or .after_symbol.')
        size_info = self._SizeInfoForSymbol(symbol)
        container = symbol.container
        tool_prefix = self._ToolPrefixForSymbol(size_info)
        elf_path = self._ElfPathForSymbol(size_info, container, tool_prefix,
                                          elf_path)
        # Always use Android NDK's objdump because llvm-objdump does not print
        # the target of jump instructions, which is really useful.
        output_directory_finder = self._output_directory_finder
        if not output_directory_finder.Tentative():
            output_directory_finder = path_util.OutputDirectoryFinder(
                any_path_within_output_directory=elf_path)
        if output_directory_finder.Tentative():
            tool_prefix = path_util.ToolPrefixFinder(
                output_directory=output_directory_finder.Finalized(),
                linker_name='ld').Finalized()

        args = [
            path_util.GetObjDumpPath(tool_prefix),
            '--disassemble',
            '--source',
            '--line-numbers',
            '--demangle',
            '--start-address=0x%x' % symbol.address,
            '--stop-address=0x%x' % symbol.end_address,
            elf_path,
        ]

        # pylint: disable=unexpected-keyword-arg
        proc = subprocess.Popen(args, stdout=subprocess.PIPE, encoding='utf-8')
        lines = itertools.chain(('Showing disassembly for %r' % symbol,
                                 'Command: %s' % ' '.join(args)),
                                (l.rstrip() for l in proc.stdout))
        _WriteToStream(lines, use_pager=use_pager, to_file=to_file)
        proc.kill()
Beispiel #4
0
    def _ToolPrefixForSymbol(self, size_info):
        tool_prefix = self._tool_prefix_finder.Tentative()
        orig_tool_prefix = size_info.metadata.get(models.METADATA_TOOL_PREFIX)
        if orig_tool_prefix:
            orig_tool_prefix = path_util.FromSrcRootRelative(orig_tool_prefix)
            if os.path.exists(path_util.GetObjDumpPath(orig_tool_prefix)):
                tool_prefix = orig_tool_prefix

        # TODO(agrieve): Would be even better to use objdump --info to check that
        #     the toolchain is for the correct architecture.
        assert tool_prefix is not None, (
            'Could not determine --tool-prefix. Possible fixes include setting '
            '--tool-prefix, or setting --output-directory')
        return tool_prefix
Beispiel #5
0
    def _DisassembleFunc(self,
                         symbol,
                         elf_path=None,
                         use_pager=None,
                         to_file=None):
        """Shows objdump disassembly for the given symbol.

    Args:
      symbol: Must be a .text symbol and not a SymbolGroup.
      elf_path: Path to the executable containing the symbol. Required only
          when auto-detection fails.
    """
        assert not symbol.IsGroup()
        assert symbol.address and symbol.section_name == models.SECTION_TEXT
        assert not symbol.IsDelta(), (
            'Cannot disasseble a Diff\'ed symbol. Try '
            'passing .before_symbol or .after_symbol.')
        size_info = self._SizeInfoForSymbol(symbol)
        container = symbol.container
        tool_prefix = self._ToolPrefixForSymbol(size_info)
        elf_path = self._ElfPathForSymbol(size_info, container, tool_prefix,
                                          elf_path)
        # Always use Android NDK's objdump because llvm-objdump does not print
        # the target of jump instructions, which is really useful.
        output_directory_finder = self._output_directory_finder
        if not output_directory_finder.Tentative():
            output_directory_finder = path_util.OutputDirectoryFinder(
                any_path_within_output_directory=elf_path)
        if output_directory_finder.Tentative():
            tool_prefix = path_util.ToolPrefixFinder(
                output_directory=output_directory_finder.Finalized(),
                linker_name='ld').Finalized()
            # Running objdump from an output directory means that objdump can
            # interleave source file lines in the disassembly.
            objdump_pwd = output_directory_finder.Finalized()
        else:
            # Output directory is not set, so we cannot load tool_prefix from
            # build_vars.json, nor resolve the output directory-relative path stored
            # size_info.metadata.
            is_android = next(
                filter(None, (m.get(models.METADATA_APK_FILENAME)
                              for m in size_info.metadata)), None)
            arch = next(
                filter(None, (m.get(models.METADATA_ELF_ARCHITECTURE)
                              for m in size_info.metadata)), None)
            # Hardcode path for arm32.
            if is_android and arch == 'arm':
                tool_prefix = path_util.ANDROID_ARM_NDK_TOOL_PREFIX
            # If we do not know/guess the output directory, run from any directory 2
            # levels below src since it is better than a random cwd (because usually
            # source file paths are relative to an output directory two levels below
            # src and start with ../../).
            objdump_pwd = os.path.join(path_util.TOOLS_SRC_ROOT, 'tools',
                                       'binary_size')

        args = [
            os.path.relpath(path_util.GetObjDumpPath(tool_prefix),
                            objdump_pwd),
            '--disassemble',
            '--source',
            '--line-numbers',
            '--demangle',
            '--start-address=0x%x' % symbol.address,
            '--stop-address=0x%x' % symbol.end_address,
            os.path.relpath(elf_path, objdump_pwd),
        ]

        # pylint: disable=unexpected-keyword-arg
        proc = subprocess.Popen(args,
                                stdout=subprocess.PIPE,
                                encoding='utf-8',
                                cwd=objdump_pwd)
        lines = itertools.chain(('Showing disassembly for %r' % symbol,
                                 'Command: %s' % ' '.join(args)),
                                (l.rstrip() for l in proc.stdout))
        _WriteToStream(lines, use_pager=use_pager, to_file=to_file)
        proc.kill()