1#===----------------------------------------------------------------------===##
2#
3# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4# See https://llvm.org/LICENSE.txt for license information.
5# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6#
7#===----------------------------------------------------------------------===##
8
9import ast
10import distutils.spawn
11import sys
12import re
13import libcxx.util
14from pprint import pformat
15
16
17def read_syms_from_list(slist):
18    """
19    Read a list of symbols from a list of strings.
20    Each string is one symbol.
21    """
22    return [ast.literal_eval(l) for l in slist]
23
24
25def read_syms_from_file(filename):
26    """
27    Read a list of symbols in from a file.
28    """
29    with open(filename, 'r') as f:
30        data = f.read()
31    return read_syms_from_list(data.splitlines())
32
33
34def read_exclusions(filename):
35    with open(filename, 'r') as f:
36        data = f.read()
37    lines = [l.strip() for l in data.splitlines() if l.strip()]
38    lines = [l for l in lines if not l.startswith('#')]
39    return lines
40
41
42def write_syms(sym_list, out=None, names_only=False, filter=None):
43    """
44    Write a list of symbols to the file named by out.
45    """
46    out_str = ''
47    out_list = sym_list
48    out_list.sort(key=lambda x: x['name'])
49    if filter is not None:
50        out_list = filter(out_list)
51    if names_only:
52        out_list = [sym['name'] for sym in out_list]
53    for sym in out_list:
54        # Use pformat for consistent ordering of keys.
55        out_str += pformat(sym, width=100000) + '\n'
56    if out is None:
57        sys.stdout.write(out_str)
58    else:
59        with open(out, 'w') as f:
60            f.write(out_str)
61
62
63_cppfilt_exe = distutils.spawn.find_executable('c++filt')
64
65
66def demangle_symbol(symbol):
67    if _cppfilt_exe is None:
68        return symbol
69    out, _, exit_code = libcxx.util.executeCommandVerbose(
70        [_cppfilt_exe], input=symbol)
71    if exit_code != 0:
72        return symbol
73    return out
74
75
76def is_elf(filename):
77    with open(filename, 'rb') as f:
78        magic_bytes = f.read(4)
79    return magic_bytes == b'\x7fELF'
80
81
82def is_mach_o(filename):
83    with open(filename, 'rb') as f:
84        magic_bytes = f.read(4)
85    return magic_bytes in [
86        b'\xfe\xed\xfa\xce',  # MH_MAGIC
87        b'\xce\xfa\xed\xfe',  # MH_CIGAM
88        b'\xfe\xed\xfa\xcf',  # MH_MAGIC_64
89        b'\xcf\xfa\xed\xfe',  # MH_CIGAM_64
90        b'\xca\xfe\xba\xbe',  # FAT_MAGIC
91        b'\xbe\xba\xfe\xca'   # FAT_CIGAM
92    ]
93
94
95def is_library_file(filename):
96    if sys.platform == 'darwin':
97        return is_mach_o(filename)
98    else:
99        return is_elf(filename)
100
101
102def extract_or_load(filename):
103    import libcxx.sym_check.extract
104    if is_library_file(filename):
105        return libcxx.sym_check.extract.extract_symbols(filename)
106    return read_syms_from_file(filename)
107
108def adjust_mangled_name(name):
109    if not name.startswith('__Z'):
110        return name
111    return name[1:]
112
113new_delete_std_symbols = [
114    '_Znam',
115    '_Znwm',
116    '_ZdaPv',
117    '_ZdaPvm',
118    '_ZdlPv',
119    '_ZdlPvm'
120]
121
122cxxabi_symbols = [
123    '___dynamic_cast',
124    '___gxx_personality_v0',
125    '_ZTIDi',
126    '_ZTIDn',
127    '_ZTIDs',
128    '_ZTIPDi',
129    '_ZTIPDn',
130    '_ZTIPDs',
131    '_ZTIPKDi',
132    '_ZTIPKDn',
133    '_ZTIPKDs',
134    '_ZTIPKa',
135    '_ZTIPKb',
136    '_ZTIPKc',
137    '_ZTIPKd',
138    '_ZTIPKe',
139    '_ZTIPKf',
140    '_ZTIPKh',
141    '_ZTIPKi',
142    '_ZTIPKj',
143    '_ZTIPKl',
144    '_ZTIPKm',
145    '_ZTIPKs',
146    '_ZTIPKt',
147    '_ZTIPKv',
148    '_ZTIPKw',
149    '_ZTIPKx',
150    '_ZTIPKy',
151    '_ZTIPa',
152    '_ZTIPb',
153    '_ZTIPc',
154    '_ZTIPd',
155    '_ZTIPe',
156    '_ZTIPf',
157    '_ZTIPh',
158    '_ZTIPi',
159    '_ZTIPj',
160    '_ZTIPl',
161    '_ZTIPm',
162    '_ZTIPs',
163    '_ZTIPt',
164    '_ZTIPv',
165    '_ZTIPw',
166    '_ZTIPx',
167    '_ZTIPy',
168    '_ZTIa',
169    '_ZTIb',
170    '_ZTIc',
171    '_ZTId',
172    '_ZTIe',
173    '_ZTIf',
174    '_ZTIh',
175    '_ZTIi',
176    '_ZTIj',
177    '_ZTIl',
178    '_ZTIm',
179    '_ZTIs',
180    '_ZTIt',
181    '_ZTIv',
182    '_ZTIw',
183    '_ZTIx',
184    '_ZTIy',
185    '_ZTSDi',
186    '_ZTSDn',
187    '_ZTSDs',
188    '_ZTSPDi',
189    '_ZTSPDn',
190    '_ZTSPDs',
191    '_ZTSPKDi',
192    '_ZTSPKDn',
193    '_ZTSPKDs',
194    '_ZTSPKa',
195    '_ZTSPKb',
196    '_ZTSPKc',
197    '_ZTSPKd',
198    '_ZTSPKe',
199    '_ZTSPKf',
200    '_ZTSPKh',
201    '_ZTSPKi',
202    '_ZTSPKj',
203    '_ZTSPKl',
204    '_ZTSPKm',
205    '_ZTSPKs',
206    '_ZTSPKt',
207    '_ZTSPKv',
208    '_ZTSPKw',
209    '_ZTSPKx',
210    '_ZTSPKy',
211    '_ZTSPa',
212    '_ZTSPb',
213    '_ZTSPc',
214    '_ZTSPd',
215    '_ZTSPe',
216    '_ZTSPf',
217    '_ZTSPh',
218    '_ZTSPi',
219    '_ZTSPj',
220    '_ZTSPl',
221    '_ZTSPm',
222    '_ZTSPs',
223    '_ZTSPt',
224    '_ZTSPv',
225    '_ZTSPw',
226    '_ZTSPx',
227    '_ZTSPy',
228    '_ZTSa',
229    '_ZTSb',
230    '_ZTSc',
231    '_ZTSd',
232    '_ZTSe',
233    '_ZTSf',
234    '_ZTSh',
235    '_ZTSi',
236    '_ZTSj',
237    '_ZTSl',
238    '_ZTSm',
239    '_ZTSs',
240    '_ZTSt',
241    '_ZTSv',
242    '_ZTSw',
243    '_ZTSx',
244    '_ZTSy'
245]
246
247def is_stdlib_symbol_name(name, sym):
248    name = adjust_mangled_name(name)
249    if re.search("@GLIBC|@GCC", name):
250        # Only when symbol is defined do we consider it ours
251        return sym['is_defined']
252    if re.search('(St[0-9])|(__cxa)|(__cxxabi)', name):
253        return True
254    if name in new_delete_std_symbols:
255        return True
256    if name in cxxabi_symbols:
257        return True
258    if name.startswith('_Z'):
259        return True
260    return False
261
262def filter_stdlib_symbols(syms):
263    stdlib_symbols = []
264    other_symbols = []
265    for s in syms:
266        canon_name = adjust_mangled_name(s['name'])
267        if not is_stdlib_symbol_name(canon_name, s):
268            other_symbols += [s]
269        else:
270            stdlib_symbols += [s]
271    return stdlib_symbols, other_symbols
272