Make the missing symbols script more useful.
[android-sdk/platform-bionic.git] / libc / tools / check-symbols-glibc.py
1 #!/usr/bin/python
3 import glob
4 import os
5 import re
6 import subprocess
7 import sys
9 only_unwanted = False
10 if len(sys.argv) > 1:
11   if sys.argv[1] in ('-u', '--unwanted'):
12     only_unwanted = True
14 toolchain = os.environ['ANDROID_TOOLCHAIN']
15 arch = re.sub(r'.*/linux-x86/([^/]+)/.*', r'\1', toolchain)
16 if arch == 'aarch64':
17   arch = 'arm64'
19 def GetSymbolsFromTxt(txt_file):
20   symbols = set()
21   f = open(txt_file, 'r')
22   for line in f.read().splitlines():
23     symbols.add(line)
24   f.close()
25   return symbols
27 def GetSymbolsFromSo(so_file):
28   # Example readelf output:
29   #   264: 0001623c     4 FUNC    GLOBAL DEFAULT    8 cabsf
30   #   266: 00016244     4 FUNC    GLOBAL DEFAULT    8 dremf
31   #   267: 00019018     4 OBJECT  GLOBAL DEFAULT   11 __fe_dfl_env
32   #   268: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_dcmplt
34   r = re.compile(r' +\d+: [0-9a-f]+ +\d+ (I?FUNC|OBJECT) +\S+ +\S+ +\d+ (\S+)')
36   symbols = set()
38   for line in subprocess.check_output(['readelf', '--dyn-syms', '-W', so_file]).split('\n'):
39     if ' HIDDEN ' in line or ' UND ' in line:
40       continue
41     m = r.match(line)
42     if m:
43       symbol = m.group(2)
44       symbol = re.sub('@.*', '', symbol)
45       symbols.add(symbol)
47   return symbols
49 def GetSymbolsFromAndroidSo(*files):
50   symbols = set()
51   for f in files:
52     symbols = symbols | GetSymbolsFromSo('%s/system/lib64/%s' % (os.environ['ANDROID_PRODUCT_OUT'], f))
53   return symbols
55 def GetSymbolsFromSystemSo(*files):
56   symbols = set()
57   for f in files:
58     f = glob.glob('/lib/x86_64-linux-gnu/%s' % f)[-1]
59     symbols = symbols | GetSymbolsFromSo(f)
60   return symbols
62 def MangleGlibcNameToBionic(name):
63   if name in glibc_to_bionic_names:
64     return glibc_to_bionic_names[name]
65   return name
67 def GetNdkIgnored():
68   global arch
69   symbols = set()
70   files = glob.glob('%s/ndk/build/tools/unwanted-symbols/%s/*' %
71                     (os.getenv('ANDROID_BUILD_TOP'), arch))
72   for f in files:
73     symbols |= set(open(f, 'r').read().splitlines())
74   return symbols
76 glibc_to_bionic_names = {
77   '__res_init': 'res_init',
78   '__res_mkquery': 'res_mkquery',
79   '__res_query': 'res_query',
80   '__res_search': 'res_search',
81   '__xpg_basename': '__gnu_basename',
82 }
84 glibc = GetSymbolsFromSystemSo('libc.so.*', 'librt.so.*', 'libpthread.so.*', 'libresolv.so.*', 'libm.so.*', 'libutil.so.*')
85 bionic = GetSymbolsFromAndroidSo('libc.so', 'libm.so')
86 posix = GetSymbolsFromTxt(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'posix-2013.txt'))
87 ndk_ignored = GetNdkIgnored()
89 glibc = set(map(MangleGlibcNameToBionic, glibc))
91 # bionic includes various BSD symbols to ease porting other BSD-licensed code.
92 bsd_stuff = set([
93   'basename_r',
94   'dirname_r',
95   'fgetln',
96   'fpurge',
97   'funopen',
98   'gamma_r',
99   'gammaf_r',
100   'getprogname',
101   'setprogname',
102   'strlcat',
103   'strlcpy',
104   'sys_signame',
105   'wcslcat',
106   'wcslcpy'
107 ])
108 # Some symbols are part of the FORTIFY implementation.
109 FORTIFY_stuff = set([
110   '__FD_CLR_chk',
111   '__FD_ISSET_chk',
112   '__FD_SET_chk',
113   '__stack_chk_guard',
114   '__stpncpy_chk2',
115   '__strchr_chk',
116   '__strlcat_chk',
117   '__strlcpy_chk',
118   '__strlen_chk',
119   '__strncpy_chk2',
120   '__strrchr_chk',
121   '__umask_chk'
122 ])
123 # Some symbols are used to implement public macros.
124 macro_stuff = set([
125   '__assert2',
126   '__errno',
127   '__fe_dfl_env',
128   '__get_h_errno',
129   '__fpclassifyd',
130   '__isfinite',
131   '__isfinitef',
132   '__isfinitel',
133   '__isnormal',
134   '__isnormalf',
135   '__isnormall',
136   '__sF',
137   '__pthread_cleanup_pop',
138   '__pthread_cleanup_push',
139 ])
140 # bionic exposes various Linux features that glibc doesn't.
141 linux_stuff = set([
142   'getauxval',
143   'gettid',
144   'tgkill'
145 ])
146 # Some standard stuff isn't yet in the versions of glibc we're using.
147 std_stuff = set([
148   'at_quick_exit',
149   'c16rtomb',
150   'c32rtomb',
151   'mbrtoc16',
152   'mbrtoc32',
153 ])
154 # These have mangled names in glibc, with a macro taking the "obvious" name.
155 weird_stuff = set([
156   'fstat',
157   'fstat64',
158   'fstatat',
159   'fstatat64',
160   'isfinite',
161   'isfinitef',
162   'isfinitel',
163   'isnormal',
164   'isnormalf',
165   'isnormall',
166   'lstat',
167   'lstat64',
168   'mknod',
169   'mknodat',
170   'stat',
171   'stat64',
172   'optreset',
173   'sigsetjmp',
174 ])
175 # These exist in glibc, but under slightly different names (generally one extra
176 # or one fewer _). TODO: check against glibc names.
177 libresolv_stuff = set([
178   '__res_send_setqhook',
179   '__res_send_setrhook',
180   '_resolv_flush_cache_for_net',
181   '_resolv_set_nameservers_for_net',
182   'dn_expand',
183   'nsdispatch',
184 ])
185 # Implementation details we know we export (and can't get away from).
186 known = set([
187   '_ctype_',
188   '__libc_init',
189 ])
191 if not only_unwanted:
192   #print 'glibc:'
193   #for symbol in sorted(glibc):
194   #  print symbol
195   #print
197   #print 'bionic:'
198   #for symbol in sorted(bionic):
199   #  print symbol
200   #print
202   print 'in glibc (but not posix) but not bionic:'
203   for symbol in sorted((glibc - posix).difference(bionic)):
204     print symbol
205   print
207   print 'in posix (and implemented in glibc) but not bionic:'
208   for symbol in sorted((posix.intersection(glibc)).difference(bionic)):
209     print symbol
210   print
212   print 'in bionic but not glibc:'
214 allowed_stuff = (bsd_stuff | FORTIFY_stuff | linux_stuff | macro_stuff |
215                  std_stuff | weird_stuff | libresolv_stuff | known)
216 for symbol in sorted((bionic - allowed_stuff).difference(glibc)):
217   if symbol in ndk_ignored:
218     symbol += '*'
219   print symbol
221 sys.exit(0)