blob: f2ff7b0285e656288983b18204983d3aabd7c2d1 [file] [log] [blame]
Dmitriy Ivanovadab51a2014-02-19 17:23:24 -08001#!/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
68import os
69import sys
70import subprocess
71import tempfile
72import re
73
74libgcc_compat_header = "/* Generated by genlibgcc_compat.py */\n" + \
75"""
76#define COMPAT_FUNCTIONS_LIST \\
77"""
78
79libgcc_compat_footer = """
80
81#define XX(f) extern void f(void);
82COMPAT_FUNCTIONS_LIST
83#undef XX
84
85void __bionic_libgcc_compat_hooks(void) {
86#define XX(f) f();
87COMPAT_FUNCTIONS_LIST
88#undef XX
89}
90"""
91
92class 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
142generator = Generator()
143generator.process()
144