|  | #!/usr/bin/python | 
|  |  | 
|  | import glob | 
|  | import os | 
|  | import re | 
|  | import subprocess | 
|  | import sys | 
|  |  | 
|  | only_unwanted = False | 
|  | if len(sys.argv) > 1: | 
|  | if sys.argv[1] in ('-u', '--unwanted'): | 
|  | only_unwanted = True | 
|  |  | 
|  | toolchain = os.environ['ANDROID_TOOLCHAIN'] | 
|  | arch = re.sub(r'.*/linux-x86/([^/]+)/.*', r'\1', toolchain) | 
|  | if arch == 'aarch64': | 
|  | arch = 'arm64' | 
|  |  | 
|  | def GetSymbolsFromTxt(txt_file): | 
|  | symbols = set() | 
|  | f = open(txt_file, 'r') | 
|  | for line in f.read().splitlines(): | 
|  | symbols.add(line) | 
|  | f.close() | 
|  | return symbols | 
|  |  | 
|  | def GetSymbolsFromSo(so_file): | 
|  | # Example readelf output: | 
|  | #   264: 0001623c     4 FUNC    GLOBAL DEFAULT    8 cabsf | 
|  | #   266: 00016244     4 FUNC    GLOBAL DEFAULT    8 dremf | 
|  | #   267: 00019018     4 OBJECT  GLOBAL DEFAULT   11 __fe_dfl_env | 
|  | #   268: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_dcmplt | 
|  |  | 
|  | r = re.compile(r' +\d+: [0-9a-f]+ +\d+ (I?FUNC|OBJECT) +\S+ +\S+ +\d+ (\S+)') | 
|  |  | 
|  | symbols = set() | 
|  |  | 
|  | for line in subprocess.check_output(['readelf', '--dyn-syms', '-W', so_file]).split('\n'): | 
|  | if ' HIDDEN ' in line or ' UND ' in line: | 
|  | continue | 
|  | m = r.match(line) | 
|  | if m: | 
|  | symbol = m.group(2) | 
|  | symbol = re.sub('@.*', '', symbol) | 
|  | symbols.add(symbol) | 
|  |  | 
|  | return symbols | 
|  |  | 
|  | def GetSymbolsFromAndroidSo(*files): | 
|  | symbols = set() | 
|  | for f in files: | 
|  | symbols = symbols | GetSymbolsFromSo('%s/system/lib64/%s' % (os.environ['ANDROID_PRODUCT_OUT'], f)) | 
|  | return symbols | 
|  |  | 
|  | def GetSymbolsFromSystemSo(*files): | 
|  | symbols = set() | 
|  | for f in files: | 
|  | f = glob.glob('/lib/x86_64-linux-gnu/%s' % f)[-1] | 
|  | symbols = symbols | GetSymbolsFromSo(f) | 
|  | return symbols | 
|  |  | 
|  | def MangleGlibcNameToBionic(name): | 
|  | if name in glibc_to_bionic_names: | 
|  | return glibc_to_bionic_names[name] | 
|  | return name | 
|  |  | 
|  | def GetNdkIgnored(): | 
|  | global arch | 
|  | symbols = set() | 
|  | files = glob.glob('%s/ndk/build/tools/unwanted-symbols/%s/*' % | 
|  | (os.getenv('ANDROID_BUILD_TOP'), arch)) | 
|  | for f in files: | 
|  | symbols |= set(open(f, 'r').read().splitlines()) | 
|  | return symbols | 
|  |  | 
|  | glibc_to_bionic_names = { | 
|  | '__res_init': 'res_init', | 
|  | '__res_mkquery': 'res_mkquery', | 
|  | '__res_query': 'res_query', | 
|  | '__res_search': 'res_search', | 
|  | '__xpg_basename': '__gnu_basename', | 
|  | } | 
|  |  | 
|  | glibc = GetSymbolsFromSystemSo('libc.so.*', 'librt.so.*', 'libpthread.so.*', 'libresolv.so.*', 'libm.so.*', 'libutil.so.*') | 
|  | bionic = GetSymbolsFromAndroidSo('libc.so', 'libm.so') | 
|  | posix = GetSymbolsFromTxt(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'posix-2013.txt')) | 
|  | ndk_ignored = GetNdkIgnored() | 
|  |  | 
|  | glibc = set(map(MangleGlibcNameToBionic, glibc)) | 
|  |  | 
|  | # bionic includes various BSD symbols to ease porting other BSD-licensed code. | 
|  | bsd_stuff = set([ | 
|  | 'basename_r', | 
|  | 'dirname_r', | 
|  | 'fgetln', | 
|  | 'fpurge', | 
|  | 'funopen', | 
|  | 'gamma_r', | 
|  | 'gammaf_r', | 
|  | 'getprogname', | 
|  | 'setprogname', | 
|  | 'strlcat', | 
|  | 'strlcpy', | 
|  | 'sys_signame', | 
|  | 'wcslcat', | 
|  | 'wcslcpy' | 
|  | ]) | 
|  | # Some symbols are part of the FORTIFY implementation. | 
|  | FORTIFY_stuff = set([ | 
|  | '__FD_CLR_chk', | 
|  | '__FD_ISSET_chk', | 
|  | '__FD_SET_chk', | 
|  | '__stack_chk_guard', | 
|  | '__stpncpy_chk2', | 
|  | '__strchr_chk', | 
|  | '__strlcat_chk', | 
|  | '__strlcpy_chk', | 
|  | '__strlen_chk', | 
|  | '__strncpy_chk2', | 
|  | '__strrchr_chk', | 
|  | '__umask_chk' | 
|  | ]) | 
|  | # Some symbols are used to implement public macros. | 
|  | macro_stuff = set([ | 
|  | '__assert2', | 
|  | '__errno', | 
|  | '__fe_dfl_env', | 
|  | '__get_h_errno', | 
|  | '__fpclassifyd', | 
|  | '__isfinite', | 
|  | '__isfinitef', | 
|  | '__isfinitel', | 
|  | '__isnormal', | 
|  | '__isnormalf', | 
|  | '__isnormall', | 
|  | '__sF', | 
|  | '__pthread_cleanup_pop', | 
|  | '__pthread_cleanup_push', | 
|  | ]) | 
|  | # bionic exposes various Linux features that glibc doesn't. | 
|  | linux_stuff = set([ | 
|  | 'getauxval', | 
|  | 'gettid', | 
|  | 'tgkill' | 
|  | ]) | 
|  | # Some standard stuff isn't yet in the versions of glibc we're using. | 
|  | std_stuff = set([ | 
|  | 'at_quick_exit', | 
|  | 'c16rtomb', | 
|  | 'c32rtomb', | 
|  | 'mbrtoc16', | 
|  | 'mbrtoc32', | 
|  | ]) | 
|  | # These have mangled names in glibc, with a macro taking the "obvious" name. | 
|  | weird_stuff = set([ | 
|  | 'fstat', | 
|  | 'fstat64', | 
|  | 'fstatat', | 
|  | 'fstatat64', | 
|  | 'isfinite', | 
|  | 'isfinitef', | 
|  | 'isfinitel', | 
|  | 'isnormal', | 
|  | 'isnormalf', | 
|  | 'isnormall', | 
|  | 'lstat', | 
|  | 'lstat64', | 
|  | 'mknod', | 
|  | 'mknodat', | 
|  | 'stat', | 
|  | 'stat64', | 
|  | 'optreset', | 
|  | 'sigsetjmp', | 
|  | ]) | 
|  | # These exist in glibc, but under slightly different names (generally one extra | 
|  | # or one fewer _). TODO: check against glibc names. | 
|  | libresolv_stuff = set([ | 
|  | '__res_send_setqhook', | 
|  | '__res_send_setrhook', | 
|  | '_resolv_flush_cache_for_net', | 
|  | '_resolv_set_nameservers_for_net', | 
|  | 'dn_expand', | 
|  | 'nsdispatch', | 
|  | ]) | 
|  | # Implementation details we know we export (and can't get away from). | 
|  | known = set([ | 
|  | '_ctype_', | 
|  | '__libc_init', | 
|  | ]) | 
|  |  | 
|  | if not only_unwanted: | 
|  | #print 'glibc:' | 
|  | #for symbol in sorted(glibc): | 
|  | #  print symbol | 
|  | #print | 
|  |  | 
|  | #print 'bionic:' | 
|  | #for symbol in sorted(bionic): | 
|  | #  print symbol | 
|  | #print | 
|  |  | 
|  | print 'in glibc (but not posix) but not bionic:' | 
|  | for symbol in sorted((glibc - posix).difference(bionic)): | 
|  | print symbol | 
|  | print | 
|  |  | 
|  | print 'in posix (and implemented in glibc) but not bionic:' | 
|  | for symbol in sorted((posix.intersection(glibc)).difference(bionic)): | 
|  | print symbol | 
|  | print | 
|  |  | 
|  | print 'in bionic but not glibc:' | 
|  |  | 
|  | allowed_stuff = (bsd_stuff | FORTIFY_stuff | linux_stuff | macro_stuff | | 
|  | std_stuff | weird_stuff | libresolv_stuff | known) | 
|  | for symbol in sorted((bionic - allowed_stuff).difference(glibc)): | 
|  | if symbol in ndk_ignored: | 
|  | symbol += '*' | 
|  | print symbol | 
|  |  | 
|  | sys.exit(0) |