| #!/usr/bin/python |
| |
| ''' |
| /* This file generates libgcc_compat.c file that contains dummy |
| * references to libgcc.a functions to force the dynamic linker |
| * to copy their definition into the final libc.so binary. |
| * |
| * They are required to ensure backwards binary compatibility with |
| * libc.so provided by the platform and binaries built with the NDK or |
| * different versions/configurations of toolchains. |
| * |
| * Now, for a more elaborate description of the issue: |
| * |
| * libgcc.a is a compiler-specific library containing various helper |
| * functions used to implement certain operations that are not necessarily |
| * supported by the target CPU. For example, integer division doesn't have a |
| * corresponding CPU instruction on ARMv5, and is instead implemented in the |
| * compiler-generated machine code as a call to an __idiv helper function. |
| * |
| * Normally, one has to place libgcc.a in the link command used to generate |
| * target binaries (shared libraries and executables) after all objects and |
| * static libraries, but before dependent shared libraries, i.e. something |
| * like: |
| * gcc <options> -o libfoo.so foo.a libgcc.a -lc -lm |
| * |
| * This ensures that any helper function needed by the code in foo.a is copied |
| * into the final libfoo.so. However, doing so will link a bunch of other __cxa |
| * functions from libgcc.a into each .so and executable, causing 4k+ increase |
| * in every binary. Therefore the Android platform build system has been |
| * using this instead: |
| * |
| * gcc <options> -o libfoo.so foo.a -lc -lm libgcc.a |
| * |
| * The problem with this is that if one helper function needed by foo.a has |
| * already been copied into libc.so or libm.so, then nothing will be copied |
| * into libfoo.so. Instead, a symbol import definition will be added to it |
| * so libfoo.so can directly call the one in libc.so at runtime. |
| * |
| * When refreshing toolchains for new versions or using different architecture |
| * flags, the set of helper functions copied to libc.so may change, which |
| * resulted in some native shared libraries generated with the NDK or prebuilts |
| * from vendors to fail to load properly. |
| * |
| * The NDK has been fixed after 1.6_r1 to use the correct link command, so |
| * any native shared library generated with it should now be safe from that |
| * problem. On the other hand, existing shared libraries distributed with |
| * applications that were generated with a previous version of the NDK |
| * still need all 1.5/1.6 helper functions in libc.so and libm.so |
| * |
| * After 3.2, the toolchain was updated again, adding __aeabi_f2uiz to the |
| * list of requirements. Technically, this is due to mis-linked NDK libraries |
| * but it is easier to add a single function here than asking several app |
| * developers to fix their build. |
| * |
| * The __aeabi_idiv function is added to the list since cortex-a15 supports |
| * HW idiv instructions so the system libc.so doesn't pull in the reference to |
| * __aeabi_idiv but legacy libraries built against cortex-a9 targets still need |
| * it. |
| * |
| * Final note: some of the functions below should really be in libm.so to |
| * completely reflect the state of 1.5/1.6 system images. However, |
| * since libm.so depends on libc.so, it's easier to put all of |
| * these in libc.so instead, since the dynamic linker will always |
| * search in libc.so before libm.so for dependencies. |
| */ |
| ''' |
| |
| import os |
| import sys |
| import subprocess |
| import tempfile |
| import re |
| |
| libgcc_compat_header = "/* Generated by genlibgcc_compat.py */\n\n" |
| |
| class Generator: |
| def process(self): |
| android_build_top_path = os.environ["ANDROID_BUILD_TOP"] |
| |
| print "* ANDROID_BUILD_TOP=" + android_build_top_path |
| |
| # Check TARGET_ARCH |
| arch = subprocess.check_output(["CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core make --no-print-directory -f build/core/config.mk dumpvar-TARGET_ARCH"], |
| cwd=android_build_top_path, shell=True).strip() |
| |
| if arch != 'arm' and arch != 'x86': |
| sys.exit("Error: Invalid TARGET_ARCH='" + arch + "' expecting 'arm' or 'x86'") |
| |
| build_path = android_build_top_path + "/bionic/libc" |
| file_name = "libgcc_compat.c" |
| file_path = build_path + "/arch-" + arch + "/bionic/" + file_name |
| |
| build_output_file_path = tempfile.mkstemp()[1] |
| |
| p = subprocess.Popen(["ONE_SHOT_MAKEFILE=bionic/libc/Android.mk make -C " + android_build_top_path |
| + " -f build/core/main.mk all_modules TARGET_LIBGCC= -j20 -B 2>&1 | tee " + build_output_file_path], |
| cwd=build_path, shell=True) |
| p.wait() |
| |
| print "* Build complete, logfile: " + build_output_file_path |
| |
| symbol_set = set() |
| prog=re.compile("(?<=undefined reference to ')\w+") |
| fd = open(build_output_file_path, 'r') |
| for line in fd: |
| m = prog.search(line) |
| if m: |
| symbol_set.add(m.group(0)) |
| |
| fd.close() |
| |
| symbol_list = sorted(symbol_set) |
| |
| print "* Found " + repr(len(symbol_list)) + " referenced symbols: " + repr(symbol_list) |
| |
| if 0 == len(symbol_list): |
| sys.exit("Error: symbol list is empty, please check the build log: " + build_output_file_path) |
| |
| print "* Generating " + file_path |
| fres = open(file_path, 'w') |
| fres.write(libgcc_compat_header) |
| |
| for sym_name in symbol_list: |
| fres.write("extern char "+sym_name+";\n") |
| fres.write("\n"); |
| |
| fres.write("void* __bionic_libgcc_compat_symbols[] = {\n"); |
| for sym_name in symbol_list: |
| fres.write(" &"+sym_name+",\n") |
| fres.write("};\n"); |
| |
| fres.close() |
| |
| generator = Generator() |
| generator.process() |
| |