blob: a6d95c78921d5195f131716ff7c067b50dd331b5 [file] [log] [blame]
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +08001#!/usr/bin/env python
2#
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
22
23
24class LsdumpError(Exception):
25 """The exception raised by ParseLsdumpFile."""
26 pass
27
28
29class AttrDict(dict):
30 """A dictionary with attribute accessors."""
31
32 def __getattr__(self, key):
33 """Returns self[key]."""
34 try:
35 return self[key]
36 except KeyError:
37 raise AttributeError('Failed to get attribute: %s' % key)
38
39 def __setattr__(self, key, value):
40 """Assigns value to self[key]."""
41 self[key] = value
42
43
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +080044def _GetTypeSymbol(record_type):
45 """Gets the mangled name of a record type.
46
47 Before Android R, unique_id was mangled name starting with "_ZTS".
48 linker_set_key was unmangled.
49 Since Android R, unique_id has been removed, and linker_set_key has
50 been changed to mangled name starting with "_ZTI".
51
52 Args:
53 record_type: An AttrDict, a record type in lsdump.
54
55 Returns:
56 A string, the mangled name starting with "_ZTI".
57 """
58 if "unique_id" in record_type:
59 return record_type["unique_id"].replace("_ZTS", "_ZTI", 1)
60 return record_type["linker_set_key"]
61
62
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +080063def _OpenFileOrGzipped(file_name):
64 """Opens a file that is either in gzip or uncompressed format.
65
66 If file_name ends with '.gz' then return gzip.open(file_name, 'rb'),
67 else return open(file_name, 'rb').
68
69 Args:
70 file_name: The file name to open.
71
72 Returns:
73 A file object.
74
75 Raises:
76 IOError if fails to open.
77 """
78 if file_name.endswith('.gz'):
79 return gzip.open(file_name, 'rb')
80 return open(file_name, 'rb')
81
82
83def _ConsumeOffset(tok, beg=0):
84 """Consumes a <offset-number> in a thunk symbol."""
85 pos = tok.find('_', beg) + 1
86 return tok[:pos], tok[pos:]
87
88
89def _ConsumeCallOffset(tok):
90 """Consumes a <call-offset> in a thunk symbol."""
91 if tok[:1] == 'h':
Hsin-Yi Chen6c16c812018-09-28 18:52:06 +080092 lhs, rhs = _ConsumeOffset(tok, 1)
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +080093 elif tok[:1] == 'v':
Hsin-Yi Chen6c16c812018-09-28 18:52:06 +080094 lhs, rhs = _ConsumeOffset(tok, 1)
95 lhs2, rhs = _ConsumeOffset(rhs)
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +080096 if lhs and lhs2:
97 lhs = lhs + lhs2
98 else:
99 lhs, rhs = '', tok
100 else:
101 lhs, rhs = '', tok
102 return lhs, rhs
103
104
105def _FindThunkTarget(name):
106 """Finds thunk symbol's target function.
107
108 <thunk-symbol> ::= _ZT <call-offset> <base-encoding>
109 | _ZTc <call-offset> <call-offset> <base-encoding>
110 <call-offset> ::= h <nv-offset>
111 | v <v-offset>
112 <nv-offset> ::= <offset-number> _
113 <v-offset> ::= <offset-number> _ <offset-number> _
114
115 Args:
116 name: A string, the symbol name to resolve.
117
118 Returns:
119 A string, symbol name of the nominal target function.
120 The input symbol name if it is not a thunk symbol.
121 """
122 if name.startswith('_ZTh') or name.startswith('_ZTv'):
123 lhs, rhs = _ConsumeCallOffset(name[len('_ZT'):])
124 if lhs:
125 return '_Z' + rhs
126 if name.startswith('_ZTc'):
127 lhs, rhs = _ConsumeCallOffset(name[len('_ZTc'):])
128 lhs2, rhs = _ConsumeCallOffset(rhs)
129 if lhs and lhs2:
130 return '_Z' + rhs
131 return name
132
133
134def _FilterElfFunctions(lsdump):
135 """Finds exported functions and thunks in lsdump.
136
137 Args:
138 lsdump: An AttrDict object containing the lsdump.
139
140 Yields:
141 The AttrDict objects in lsdump.elf_functions.
142 """
143 functions = {function.linker_set_key for function in lsdump.functions}
144
145 for elf_function in lsdump.elf_functions:
146 if elf_function.name in functions:
147 yield elf_function
148 elif _FindThunkTarget(elf_function.name) in functions:
149 yield elf_function
150
151
152def _FilterElfObjects(lsdump):
153 """Finds exported variables, type info, and vtables in lsdump.
154
155 Args:
156 lsdump: An AttrDict object containing the lsdump.
157
158 Yields:
159 The AttrDict objects in lsdump.elf_objects.
160 """
161 global_vars = {global_var.linker_set_key
162 for global_var in lsdump.global_vars}
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +0800163 record_names = {_GetTypeSymbol(record_type)[len('_ZTI'):]
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800164 for record_type in lsdump.record_types}
165
166 for elf_object in lsdump.elf_objects:
167 name = elf_object.name
168 if name in global_vars:
169 yield elf_object
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +0800170 elif (name[:len('_ZTI')] in {'_ZTV', '_ZTT', '_ZTI', '_ZTS'} and
171 name[len('_ZTI'):] in record_names):
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800172 yield elf_object
173
174
175def _ParseSymbolsFromLsdump(lsdump, output_dump):
176 """Parses symbols from an lsdump.
177
178 Args:
179 lsdump: An AttrDict object containing the lsdump.
180 output_dump: An AttrDict object containing the output.
181 """
182 output_dump.elf_functions = list(_FilterElfFunctions(lsdump))
183 output_dump.elf_objects = list(_FilterElfObjects(lsdump))
184
185
186def _ParseVtablesFromLsdump(lsdump, output_dump):
187 """Parses vtables from an lsdump.
188
189 Args:
190 lsdump: An AttrDict object containing the lsdump.
191 output_dump: An AttrDict object containing the output.
192 """
193 vtable_symbols = {elf_object.name for elf_object in lsdump.elf_objects
194 if elf_object.name.startswith('_ZTV')}
195
196 output_dump.record_types = []
197 for lsdump_record_type in lsdump.record_types:
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +0800198 type_symbol = _GetTypeSymbol(lsdump_record_type)
199 vtable_symbol = '_ZTV' + type_symbol[len('_ZTI'):]
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800200 if vtable_symbol not in vtable_symbols:
201 continue
202 record_type = AttrDict()
Hsin-Yi Chen850a88f2019-05-03 19:22:12 +0800203 record_type.linker_set_key = type_symbol
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +0800204 record_type.vtable_components = lsdump_record_type.vtable_components
205 output_dump.record_types.append(record_type)
206
207
208def ParseLsdumpFile(input_path, output_path):
209 """Converts an lsdump file to a dump file for the ABI test.
210
211 Args:
212 input_path: The path to the (gzipped) lsdump file.
213 output_path: The path to the output dump file.
214
215 Raises:
216 LsdumpError if fails to create the dump file.
217 """
218 try:
219 with _OpenFileOrGzipped(input_path) as lsdump_file:
220 lsdump = json.load(lsdump_file, object_hook=AttrDict)
221 except (IOError, ValueError) as e:
222 raise LsdumpError(e)
223
224 try:
225 output_dump = AttrDict()
226 _ParseVtablesFromLsdump(lsdump, output_dump)
227 _ParseSymbolsFromLsdump(lsdump, output_dump)
228 except AttributeError as e:
229 raise LsdumpError(e)
230
231 abs_output_path = os.path.abspath(output_path)
232 abs_output_dir = os.path.dirname(abs_output_path)
233
234 try:
235 if abs_output_dir and not os.path.exists(abs_output_dir):
236 os.makedirs(abs_output_dir)
237 with open(output_path, 'wb') as output_file:
238 json.dump(output_dump, output_file,
239 indent=1, separators=(',', ':'))
240 except (IOError, OSError) as e:
241 raise LsdumpError(e)
242
243
244def main():
245 arg_parser = argparse.ArgumentParser(
246 description='This script converts an lsdump file to a dump file for '
247 'the ABI test in VTS.')
248 arg_parser.add_argument('input_path',
249 help='input lsdump file path.')
250 arg_parser.add_argument('output_path',
251 help='output dump file path.')
252 args = arg_parser.parse_args()
253
254 try:
255 ParseLsdumpFile(args.input_path, args.output_path)
256 except LsdumpError as e:
257 print(e)
258 exit(1)
259
260
261if __name__ == '__main__':
262 main()