1 #!/usr/bin/python 2 3 ''' 4 /* This file generates libgcc_compat.c file that contains dummy 5 * references to libgcc.a functions to force the dynamic linker 6 * to copy their definition into the final libc.so binary. 7 * 8 * They are required to ensure backwards binary compatibility with 9 * libc.so provided by the platform and binaries built with the NDK or 10 * different versions/configurations of toolchains. 11 * 12 * Now, for a more elaborate description of the issue: 13 * 14 * libgcc.a is a compiler-specific library containing various helper 15 * functions used to implement certain operations that are not necessarily 16 * supported by the target CPU. For example, integer division doesn't have a 17 * corresponding CPU instruction on ARMv5, and is instead implemented in the 18 * compiler-generated machine code as a call to an __idiv helper function. 19 * 20 * Normally, one has to place libgcc.a in the link command used to generate 21 * target binaries (shared libraries and executables) after all objects and 22 * static libraries, but before dependent shared libraries, i.e. something 23 * like: 24 * gcc <options> -o libfoo.so foo.a libgcc.a -lc -lm 25 * 26 * This ensures that any helper function needed by the code in foo.a is copied 27 * into the final libfoo.so. However, doing so will link a bunch of other __cxa 28 * functions from libgcc.a into each .so and executable, causing 4k+ increase 29 * in every binary. Therefore the Android platform build system has been 30 * using this instead: 31 * 32 * gcc <options> -o libfoo.so foo.a -lc -lm libgcc.a 33 * 34 * The problem with this is that if one helper function needed by foo.a has 35 * already been copied into libc.so or libm.so, then nothing will be copied 36 * into libfoo.so. Instead, a symbol import definition will be added to it 37 * so libfoo.so can directly call the one in libc.so at runtime. 38 * 39 * When refreshing toolchains for new versions or using different architecture 40 * flags, the set of helper functions copied to libc.so may change, which 41 * resulted in some native shared libraries generated with the NDK or prebuilts 42 * from vendors to fail to load properly. 43 * 44 * The NDK has been fixed after 1.6_r1 to use the correct link command, so 45 * any native shared library generated with it should now be safe from that 46 * problem. On the other hand, existing shared libraries distributed with 47 * applications that were generated with a previous version of the NDK 48 * still need all 1.5/1.6 helper functions in libc.so and libm.so 49 * 50 * After 3.2, the toolchain was updated again, adding __aeabi_f2uiz to the 51 * list of requirements. Technically, this is due to mis-linked NDK libraries 52 * but it is easier to add a single function here than asking several app 53 * developers to fix their build. 54 * 55 * The __aeabi_idiv function is added to the list since cortex-a15 supports 56 * HW idiv instructions so the system libc.so doesn't pull in the reference to 57 * __aeabi_idiv but legacy libraries built against cortex-a9 targets still need 58 * it. 59 * 60 * Final note: some of the functions below should really be in libm.so to 61 * completely reflect the state of 1.5/1.6 system images. However, 62 * since libm.so depends on libc.so, it's easier to put all of 63 * these in libc.so instead, since the dynamic linker will always 64 * search in libc.so before libm.so for dependencies. 65 */ 66 ''' 67 68 import os 69 import sys 70 import subprocess 71 import tempfile 72 import re 73 74 libgcc_compat_header = "/* Generated by genlibgcc_compat.py */\n\n" 75 76 class Generator: 77 def process(self): 78 android_build_top_path = os.environ["ANDROID_BUILD_TOP"] 79 80 print "* ANDROID_BUILD_TOP=" + android_build_top_path 81 82 # Check TARGET_ARCH 83 arch = subprocess.check_output(["CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core make --no-print-directory -f build/core/config.mk dumpvar-TARGET_ARCH"], 84 cwd=android_build_top_path, shell=True).strip() 85 86 if arch != 'arm' and arch != 'x86': 87 sys.exit("Error: Invalid TARGET_ARCH='" + arch + "' expecting 'arm' or 'x86'") 88 89 build_path = android_build_top_path + "/bionic/libc" 90 file_name = "libgcc_compat.c" 91 file_path = build_path + "/arch-" + arch + "/bionic/" + file_name 92 93 build_output_file_path = tempfile.mkstemp()[1] 94 95 p = subprocess.Popen(["ONE_SHOT_MAKEFILE=bionic/libc/Android.mk make -C " + android_build_top_path 96 + " -f build/core/main.mk all_modules TARGET_LIBGCC= -j20 -B 2>&1 | tee " + build_output_file_path], 97 cwd=build_path, shell=True) 98 p.wait() 99 100 print "* Build complete, logfile: " + build_output_file_path 101 102 symbol_set = set() 103 prog=re.compile("(?<=undefined reference to ')\w+") 104 fd = open(build_output_file_path, 'r') 105 for line in fd: 106 m = prog.search(line) 107 if m: 108 symbol_set.add(m.group(0)) 109 110 fd.close() 111 112 symbol_list = sorted(symbol_set) 113 114 print "* Found " + repr(len(symbol_list)) + " referenced symbols: " + repr(symbol_list) 115 116 if 0 == len(symbol_list): 117 sys.exit("Error: symbol list is empty, please check the build log: " + build_output_file_path) 118 119 print "* Generating " + file_path 120 fres = open(file_path, 'w') 121 fres.write(libgcc_compat_header) 122 123 for sym_name in symbol_list: 124 fres.write("extern char "+sym_name+";\n") 125 fres.write("\n"); 126 127 fres.write("void* __bionic_libgcc_compat_symbols[] = {\n"); 128 for sym_name in symbol_list: 129 fres.write(" &"+sym_name+",\n") 130 fres.write("};\n"); 131 132 fres.close() 133 134 generator = Generator() 135 generator.process() 136 137