diff options
author | Dmitriy Ivanov <dimitry@google.com> | 2014-02-20 19:40:08 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2014-02-20 19:40:08 +0000 |
commit | 54d49ac75b4b71f38eb154ab8e05f60b7c347a2b (patch) | |
tree | 558f9c03922871709ca0fd86adce194428103889 /libc/tools | |
parent | ca566b31036704eb6cdde05a0bd9ee972d7a995d (diff) | |
parent | adab51aefdd00b65d631f64e6e313713d7dd9564 (diff) | |
download | bionic-54d49ac75b4b71f38eb154ab8e05f60b7c347a2b.zip bionic-54d49ac75b4b71f38eb154ab8e05f60b7c347a2b.tar.gz bionic-54d49ac75b4b71f38eb154ab8e05f60b7c347a2b.tar.bz2 |
Merge "Added script generating additions to libgcc_compat.c from linker errors"
Diffstat (limited to 'libc/tools')
-rwxr-xr-x | libc/tools/genlibgcc_compat.py | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/libc/tools/genlibgcc_compat.py b/libc/tools/genlibgcc_compat.py new file mode 100755 index 0000000..f2ff7b0 --- /dev/null +++ b/libc/tools/genlibgcc_compat.py @@ -0,0 +1,144 @@ +#!/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" + \ +""" +#define COMPAT_FUNCTIONS_LIST \\ +""" + +libgcc_compat_footer = """ + +#define XX(f) extern void f(void); +COMPAT_FUNCTIONS_LIST +#undef XX + +void __bionic_libgcc_compat_hooks(void) { +#define XX(f) f(); +COMPAT_FUNCTIONS_LIST +#undef XX +} +""" + +class Generator: + def process(self): + android_build_top_path = os.environ["ANDROID_BUILD_TOP"] + build_path = android_build_top_path + "/bionic/libc" + file_name = "libgcc_compat.c" + file_path = build_path + "/arch-arm/bionic/" + file_name + + 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': + sys.exit("Error: Invalid TARGET_ARCH='" + arch + "' expecting 'arm'") + + 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 + + func_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: + func_set.add(m.group(0)) + + fd.close() + + func_list = sorted(func_set) + + print "* Found " + repr(len(func_list)) + " referenced functions: " + repr(func_list) + + if 0 == len(func_list): + sys.exit("Error: function 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 func_name in func_list: + fres.write(" XX("+func_name+") \\\n") + fres.write(libgcc_compat_footer) + fres.close() + +generator = Generator() +generator.process() + |