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