summaryrefslogtreecommitdiff
path: root/chromium/tools/resources/ar.py
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/tools/resources/ar.py')
-rwxr-xr-xchromium/tools/resources/ar.py96
1 files changed, 96 insertions, 0 deletions
diff --git a/chromium/tools/resources/ar.py b/chromium/tools/resources/ar.py
new file mode 100755
index 00000000000..19c294fba54
--- /dev/null
+++ b/chromium/tools/resources/ar.py
@@ -0,0 +1,96 @@
+#!/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