tools/binary_size/libsupersize/archive.py
def _ParseElfInfo(map_path, elf_path, tool_prefix, track_string_literals,
                  outdir_context=None, linker_name=None):
  """Adds ELF section ranges and symbols."""
  assert map_path or elf_path, 'Need a linker map or an ELF file.'
  assert map_path or not track_string_literals, (
      'track_string_literals not yet implemented without map file')
  if elf_path:
    elf_section_ranges = readelf.SectionInfoFromElf(elf_path, tool_prefix)

    # Run nm on the elf file to retrieve the list of symbol names per-address.
    # This list is required because the .map file contains only a single name
    # for each address, yet multiple symbols are often coalesced when they are
    # identical. This coalescing happens mainly for small symbols and for C++
    # templates. Such symbols make up ~500kb of libchrome.so on Android.
    elf_nm_result = nm.CollectAliasesByAddressAsync(elf_path, tool_prefix)

    # Run nm on all .o/.a files to retrieve the symbol names within them.
    # The list is used to detect when mutiple .o files contain the same symbol
    # (e.g. inline functions), and to update the object_path / source_path
    # fields accordingly.
    # Looking in object files is required because the .map file choses a
    # single path for these symbols.
    # Rather than record all paths for each symbol, set the paths to be the
    # common ancestor of all paths.
    if outdir_context and map_path:
      bulk_analyzer = obj_analyzer.BulkObjectFileAnalyzer(
          tool_prefix, outdir_context.output_directory,
          track_string_literals=track_string_literals)
      bulk_analyzer.AnalyzePaths(outdir_context.elf_object_paths)

  if map_path:
    logging.info('Parsing Linker Map')
    with _OpenMaybeGzAsText(map_path) as f:
      map_section_ranges, raw_symbols, linker_map_extras = (
          linker_map_parser.MapFileParser().Parse(linker_name, f))

      if outdir_context and outdir_context.thin_archives:
        _ResolveThinArchivePaths(raw_symbols, outdir_context.thin_archives)
  else:
    logging.info('Collecting symbols from nm')
    raw_symbols = nm.CreateUniqueSymbols(elf_path, tool_prefix,
                                         elf_section_ranges)

  if elf_path and map_path:
    logging.debug('Validating section sizes')
    differing_elf_section_sizes = {}
    differing_map_section_sizes = {}
    for k, (_, elf_size) in elf_section_ranges.items():
      if k in _SECTION_SIZE_BLOCKLIST:
        continue
      (_, map_size) = map_section_ranges.get(k)
      if map_size != elf_size:
        differing_map_section_sizes[k] = map_size
        differing_elf_section_sizes[k] = elf_size
    if differing_map_section_sizes:
      logging.error('ELF file and .map file do not agree on section sizes.')
      logging.error('readelf: %r', differing_elf_section_sizes)
      logging.error('.map file: %r', differing_map_section_sizes)
      sys.exit(1)

  if elf_path and map_path and outdir_context:
    missed_object_paths = _DiscoverMissedObjectPaths(
        raw_symbols, outdir_context.known_inputs)
    missed_object_paths = ar.ExpandThinArchives(
        missed_object_paths, outdir_context.output_directory)[0]
    bulk_analyzer.AnalyzePaths(missed_object_paths)
    bulk_analyzer.SortPaths()
    if track_string_literals:
      merge_string_syms = [s for s in raw_symbols if
                           s.full_name == '** merge strings' or
                           s.full_name == '** lld merge strings']
      # More likely for there to be a bug in supersize than an ELF to not have a
      # single string literal.
      assert merge_string_syms
      string_ranges = [(s.address, s.size) for s in merge_string_syms]
      bulk_analyzer.AnalyzeStringLiterals(elf_path, string_ranges)

  # Map file for some reason doesn't demangle all names.
  # Demangle prints its own log statement.
  demangle.DemangleRemainingSymbols(raw_symbols, tool_prefix)

  object_paths_by_name = {}
  if elf_path:
    logging.info(
        'Adding symbols removed by identical code folding (as reported by nm)')
    # This normally does not block (it's finished by this time).
    names_by_address = elf_nm_result.get()
    _UpdateSymbolNamesFromNm(raw_symbols, names_by_address)

    raw_symbols = _AddNmAliases(raw_symbols, names_by_address)

    if map_path and outdir_context:
      object_paths_by_name = bulk_analyzer.GetSymbolNames()
      logging.debug(
          'Fetched path information for %d symbols from %d files',
          len(object_paths_by_name),
          len(outdir_context.elf_object_paths) + len(missed_object_paths))
      _DeduceObjectPathForSwitchTables(raw_symbols, object_paths_by_name)
      # For aliases, this provides path information where there wasn't any.
      logging.info('Creating aliases for symbols shared by multiple paths')
      raw_symbols = _AssignNmAliasPathsAndCreatePathAliases(
          raw_symbols, object_paths_by_name)

      if track_string_literals:
        logging.info('Waiting for string literal extraction to complete.')
        list_of_positions_by_object_path = bulk_analyzer.GetStringPositions()
      bulk_analyzer.Close()

      if track_string_literals:
        logging.info('Deconstructing ** merge strings into literals')
        replacements = _CreateMergeStringsReplacements(merge_string_syms,
            list_of_positions_by_object_path)
        for merge_sym, literal_syms in zip(merge_string_syms, replacements):
          # Don't replace if no literals were found.
          if literal_syms:
            # Re-find the symbols since aliases cause their indices to change.
            idx = raw_symbols.index(merge_sym)
            # This assignment is a bit slow (causes array to be shifted), but
            # is fast enough since len(merge_string_syms) < 10.
            raw_symbols[idx:idx + 1] = literal_syms

  if map_path:
    linker_map_parser.DeduceObjectPathsFromThinMap(raw_symbols,
                                                   linker_map_extras)

  if elf_path and track_string_literals:
    _NameStringLiterals(raw_symbols, elf_path, tool_prefix)

  # If we have an ELF file, use its ranges as the source of truth, since some
  # sections can differ from the .map.
  return (elf_section_ranges if elf_path else map_section_ranges, raw_symbols,
          object_paths_by_name)
