summaryrefslogtreecommitdiffstats
path: root/libc/tools
diff options
context:
space:
mode:
authorDmitriy Ivanov <dimitry@google.com>2014-02-19 17:23:24 -0800
committerDmitriy Ivanov <dimitry@google.com>2014-02-20 11:34:08 -0800
commitadab51aefdd00b65d631f64e6e313713d7dd9564 (patch)
tree8c2c67817acae0476ecc5953d11dfbbd618ccacb /libc/tools
parent44ef37c1b445e864068cdcd885ab3d7b17079ad3 (diff)
downloadbionic-adab51aefdd00b65d631f64e6e313713d7dd9564.zip
bionic-adab51aefdd00b65d631f64e6e313713d7dd9564.tar.gz
bionic-adab51aefdd00b65d631f64e6e313713d7dd9564.tar.bz2
Added script generating additions to libgcc_compat.c from linker errors
bug: 12234455 Change-Id: Icac35237f06e75745da5a91d9c4c941d7df4f84d
Diffstat (limited to 'libc/tools')
-rwxr-xr-xlibc/tools/genlibgcc_compat.py144
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()
+