blob: 8849d48bdf8649dc6793b67da5db05b5c7751279 [file] [log] [blame]
Hsin-Yi Chen97e65ca2020-06-23 12:02:07 +08001#!/usr/bin/env python3
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +08002#
3# Copyright (C) 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import argparse
19import gzip
20import json
21import os
Hsin-Yi Chen97e65ca2020-06-23 12:02:07 +080022import sys
Dan Shif5c3e3c2020-03-11 15:40:11 +000023import zipfile
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +080024
25
26class LsdumpError(Exception):
27 """The exception raised by ParseLsdumpFile."""
28 pass
29
30
31class AttrDict(dict):
32 """A dictionary with attribute accessors."""
33
34 def __getattr__(self, key):
35 """Returns self[key]."""
36 try:
37 return self[key]
38 except KeyError:
39 raise AttributeError('Failed to get attribute: %s' % key)
40
41 def __setattr__(self, key, value):
42 """Assigns value to self[key]."""
43 self[key] = value
44
45
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +080046def _GetTypeSymbol(record_type):
47 """Gets the mangled name of a record type.
48
Hsin-Yi Chen97e65ca2020-06-23 12:02:07 +080049 Before Android R, unique_id was mangled name starting with '_ZTS'.
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +080050 linker_set_key was unmangled.
51 Since Android R, unique_id has been removed, and linker_set_key has
Hsin-Yi Chen97e65ca2020-06-23 12:02:07 +080052 been changed to mangled name starting with '_ZTI'.
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +080053
54 Args:
55 record_type: An AttrDict, a record type in lsdump.
56
57 Returns:
Hsin-Yi Chen97e65ca2020-06-23 12:02:07 +080058 A string, the mangled name starting with '_ZTI'.
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +080059 """
Hsin-Yi Chen97e65ca2020-06-23 12:02:07 +080060 if 'unique_id' in record_type:
61 return record_type['unique_id'].replace('_ZTS', '_ZTI', 1)
62 return record_type['linker_set_key']
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +080063
64
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +080065def _OpenFileOrGzipped(file_name):
66 """Opens a file that is either in gzip or uncompressed format.
67
Hsin-Yi Chen97e65ca2020-06-23 12:02:07 +080068 If file_name ends with '.gz' then an opened gzip text file, else return an
69 opened text file.
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +080070
71 Args:
72 file_name: The file name to open.
73
74 Returns:
75 A file object.
76
77 Raises:
78 IOError if fails to open.
79 """
80 if file_name.endswith('.gz'):
Hsin-Yi Chen97e65ca2020-06-23 12:02:07 +080081 return gzip.open(file_name, 'rt')
82 return open(file_name, 'r')
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +080083
84
85def _ConsumeOffset(tok, beg=0):
86 """Consumes a <offset-number> in a thunk symbol."""
87 pos = tok.find('_', beg) + 1
88 return tok[:pos], tok[pos:]
89
90
91def _ConsumeCallOffset(tok):
92 """Consumes a <call-offset> in a thunk symbol."""
93 if tok[:1] == 'h':
Hsin-Yi Chen6c16c812018-09-28 18:52:06 +080094 lhs, rhs = _ConsumeOffset(tok, 1)
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +080095 elif tok[:1] == 'v':
Hsin-Yi Chen6c16c812018-09-28 18:52:06 +080096 lhs, rhs = _ConsumeOffset(tok, 1)
97 lhs2, rhs = _ConsumeOffset(rhs)
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +080098 if lhs and lhs2:
99 lhs = lhs + lhs2
100 else:
101 lhs, rhs = '', tok
102 else:
103 lhs, rhs = '', tok
104 return lhs, rhs
105
106
107def _FindThunkTarget(name):
108 """Finds thunk symbol's target function.
109
110 <thunk-symbol> ::= _ZT <call-offset> <base-encoding>
111 | _ZTc <call-offset> <call-offset> <base-encoding>
112 <call-offset> ::= h <nv-offset>
113 | v <v-offset>
114 <nv-offset> ::= <offset-number> _
115 <v-offset> ::= <offset-number> _ <offset-number> _
116
117 Args:
118 name: A string, the symbol name to resolve.
119
120 Returns:
121 A string, symbol name of the nominal target function.
122 The input symbol name if it is not a thunk symbol.
123 """
124 if name.startswith('_ZTh') or name.startswith('_ZTv'):
125 lhs, rhs = _ConsumeCallOffset(name[len('_ZT'):])
126 if lhs:
127 return '_Z' + rhs
128 if name.startswith('_ZTc'):
129 lhs, rhs = _ConsumeCallOffset(name[len('_ZTc'):])
130 lhs2, rhs = _ConsumeCallOffset(rhs)
131 if lhs and lhs2:
132 return '_Z' + rhs
133 return name
134
135
136def _FilterElfFunctions(lsdump):
137 """Finds exported functions and thunks in lsdump.
138
139 Args:
140 lsdump: An AttrDict object containing the lsdump.
141
142 Yields:
143 The AttrDict objects in lsdump.elf_functions.
144 """
145 functions = {function.linker_set_key for function in lsdump.functions}
146
147 for elf_function in lsdump.elf_functions:
148 if elf_function.name in functions:
149 yield elf_function
150 elif _FindThunkTarget(elf_function.name) in functions:
151 yield elf_function
152
153
154def _FilterElfObjects(lsdump):
155 """Finds exported variables, type info, and vtables in lsdump.
156
157 Args:
158 lsdump: An AttrDict object containing the lsdump.
159
160 Yields:
161 The AttrDict objects in lsdump.elf_objects.
162 """
163 global_vars = {global_var.linker_set_key
164 for global_var in lsdump.global_vars}
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +0800165 record_names = {_GetTypeSymbol(record_type)[len('_ZTI'):]
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800166 for record_type in lsdump.record_types}
167
168 for elf_object in lsdump.elf_objects:
169 name = elf_object.name
170 if name in global_vars:
171 yield elf_object
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +0800172 elif (name[:len('_ZTI')] in {'_ZTV', '_ZTT', '_ZTI', '_ZTS'} and
173 name[len('_ZTI'):] in record_names):
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800174 yield elf_object
175
176
177def _ParseSymbolsFromLsdump(lsdump, output_dump):
178 """Parses symbols from an lsdump.
179
180 Args:
181 lsdump: An AttrDict object containing the lsdump.
182 output_dump: An AttrDict object containing the output.
183 """
184 output_dump.elf_functions = list(_FilterElfFunctions(lsdump))
185 output_dump.elf_objects = list(_FilterElfObjects(lsdump))
186
187
188def _ParseVtablesFromLsdump(lsdump, output_dump):
189 """Parses vtables from an lsdump.
190
191 Args:
192 lsdump: An AttrDict object containing the lsdump.
193 output_dump: An AttrDict object containing the output.
194 """
195 vtable_symbols = {elf_object.name for elf_object in lsdump.elf_objects
196 if elf_object.name.startswith('_ZTV')}
197
198 output_dump.record_types = []
199 for lsdump_record_type in lsdump.record_types:
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +0800200 type_symbol = _GetTypeSymbol(lsdump_record_type)
201 vtable_symbol = '_ZTV' + type_symbol[len('_ZTI'):]
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800202 if vtable_symbol not in vtable_symbols:
203 continue
204 record_type = AttrDict()
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +0800205 record_type.linker_set_key = type_symbol
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800206 record_type.vtable_components = lsdump_record_type.vtable_components
207 output_dump.record_types.append(record_type)
208
209
Dan Shif5c3e3c2020-03-11 15:40:11 +0000210def _ParseLsdumpFileObject(lsdump_file):
211 """Converts an lsdump file to a dump object.
212
213 Args:
214 lsdump_file: The file object of the input lsdump.
215
216 Returns:
217 The AttrDict object converted from the lsdump file.
218
219 Raises:
220 LsdumpError if fails to create the dump file.
221 """
222 try:
223 lsdump = json.load(lsdump_file, object_hook=AttrDict)
224 except ValueError:
225 raise LsdumpError(e)
226
227 try:
228 output_dump = AttrDict()
229 _ParseVtablesFromLsdump(lsdump, output_dump)
230 _ParseSymbolsFromLsdump(lsdump, output_dump)
231 return output_dump
232 except AttributeError as e:
233 raise LsdumpError(e)
234
235
236def _ParseLsdumpZipFile(lsdump_zip, output_zip):
237 """Converts zipped lsdump files to the dump files for ABI test."""
238 for name in lsdump_zip.namelist():
Hsin-Yi Chen97e65ca2020-06-23 12:02:07 +0800239 if name.endswith('.lsdump'):
240 with lsdump_zip.open(name, mode='r') as lsdump_file:
Dan Shif5c3e3c2020-03-11 15:40:11 +0000241 output_dump = _ParseLsdumpFileObject(lsdump_file)
242 output_str = json.dumps(output_dump, indent=1,
243 separators=(',', ':'))
244 output_zip.writestr(name, output_str)
245
246
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800247def ParseLsdumpFile(input_path, output_path):
248 """Converts an lsdump file to a dump file for the ABI test.
249
250 Args:
251 input_path: The path to the (gzipped) lsdump file.
252 output_path: The path to the output dump file.
253
254 Raises:
255 LsdumpError if fails to create the dump file.
256 """
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800257 abs_output_path = os.path.abspath(output_path)
258 abs_output_dir = os.path.dirname(abs_output_path)
259
260 try:
261 if abs_output_dir and not os.path.exists(abs_output_dir):
262 os.makedirs(abs_output_dir)
Dan Shif5c3e3c2020-03-11 15:40:11 +0000263
264 with _OpenFileOrGzipped(input_path) as lsdump_file:
265 output_dump = _ParseLsdumpFileObject(lsdump_file)
Hsin-Yi Chen97e65ca2020-06-23 12:02:07 +0800266 with open(output_path, 'w') as output_file:
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800267 json.dump(output_dump, output_file,
268 indent=1, separators=(',', ':'))
269 except (IOError, OSError) as e:
270 raise LsdumpError(e)
271
272
273def main():
274 arg_parser = argparse.ArgumentParser(
275 description='This script converts an lsdump file to a dump file for '
276 'the ABI test in VTS.')
277 arg_parser.add_argument('input_path',
278 help='input lsdump file path.')
279 arg_parser.add_argument('output_path',
280 help='output dump file path.')
281 args = arg_parser.parse_args()
282
Dan Shif5c3e3c2020-03-11 15:40:11 +0000283 # TODO(b/150663999): Remove this when the build system is able to process
284 # the large file group.
285 if zipfile.is_zipfile(args.input_path):
286 with zipfile.ZipFile(args.input_path, mode='r') as input_zip:
287 # The zip will be added to a Python package. It is not necessary
288 # to reduce the file size.
289 with zipfile.ZipFile(args.output_path, mode='w',
290 compression=zipfile.ZIP_STORED) as output_zip:
291 _ParseLsdumpZipFile(input_zip, output_zip)
Hsin-Yi Chen97e65ca2020-06-23 12:02:07 +0800292 sys.exit(0)
Dan Shif5c3e3c2020-03-11 15:40:11 +0000293
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800294 try:
295 ParseLsdumpFile(args.input_path, args.output_path)
296 except LsdumpError as e:
297 print(e)
Hsin-Yi Chen97e65ca2020-06-23 12:02:07 +0800298 sys.exit(1)
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800299
300
301if __name__ == '__main__':
302 main()