123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- #!/usr/bin/env python3
- # Copyright 2018 The Chromium Authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- """Logic for reading .a files.
- Copied from //tools/binary_size/libsupersize/ar.py"""
- import os
- def _ResolveThinObjectPath(archive_path, subpath):
- """Given the .a path and .o subpath, returns the .o path."""
- # |subpath| is path complete under Gold, and incomplete under LLD. Check its
- # prefix to test completeness, and if not, use |archive_path| to supply the
- # required prefix.
- if subpath.startswith('obj/'):
- return subpath
- # .o subpaths in thin archives are relative to the directory of the .a.
- parent_path = os.path.dirname(archive_path)
- return os.path.normpath(os.path.join(parent_path, subpath))
- def _IterThinPaths(path):
- """Given the .a path, yields all nested .o paths."""
- # File format reference:
- # https://github.com/pathscale/binutils/blob/master/gold/archive.cc
- with open(path, 'rb') as f:
- header = f.read(8)
- is_thin = header == b'!<thin>\n'
- if not is_thin and header != b'!<arch>\n':
- raise Exception('Invalid .a: ' + path)
- if not is_thin:
- return
- def read_payload(size):
- ret = f.read(size)
- # Entries are 2-byte aligned.
- if size & 1 != 0:
- f.read(1)
- return ret
- while True:
- entry = f.read(60)
- if not entry:
- return
- entry_name = entry[:16].rstrip()
- entry_size = int(entry[48:58].rstrip())
- if entry_name in (b'', b'/', b'//', b'/SYM64/'):
- payload = read_payload(entry_size)
- # Metadata sections we don't care about.
- if entry_name == b'//':
- name_list = payload
- continue
- if entry_name[0:1] == b'/':
- # Name is specified as location in name table.
- # E.g.: /123
- name_offset = int(entry_name[1:])
- # String table enties are delimited by \n (e.g. "browser.o/\n").
- end_idx = name_list.index(b'\n', name_offset)
- entry_name = name_list[name_offset:end_idx]
- else:
- # Name specified inline with spaces for padding (e.g. "browser.o/ ").
- entry_name = entry_name.rstrip()
- yield entry_name.rstrip(b'/').decode('ascii')
- def ExpandThinArchives(paths):
- """Expands all thin archives found in |paths| into .o paths.
- Args:
- paths: List of paths relative to |output_directory|.
- output_directory: Output directory.
- Returns:
- * A new list of paths with all archives replaced by .o paths.
- """
- expanded_paths = []
- for path in paths:
- if not path.endswith('.a'):
- expanded_paths.append(path)
- continue
- with open(path, 'rb') as f:
- header = f.read(8)
- is_thin = header == b'!<thin>\n'
- if is_thin:
- for subpath in _IterThinPaths(path):
- expanded_paths.append(_ResolveThinObjectPath(path, subpath))
- elif header != b'!<arch>\n':
- raise Exception('Invalid .a: ' + path)
- return expanded_paths
|