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)
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()
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()
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
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()