Adds ELF section ranges and symbols.
Adds ELF section ranges and symbols.
Adds ELF section ranges and symbols .
def _ParseElfInfo(map_path, elf_path, tool_prefix, track_string_literals,
                  outdir_context=None, linker_name=None):
  assert map_path or elf_path, 'Need a linker map or an ELF file.'
  assert map_path or not track_string_literals, (
      'track_string_literals not yet implemented without map file')
  if elf_path:
    elf_section_ranges = readelf.SectionInfoFromElf(elf_path, tool_prefix)
    elf_nm_result = nm.CollectAliasesByAddressAsync(elf_path, tool_prefix)
    if outdir_context and map_path:
      bulk_analyzer = obj_analyzer.BulkObjectFileAnalyzer(
          tool_prefix, outdir_context.output_directory,
          track_string_literals=track_string_literals)
      bulk_analyzer.AnalyzePaths(outdir_context.elf_object_paths)
  if map_path:
    logging.info('Parsing Linker Map')
    with _OpenMaybeGzAsText(map_path) as f:
      map_section_ranges, raw_symbols, linker_map_extras = (
          linker_map_parser.MapFileParser().Parse(linker_name, f))
      if outdir_context and outdir_context.thin_archives:
        _ResolveThinArchivePaths(raw_symbols, outdir_context.thin_archives)
  else:
    logging.info('Collecting symbols from nm')
    raw_symbols = nm.CreateUniqueSymbols(elf_path, tool_prefix,
                                         elf_section_ranges)
  if elf_path and map_path:
    logging.debug('Validating section sizes')
    differing_elf_section_sizes = {}
    differing_map_section_sizes = {}
    for k, (_, elf_size) in elf_section_ranges.items():
      if k in _SECTION_SIZE_BLOCKLIST:
        continue
      (_, map_size) = map_section_ranges.get(k)
      if map_size != elf_size:
        differing_map_section_sizes[k] = map_size
        differing_elf_section_sizes[k] = elf_size
    if differing_map_section_sizes:
      logging.error('ELF file and .map file do not agree on section sizes.')
      logging.error('readelf: %r', differing_elf_section_sizes)
      logging.error('.map file: %r', differing_map_section_sizes)
      sys.exit(1)
  if elf_path and map_path and outdir_context:
    missed_object_paths = _DiscoverMissedObjectPaths(
        raw_symbols, outdir_context.known_inputs)
    missed_object_paths = ar.ExpandThinArchives(
        missed_object_paths, outdir_context.output_directory)[0]
    bulk_analyzer.AnalyzePaths(missed_object_paths)
    bulk_analyzer.SortPaths()
    if track_string_literals:
      merge_string_syms = [s for s in raw_symbols if
                           s.full_name == '** merge strings' or
                           s.full_name == '** lld merge strings']
      assert merge_string_syms
      string_ranges = [(s.address, s.size) for s in merge_string_syms]
      bulk_analyzer.AnalyzeStringLiterals(elf_path, string_ranges)
  demangle.DemangleRemainingSymbols(raw_symbols, tool_prefix)
  object_paths_by_name = {}
  if elf_path:
    logging.info(
        'Adding symbols removed by identical code folding (as reported by nm)')
    names_by_address = elf_nm_result.get()
    _UpdateSymbolNamesFromNm(raw_symbols, names_by_address)
    raw_symbols = _AddNmAliases(raw_symbols, names_by_address)
    if map_path and outdir_context:
      object_paths_by_name = bulk_analyzer.GetSymbolNames()
      logging.debug(
          'Fetched path information for %d symbols from %d files',
          len(object_paths_by_name),
          len(outdir_context.elf_object_paths) + len(missed_object_paths))
      _DeduceObjectPathForSwitchTables(raw_symbols, object_paths_by_name)
      logging.info('Creating aliases for symbols shared by multiple paths')
      raw_symbols = _AssignNmAliasPathsAndCreatePathAliases(
          raw_symbols, object_paths_by_name)
      if track_string_literals:
        logging.info('Waiting for string literal extraction to complete.')
        list_of_positions_by_object_path = bulk_analyzer.GetStringPositions()
      bulk_analyzer.Close()
      if track_string_literals:
        logging.info('Deconstructing ** merge strings into literals')
        replacements = _CreateMergeStringsReplacements(merge_string_syms,
            list_of_positions_by_object_path)
        for merge_sym, literal_syms in zip(merge_string_syms, replacements):
          if literal_syms:
            idx = raw_symbols.index(merge_sym)
            raw_symbols[idx:idx + 1] = literal_syms
  if map_path:
    linker_map_parser.DeduceObjectPathsFromThinMap(raw_symbols,
                                                   linker_map_extras)
  if elf_path and track_string_literals:
    _NameStringLiterals(raw_symbols, elf_path, tool_prefix)
  return (elf_section_ranges if elf_path else map_section_ranges, raw_symbols,
          object_paths_by_name)
