Dmitriy Ivanov | adab51a | 2014-02-19 17:23:24 -0800 | [diff] [blame^] | 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" + \ |
| 75 | """ |
| 76 | #define COMPAT_FUNCTIONS_LIST \\ |
| 77 | """ |
| 78 | |
| 79 | libgcc_compat_footer = """ |
| 80 | |
| 81 | #define XX(f) extern void f(void); |
| 82 | COMPAT_FUNCTIONS_LIST |
| 83 | #undef XX |
| 84 | |
| 85 | void __bionic_libgcc_compat_hooks(void) { |
| 86 | #define XX(f) f(); |
| 87 | COMPAT_FUNCTIONS_LIST |
| 88 | #undef XX |
| 89 | } |
| 90 | """ |
| 91 | |
| 92 | class Generator: |
| 93 | def process(self): |
| 94 | android_build_top_path = os.environ["ANDROID_BUILD_TOP"] |
| 95 | build_path = android_build_top_path + "/bionic/libc" |
| 96 | file_name = "libgcc_compat.c" |
| 97 | file_path = build_path + "/arch-arm/bionic/" + file_name |
| 98 | |
| 99 | print "* ANDROID_BUILD_TOP=" + android_build_top_path |
| 100 | |
| 101 | # Check TARGET_ARCH |
| 102 | arch = subprocess.check_output(["CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core make --no-print-directory -f build/core/config.mk dumpvar-TARGET_ARCH"], |
| 103 | cwd=android_build_top_path, shell=True).strip() |
| 104 | |
| 105 | if arch != 'arm': |
| 106 | sys.exit("Error: Invalid TARGET_ARCH='" + arch + "' expecting 'arm'") |
| 107 | |
| 108 | build_output_file_path = tempfile.mkstemp()[1] |
| 109 | |
| 110 | p = subprocess.Popen(["ONE_SHOT_MAKEFILE=bionic/libc/Android.mk make -C " + android_build_top_path |
| 111 | + " -f build/core/main.mk all_modules TARGET_LIBGCC= -j20 -B 2>&1 | tee " + build_output_file_path], |
| 112 | cwd=build_path, shell=True) |
| 113 | p.wait() |
| 114 | |
| 115 | print "* Build complete, logfile: " + build_output_file_path |
| 116 | |
| 117 | func_set = set() |
| 118 | prog=re.compile("(?<=undefined reference to ')\w+") |
| 119 | fd = open(build_output_file_path, 'r') |
| 120 | for line in fd: |
| 121 | m = prog.search(line) |
| 122 | if m: |
| 123 | func_set.add(m.group(0)) |
| 124 | |
| 125 | fd.close() |
| 126 | |
| 127 | func_list = sorted(func_set) |
| 128 | |
| 129 | print "* Found " + repr(len(func_list)) + " referenced functions: " + repr(func_list) |
| 130 | |
| 131 | if 0 == len(func_list): |
| 132 | sys.exit("Error: function list is empty, please check the build log: " + build_output_file_path) |
| 133 | |
| 134 | print "* Generating " + file_path |
| 135 | fres = open(file_path, 'w') |
| 136 | fres.write(libgcc_compat_header) |
| 137 | for func_name in func_list: |
| 138 | fres.write(" XX("+func_name+") \\\n") |
| 139 | fres.write(libgcc_compat_footer) |
| 140 | fres.close() |
| 141 | |
| 142 | generator = Generator() |
| 143 | generator.process() |
| 144 | |