blob: 45e9c3817b598e48839f41923cfc296eb0eb381a [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
44def _OpenFileOrGzipped(file_name):
45 """Opens a file that is either in gzip or uncompressed format.
46
47 If file_name ends with '.gz' then return gzip.open(file_name, 'rb'),
48 else return open(file_name, 'rb').
49
50 Args:
51 file_name: The file name to open.
52
53 Returns:
54 A file object.
55
56 Raises:
57 IOError if fails to open.
58 """
59 if file_name.endswith('.gz'):
60 return gzip.open(file_name, 'rb')
61 return open(file_name, 'rb')
62
63
64def _ConsumeOffset(tok, beg=0):
65 """Consumes a <offset-number> in a thunk symbol."""
66 pos = tok.find('_', beg) + 1
67 return tok[:pos], tok[pos:]
68
69
70def _ConsumeCallOffset(tok):
71 """Consumes a <call-offset> in a thunk symbol."""
72 if tok[:1] == 'h':
Hsin-Yi Chen6c16c812018-09-28 18:52:06 +080073 lhs, rhs = _ConsumeOffset(tok, 1)
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +080074 elif tok[:1] == 'v':
Hsin-Yi Chen6c16c812018-09-28 18:52:06 +080075 lhs, rhs = _ConsumeOffset(tok, 1)
76 lhs2, rhs = _ConsumeOffset(rhs)
Hsin-Yi Chen6864d8d2018-09-25 20:01:51 +080077 if lhs and lhs2:
78 lhs = lhs + lhs2
79 else:
80 lhs, rhs = '', tok
81 else:
82 lhs, rhs = '', tok
83 return lhs, rhs
84
85
86def _FindThunkTarget(name):
87 """Finds thunk symbol's target function.
88
89 <thunk-symbol> ::= _ZT <call-offset> <base-encoding>
90 | _ZTc <call-offset> <call-offset> <base-encoding>
91 <call-offset> ::= h <nv-offset>
92 | v <v-offset>
93 <nv-offset> ::= <offset-number> _
94 <v-offset> ::= <offset-number> _ <offset-number> _
95
96 Args:
97 name: A string, the symbol name to resolve.
98
99 Returns:
100 A string, symbol name of the nominal target function.
101 The input symbol name if it is not a thunk symbol.
102 """
103 if name.startswith('_ZTh') or name.startswith('_ZTv'):
104 lhs, rhs = _ConsumeCallOffset(name[len('_ZT'):])
105 if lhs:
106 return '_Z' + rhs
107 if name.startswith('_ZTc'):
108 lhs, rhs = _ConsumeCallOffset(name[len('_ZTc'):])
109 lhs2, rhs = _ConsumeCallOffset(rhs)
110 if lhs and lhs2:
111 return '_Z' + rhs
112 return name
113
114
115def _FilterElfFunctions(lsdump):
116 """Finds exported functions and thunks in lsdump.
117
118 Args:
119 lsdump: An AttrDict object containing the lsdump.
120
121 Yields:
122 The AttrDict objects in lsdump.elf_functions.
123 """
124 functions = {function.linker_set_key for function in lsdump.functions}
125
126 for elf_function in lsdump.elf_functions:
127 if elf_function.name in functions:
128 yield elf_function
129 elif _FindThunkTarget(elf_function.name) in functions:
130 yield elf_function
131
132
133def _FilterElfObjects(lsdump):
134 """Finds exported variables, type info, and vtables in lsdump.
135
136 Args:
137 lsdump: An AttrDict object containing the lsdump.
138
139 Yields:
140 The AttrDict objects in lsdump.elf_objects.
141 """
142 global_vars = {global_var.linker_set_key
143 for global_var in lsdump.global_vars}
144 record_names = {record_type.unique_id[len('_ZTS'):]
145 for record_type in lsdump.record_types}
146
147 for elf_object in lsdump.elf_objects:
148 name = elf_object.name
149 if name in global_vars:
150 yield elf_object
151 elif (name[:len('_ZTS')] in {'_ZTV', '_ZTT', '_ZTI', '_ZTS'} and
152 name[len('_ZTS'):] in record_names):
153 yield elf_object
154
155
156def _ParseSymbolsFromLsdump(lsdump, output_dump):
157 """Parses symbols from an lsdump.
158
159 Args:
160 lsdump: An AttrDict object containing the lsdump.
161 output_dump: An AttrDict object containing the output.
162 """
163 output_dump.elf_functions = list(_FilterElfFunctions(lsdump))
164 output_dump.elf_objects = list(_FilterElfObjects(lsdump))
165
166
167def _ParseVtablesFromLsdump(lsdump, output_dump):
168 """Parses vtables from an lsdump.
169
170 Args:
171 lsdump: An AttrDict object containing the lsdump.
172 output_dump: An AttrDict object containing the output.
173 """
174 vtable_symbols = {elf_object.name for elf_object in lsdump.elf_objects
175 if elf_object.name.startswith('_ZTV')}
176
177 output_dump.record_types = []
178 for lsdump_record_type in lsdump.record_types:
179 type_symbol = lsdump_record_type.unique_id
180 vtable_symbol = '_ZTV' + type_symbol[len('_ZTS'):]
181 if vtable_symbol not in vtable_symbols:
182 continue
183 record_type = AttrDict()
184 record_type.unique_id = lsdump_record_type.unique_id
185 record_type.vtable_components = lsdump_record_type.vtable_components
186 output_dump.record_types.append(record_type)
187
188
189def ParseLsdumpFile(input_path, output_path):
190 """Converts an lsdump file to a dump file for the ABI test.
191
192 Args:
193 input_path: The path to the (gzipped) lsdump file.
194 output_path: The path to the output dump file.
195
196 Raises:
197 LsdumpError if fails to create the dump file.
198 """
199 try:
200 with _OpenFileOrGzipped(input_path) as lsdump_file:
201 lsdump = json.load(lsdump_file, object_hook=AttrDict)
202 except (IOError, ValueError) as e:
203 raise LsdumpError(e)
204
205 try:
206 output_dump = AttrDict()
207 _ParseVtablesFromLsdump(lsdump, output_dump)
208 _ParseSymbolsFromLsdump(lsdump, output_dump)
209 except AttributeError as e:
210 raise LsdumpError(e)
211
212 abs_output_path = os.path.abspath(output_path)
213 abs_output_dir = os.path.dirname(abs_output_path)
214
215 try:
216 if abs_output_dir and not os.path.exists(abs_output_dir):
217 os.makedirs(abs_output_dir)
218 with open(output_path, 'wb') as output_file:
219 json.dump(output_dump, output_file,
220 indent=1, separators=(',', ':'))
221 except (IOError, OSError) as e:
222 raise LsdumpError(e)
223
224
225def main():
226 arg_parser = argparse.ArgumentParser(
227 description='This script converts an lsdump file to a dump file for '
228 'the ABI test in VTS.')
229 arg_parser.add_argument('input_path',
230 help='input lsdump file path.')
231 arg_parser.add_argument('output_path',
232 help='output dump file path.')
233 args = arg_parser.parse_args()
234
235 try:
236 ParseLsdumpFile(args.input_path, args.output_path)
237 except LsdumpError as e:
238 print(e)
239 exit(1)
240
241
242if __name__ == '__main__':
243 main()