def _ParseElfInfo ( map_path , elf_path , tool_prefix , track_string_literals , outdir_context = None , linker_name = None ) : assert map_path or elf_path , 'Need a linker map or an ELF file.' assert map_path or not track_string_literals , ( 'track_string_literals not yet implemented without map file' ) if elf_path : elf_section_ranges = readelf . SectionInfoFromElf ( elf_path , tool_prefix ) elf_nm_result = nm . CollectAliasesByAddressAsync ( elf_path , tool_prefix ) if outdir_context and map_path : bulk_analyzer = obj_analyzer . BulkObjectFileAnalyzer ( tool_prefix , outdir_context . output_directory , track_string_literals = track_string_literals ) bulk_analyzer . AnalyzePaths ( outdir_context . elf_object_paths ) if map_path : logging . info ( 'Parsing Linker Map' ) with _OpenMaybeGzAsText ( map_path ) as f : map_section_ranges , raw_symbols , linker_map_extras = ( linker_map_parser . MapFileParser ( ) . Parse ( linker_name , f ) ) if outdir_context and outdir_context . thin_archives : _ResolveThinArchivePaths ( raw_symbols , outdir_context . thin_archives ) else : logging . info ( 'Collecting symbols from nm' ) raw_symbols = nm . CreateUniqueSymbols ( elf_path , tool_prefix , elf_section_ranges ) if elf_path and map_path : logging . debug ( 'Validating section sizes' ) differing_elf_section_sizes = { } differing_map_section_sizes = { } for k , ( _ , elf_size ) in elf_section_ranges . items ( ) : if k in _SECTION_SIZE_BLOCKLIST : continue ( _ , map_size ) = map_section_ranges . get ( k ) if map_size != elf_size : differing_map_section_sizes [ k ] = map_size differing_elf_section_sizes [ k ] = elf_size if differing_map_section_sizes : logging . error ( 'ELF file and .map file do not agree on section sizes.' ) logging . error ( 'readelf: %r' , differing_elf_section_sizes ) logging . error ( '.map file: %r' , differing_map_section_sizes ) sys . exit ( 1 ) if elf_path and map_path and outdir_context : missed_object_paths = _DiscoverMissedObjectPaths ( raw_symbols , outdir_context . known_inputs ) missed_object_paths = ar . ExpandThinArchives ( missed_object_paths , outdir_context . output_directory ) [ 0 ] bulk_analyzer . AnalyzePaths ( missed_object_paths ) bulk_analyzer . SortPaths ( ) if track_string_literals : merge_string_syms = [ s for s in raw_symbols if s . full_name == '** merge strings' or s . full_name == '** lld merge strings' ] assert merge_string_syms string_ranges = [ ( s . address , s . size ) for s in merge_string_syms ] bulk_analyzer . AnalyzeStringLiterals ( elf_path , string_ranges ) demangle . DemangleRemainingSymbols ( raw_symbols , tool_prefix ) object_paths_by_name = { } if elf_path : logging . info ( 'Adding symbols removed by identical code folding (as reported by nm)' ) names_by_address = elf_nm_result . get ( ) _UpdateSymbolNamesFromNm ( raw_symbols , names_by_address ) raw_symbols = _AddNmAliases ( raw_symbols , names_by_address ) if map_path and outdir_context : object_paths_by_name = bulk_analyzer . GetSymbolNames ( ) logging . debug ( 'Fetched path information for %d symbols from %d files' , len ( object_paths_by_name ) , len ( outdir_context . elf_object_paths ) + len ( missed_object_paths ) ) _DeduceObjectPathForSwitchTables ( raw_symbols , object_paths_by_name ) logging . info ( 'Creating aliases for symbols shared by multiple paths' ) raw_symbols = _AssignNmAliasPathsAndCreatePathAliases ( raw_symbols , object_paths_by_name ) if track_string_literals : logging . info ( 'Waiting for string literal extraction to complete.' ) list_of_positions_by_object_path = bulk_analyzer . GetStringPositions ( ) bulk_analyzer . Close ( ) if track_string_literals : logging . info ( 'Deconstructing ** merge strings into literals' ) replacements = _CreateMergeStringsReplacements ( merge_string_syms , list_of_positions_by_object_path ) for merge_sym , literal_syms in zip ( merge_string_syms , replacements ) : if literal_syms : idx = raw_symbols . index ( merge_sym ) raw_symbols [ idx : idx + 1 ] = literal_syms if map_path : linker_map_parser . DeduceObjectPathsFromThinMap ( raw_symbols , linker_map_extras ) if elf_path and track_string_literals : _NameStringLiterals ( raw_symbols , elf_path , tool_prefix ) return ( elf_section_ranges if elf_path else map_section_ranges , raw_symbols , object_paths_by_name )
Adds ELF section ranges and symbols.
Adds ELF section ranges and symbols .
"""Adds ELF section ranges and symbols.""" # Run nm on the elf file to retrieve the list of symbol names per-address. # This list is required because the .map file contains only a single name # for each address, yet multiple symbols are often coalesced when they are # identical. This coalescing happens mainly for small symbols and for C++ # templates. Such symbols make up ~500kb of libchrome.so on Android. # Run nm on all .o/.a files to retrieve the symbol names within them. # The list is used to detect when mutiple .o files contain the same symbol # (e.g. inline functions), and to update the object_path / source_path # fields accordingly. # Looking in object files is required because the .map file choses a # single path for these symbols. # Rather than record all paths for each symbol, set the paths to be the # common ancestor of all paths. # More likely for there to be a bug in supersize than an ELF to not have a # single string literal. # Map file for some reason doesn't demangle all names. # Demangle prints its own log statement. # This normally does not block (it's finished by this time). # For aliases, this provides path information where there wasn't any. # Don't replace if no literals were found. # Re-find the symbols since aliases cause their indices to change. # This assignment is a bit slow (causes array to be shifted), but # is fast enough since len(merge_string_syms) < 10. # If we have an ELF file, use its ranges as the source of truth, since some # sections can differ from the .map.
map_path
elf_path
tool_prefix
track_string_literals
outdir_context
linker_name