2 # Copyright 2019 The Chromium Authors
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5 """Extracts an LLD partition from an ELF file."""
17 def _ComputeNewBuildId(old_build_id, file_path):
19 Computes the new build-id from old build-id and file_path.
22 old_build_id: Original build-id in bytearray.
23 file_path: Path to output ELF file.
26 New build id with the same length as |old_build_id|.
29 m.update(old_build_id)
30 m.update(os.path.basename(file_path).encode('utf-8'))
31 hash_bytes = m.digest()
32 # In case build_id is longer than hash computed, repeat the hash
33 # to the desired length first.
34 id_size = len(old_build_id)
35 hash_size = len(hash_bytes)
36 return (hash_bytes * (id_size // hash_size + 1))[:id_size]
39 def _ExtractPartition(objcopy, input_elf, output_elf, partition):
41 Extracts a partition from an ELF file.
43 For partitions other than main partition, we need to rewrite
44 the .note.gnu.build-id section so that the build-id remains
48 - `objcopy` does not modify build-id when partitioning the
49 combined ELF file by default.
50 - The new build-id is calculated as hash of original build-id
51 and partitioned ELF file name.
54 objcopy: Path to objcopy binary.
55 input_elf: Path to input ELF file.
56 output_elf: Path to output ELF file.
57 partition: Partition to extract from combined ELF file. None when
58 extracting main partition.
60 if not partition: # main partition
61 # We do not overwrite build-id on main partition to allow the expected
62 # partition build ids to be synthesized given a libchrome.so binary,
64 subprocess.check_call(
65 [objcopy, '--extract-main-partition', input_elf, output_elf])
69 build_id_section = '.note.gnu.build-id'
71 with tempfile.TemporaryDirectory() as tempdir:
72 temp_elf = os.path.join(tempdir, 'obj_without_id.so')
73 old_build_id_file = os.path.join(tempdir, 'old_build_id')
74 new_build_id_file = os.path.join(tempdir, 'new_build_id')
76 # Dump out build-id section and remove original build-id section from
78 subprocess.check_call([
80 '--extract-partition',
82 # Note: Not using '--update-section' here as it is not supported
87 '{}={}'.format(build_id_section, old_build_id_file),
92 with open(old_build_id_file, 'rb') as f:
93 note_content = f.read()
95 # .note section has following format according to <elf/external.h>
97 # unsigned char namesz[4]; /* Size of entry's owner string */
98 # unsigned char descsz[4]; /* Size of the note descriptor */
99 # unsigned char type[4]; /* Interpretation of the descriptor */
100 # char name[1]; /* Start of the name+desc data */
101 # } Elf_External_Note;
102 # `build-id` rewrite is only required on Android platform,
103 # where we have partitioned lib.
104 # Android platform uses little-endian.
107 # L: unsigned long, 4 bytes
108 descsz, = struct.Struct('<4xL').unpack_from(note_content)
109 prefix = note_content[:-descsz]
110 build_id = note_content[-descsz:]
112 with open(new_build_id_file, 'wb') as f:
113 f.write(prefix + _ComputeNewBuildId(build_id, output_elf))
115 # Write back the new build-id section.
116 subprocess.check_call([
119 '{}={}'.format(build_id_section, new_build_id_file),
120 # Add alloc section flag, or else the section will be removed by
121 # objcopy --strip-all when generating unstripped lib file.
122 '--set-section-flags',
123 '{}={}'.format(build_id_section, 'alloc'),
130 parser = argparse.ArgumentParser(description=__doc__)
133 help='Name of partition if not the main partition',
138 help='Path to llvm-objcopy binary',
141 '--unstripped-output',
143 help='Unstripped output file',
148 help='Stripped output file',
150 parser.add_argument('--split-dwarf', action='store_true')
151 parser.add_argument('input', help='Input file')
152 args = parser.parse_args()
154 _ExtractPartition(args.objcopy, args.input, args.unstripped_output,
156 subprocess.check_call([
159 args.unstripped_output,
160 args.stripped_output,
163 # Debug info for partitions is the same as for the main library, so just
164 # symlink the .dwp files.
166 dest = args.unstripped_output + '.dwp'
171 relpath = os.path.relpath(args.input + '.dwp', os.path.dirname(dest))
172 os.symlink(relpath, dest)
175 if __name__ == '__main__':