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