Fix FullScreen crash in Webapp
[platform/framework/web/chromium-efl.git] / build / extract_partition.py
1 #!/usr/bin/env python3
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."""
6
7 import argparse
8 import hashlib
9 import math
10 import os
11 import struct
12 import subprocess
13 import sys
14 import tempfile
15
16
17 def _ComputeNewBuildId(old_build_id, file_path):
18   """
19     Computes the new build-id from old build-id and file_path.
20
21     Args:
22       old_build_id: Original build-id in bytearray.
23       file_path: Path to output ELF file.
24
25     Returns:
26       New build id with the same length as |old_build_id|.
27     """
28   m = hashlib.sha256()
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]
37
38
39 def _ExtractPartition(objcopy, input_elf, output_elf, partition):
40   """
41   Extracts a partition from an ELF file.
42
43   For partitions other than main partition, we need to rewrite
44   the .note.gnu.build-id section so that the build-id remains
45   unique.
46
47   Note:
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.
52
53   Args:
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.
59   """
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,
63     # if necessary.
64     subprocess.check_call(
65         [objcopy, '--extract-main-partition', input_elf, output_elf])
66     return
67
68   # partitioned libs
69   build_id_section = '.note.gnu.build-id'
70
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')
75
76     # Dump out build-id section and remove original build-id section from
77     # ELF file.
78     subprocess.check_call([
79         objcopy,
80         '--extract-partition',
81         partition,
82         # Note: Not using '--update-section' here as it is not supported
83         # by llvm-objcopy.
84         '--remove-section',
85         build_id_section,
86         '--dump-section',
87         '{}={}'.format(build_id_section, old_build_id_file),
88         input_elf,
89         temp_elf,
90     ])
91
92     with open(old_build_id_file, 'rb') as f:
93       note_content = f.read()
94
95     # .note section has following format according to <elf/external.h>
96     #   typedef struct {
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.
105     # <: little-endian
106     # 4x: Skip 4 bytes
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:]
111
112     with open(new_build_id_file, 'wb') as f:
113       f.write(prefix + _ComputeNewBuildId(build_id, output_elf))
114
115     # Write back the new build-id section.
116     subprocess.check_call([
117         objcopy,
118         '--add-section',
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'),
124         temp_elf,
125         output_elf,
126     ])
127
128
129 def main():
130   parser = argparse.ArgumentParser(description=__doc__)
131   parser.add_argument(
132       '--partition',
133       help='Name of partition if not the main partition',
134       metavar='PART')
135   parser.add_argument(
136       '--objcopy',
137       required=True,
138       help='Path to llvm-objcopy binary',
139       metavar='FILE')
140   parser.add_argument(
141       '--unstripped-output',
142       required=True,
143       help='Unstripped output file',
144       metavar='FILE')
145   parser.add_argument(
146       '--stripped-output',
147       required=True,
148       help='Stripped output file',
149       metavar='FILE')
150   parser.add_argument('--split-dwarf', action='store_true')
151   parser.add_argument('input', help='Input file')
152   args = parser.parse_args()
153
154   _ExtractPartition(args.objcopy, args.input, args.unstripped_output,
155                     args.partition)
156   subprocess.check_call([
157       args.objcopy,
158       '--strip-all',
159       args.unstripped_output,
160       args.stripped_output,
161   ])
162
163   # Debug info for partitions is the same as for the main library, so just
164   # symlink the .dwp files.
165   if args.split_dwarf:
166     dest = args.unstripped_output + '.dwp'
167     try:
168       os.unlink(dest)
169     except OSError:
170       pass
171     relpath = os.path.relpath(args.input + '.dwp', os.path.dirname(dest))
172     os.symlink(relpath, dest)
173
174
175 if __name__ == '__main__':
176   sys.exit(main())