From d83e62332bdec6bc52a8e581b737c5dc8dad40e1 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Mon, 5 Feb 2018 16:09:25 -0800 Subject: [PATCH] Don't use libandroid_support for modern API levels. If we're targeting a new enough platform version, we don't actually need to cover any gaps in libc for libc++ support. In those cases, save size in the APK by avoiding libandroid_support. This is also a requirement for static executables, since using libandroid_support with a modern libc.a will result in multiple symbol definition errors. Test: ./checkbuild.py && ./run_tests.py Bug: https://github.com/android-ndk/ndk/issues/272 Change-Id: Iec548d7a75cc407d091b1a2bc632df68756c717d (cherry picked from commit cf5442c13a4cd0fe8b01e27de3850727007988f0) --- build/cmake/android.toolchain.cmake | 21 +++++++++++++++-- build/core/setup-abi.mk | 12 ++++++++++ build/tools/make_standalone_toolchain.py | 19 ++++++++------- sources/android/support/Android.mk | 26 +++++---------------- sources/cxx-stl/llvm-libc++/build.py | 14 +++++++---- tests/build/link_order/test.py | 8 +++---- tests/device/android_support/test_config.py | 4 ++++ 7 files changed, 65 insertions(+), 39 deletions(-) create mode 100644 tests/device/android_support/test_config.py diff --git a/build/cmake/android.toolchain.cmake b/build/cmake/android.toolchain.cmake index c06dbf27..1fa86b32 100644 --- a/build/cmake/android.toolchain.cmake +++ b/build/cmake/android.toolchain.cmake @@ -321,6 +321,7 @@ list(APPEND ANDROID_LINKER_FLAGS -Wl,--exclude-libs,libatomic.a) set(USE_NOSTDLIBXX TRUE) set(ANDROID_STL_STATIC_LIBRARIES) set(ANDROID_STL_SHARED_LIBRARIES) +set(ANDROID_STL_LDLIBS) if(ANDROID_STL STREQUAL system) set(USE_NOSTDLIBXX FALSE) if(NOT "x${ANDROID_CPP_FEATURES}" STREQUAL "x") @@ -338,9 +339,22 @@ elseif(ANDROID_STL STREQUAL gnustl_shared) set(ANDROID_STL_STATIC_LIBRARIES supc++) set(ANDROID_STL_SHARED_LIBRARIES gnustl_shared) elseif(ANDROID_STL STREQUAL c++_static) - set(ANDROID_STL_STATIC_LIBRARIES c++) + list(APPEND ANDROID_STL_STATIC_LIBRARIES c++_static c++abi) + if(ANDROID_PLATFORM_LEVEL LESS 21) + list(APPEND ANDROID_STL_STATIC_LIBRARIES android_support) + endif() + if(ANDROID_ABI STREQUAL armeabi-v7a) + list(APPEND ANDROID_STL_STATIC_LIBRARIES unwind) + list(APPEND ANDROID_STL_LDLIBS dl) + endif() elseif(ANDROID_STL STREQUAL c++_shared) - set(ANDROID_STL_SHARED_LIBRARIES c++) + if(ANDROID_PLATFORM_LEVEL LESS 21) + list(APPEND ANDROID_STL_STATIC_LIBRARIES android_support) + endif() + if(ANDROID_ABI STREQUAL armeabi-v7a) + list(APPEND ANDROID_STL_STATIC_LIBRARIES unwind) + endif() + list(APPEND ANDROID_STL_SHARED_LIBRARIES c++_shared) elseif(ANDROID_STL STREQUAL none) else() message(FATAL_ERROR "Invalid Android STL: ${ANDROID_STL}.") @@ -577,6 +591,9 @@ foreach(library ${ANDROID_STL_SHARED_LIBRARIES}) list(APPEND ANDROID_CXX_STANDARD_LIBRARIES "${ANDROID_NDK}/sources/cxx-stl/${ANDROID_STL_PREFIX}/libs/${ANDROID_ABI}/lib${library}.so") endforeach() +foreach(library ${ANDROID_STL_LDLIBS}) + list(APPEND ANDROID_CXX_STANDARD_LIBRARIES "-l${library}") +endforeach() set(CMAKE_C_STANDARD_LIBRARIES_INIT "-latomic -lm") set(CMAKE_CXX_STANDARD_LIBRARIES_INIT "${CMAKE_C_STANDARD_LIBRARIES_INIT}") if(ANDROID_CXX_STANDARD_LIBRARIES) diff --git a/build/core/setup-abi.mk b/build/core/setup-abi.mk index 505d00f3..773960c1 100644 --- a/build/core/setup-abi.mk +++ b/build/core/setup-abi.mk @@ -44,6 +44,18 @@ else TARGET_PIE := false endif +# If we're targeting a new enough platform version, we don't actually need to +# cover any gaps in libc for libc++ support. In those cases, save size in the +# APK by avoiding libandroid_support. +# +# This is also a requirement for static executables, since using +# libandroid_support with a modern libc.a will result in multiple symbol +# definition errors. +NDK_PLATFORM_NEEDS_ANDROID_SUPPORT := true +ifeq ($(call gte,$(TARGET_PLATFORM_LEVEL),21),$(true)) + NDK_PLATFORM_NEEDS_ANDROID_SUPPORT := false +endif + # Separate the debug and release objects. This prevents rebuilding # everything when you switch between these two modes. For projects # with lots of C++ sources, this can be a considerable time saver. diff --git a/build/tools/make_standalone_toolchain.py b/build/tools/make_standalone_toolchain.py index 149c56dd..e0f509b6 100755 --- a/build/tools/make_standalone_toolchain.py +++ b/build/tools/make_standalone_toolchain.py @@ -358,13 +358,14 @@ def copy_stlport_libs(src_dir, dst_dir, triple, abi, thumb=False): os.path.join(dst_libdir, 'libstdc++.a')) -def copy_libcxx_libs(src_dir, dst_dir, include_libunwind): +def copy_libcxx_libs(src_dir, dst_dir, abi, api): shutil.copy2(os.path.join(src_dir, 'libc++_shared.so'), dst_dir) shutil.copy2(os.path.join(src_dir, 'libc++_static.a'), dst_dir) - shutil.copy2(os.path.join(src_dir, 'libandroid_support.a'), dst_dir) + if api < 21: + shutil.copy2(os.path.join(src_dir, 'libandroid_support.a'), dst_dir) shutil.copy2(os.path.join(src_dir, 'libc++abi.a'), dst_dir) - if include_libunwind: + if abi == 'armeabi-v7a': shutil.copy2(os.path.join(src_dir, 'libunwind.a'), dst_dir) # libc++ is different from the other STLs. It has a libc++.(a|so) that is a @@ -461,11 +462,12 @@ def create_toolchain(install_path, arch, api, gcc_path, clang_path, elif stl == 'libc++': libcxx_dir = os.path.join(NDK_DIR, 'sources/cxx-stl/llvm-libc++') libcxxabi_dir = os.path.join(NDK_DIR, 'sources/cxx-stl/llvm-libc++abi') - support_dir = os.path.join(NDK_DIR, 'sources/android/support') copy_directory_contents(os.path.join(libcxx_dir, 'include'), cxx_headers) - copy_directory_contents(os.path.join(support_dir, 'include'), - support_headers) + if api < 21: + support_dir = os.path.join(NDK_DIR, 'sources/android/support') + copy_directory_contents(os.path.join(support_dir, 'include'), + support_headers) # I have no idea why we need this, but the old one does it too. copy_directory_contents( @@ -484,11 +486,10 @@ def create_toolchain(install_path, arch, api, gcc_path, clang_path, for abi in get_abis(arch): src_libdir = get_src_libdir(libcxx_dir, abi) dest_libdir = get_dest_libdir(install_path, triple, abi) - include_libunwind = arch == 'arm' - copy_libcxx_libs(src_libdir, dest_libdir, include_libunwind) + copy_libcxx_libs(src_libdir, dest_libdir, abi, api) if arch == 'arm': thumb_libdir = os.path.join(dest_libdir, 'thumb') - copy_libcxx_libs(src_libdir, thumb_libdir, include_libunwind) + copy_libcxx_libs(src_libdir, thumb_libdir, abi, api) elif stl == 'stlport': stlport_dir = os.path.join(NDK_DIR, 'sources/cxx-stl/stlport') gabixx_dir = os.path.join(NDK_DIR, 'sources/cxx-stl/gabi++') diff --git a/sources/android/support/Android.mk b/sources/android/support/Android.mk index 56315c61..d0e0060d 100644 --- a/sources/android/support/Android.mk +++ b/sources/android/support/Android.mk @@ -1,12 +1,9 @@ LOCAL_PATH := $(call my-dir) -android_support_export_c_includes := $(LOCAL_PATH)/include +# libandroid_support is only needed on LP32. +ifeq ($(filter $(NDK_KNOWN_DEVICE_ABI64S),$(TARGET_ARCH_ABI)),) -ifneq ($(filter $(NDK_KNOWN_DEVICE_ABI64S),$(TARGET_ARCH_ABI)),) - is_lp64 := true -else - is_lp64 := -endif +android_support_export_c_includes := $(LOCAL_PATH)/include ifneq ($(LIBCXX_FORCE_REBUILD),true) # Using prebuilt @@ -27,17 +24,6 @@ android_support_cflags := \ -fdata-sections \ -fvisibility=hidden \ -ifeq ($(is_lp64),true) -# 64-bit ABIs - -# We don't need this file on LP32 because libc++ has its own fallbacks for these -# functions. We can't use those fallbacks for LP64 because the file contains all -# the strto*_l functions. LP64 had some of those in L, so the inlines in libc++ -# collide with the out-of-line declarations in bionic. -android_support_sources := \ - src/locale_support.cpp \ - -else # 32-bit ABIs BIONIC_PATH := ../../../../bionic @@ -117,6 +103,7 @@ android_support_sources := \ $(BIONIC_PATH)/libm/upstream-freebsd/lib/msun/src/s_sin.c \ $(BIONIC_PATH)/libm/upstream-freebsd/lib/msun/src/s_tan.c \ $(BIONIC_PATH)/libm/upstream-freebsd/lib/msun/src/s_tanh.c \ + src/locale_support.cpp \ src/posix_memalign.cpp \ src/swprintf.cpp \ src/wcstox.cpp \ @@ -133,9 +120,6 @@ android_support_sources += \ #android_support_sources += $(BIONIC_PATH)/libm/x86/lrint.S endif -endif # 64-/32-bit ABIs - -# This is only available as a static library for now. include $(CLEAR_VARS) LOCAL_MODULE := android_support LOCAL_SRC_FILES := $(android_support_sources) @@ -152,3 +136,5 @@ LOCAL_EXPORT_C_INCLUDES := $(android_support_export_c_includes) include $(BUILD_STATIC_LIBRARY) endif # Prebuilt/building + +endif # LP32 diff --git a/sources/cxx-stl/llvm-libc++/build.py b/sources/cxx-stl/llvm-libc++/build.py index a9679a02..02ce9d0f 100755 --- a/sources/cxx-stl/llvm-libc++/build.py +++ b/sources/cxx-stl/llvm-libc++/build.py @@ -99,14 +99,16 @@ def main(args): static_lib_dir = os.path.join(obj_out, 'local', abi) install_dir = os.path.join(lib_out, abi) is_arm = abi.startswith('armeabi') + needs_android_support = abi in ('armeabi-v7a', 'x86') if is_arm: shutil.copy2( os.path.join(static_lib_dir, 'libunwind.a'), install_dir) shutil.copy2(os.path.join(static_lib_dir, 'libc++abi.a'), install_dir) - shutil.copy2( - os.path.join(static_lib_dir, 'libandroid_support.a'), install_dir) + if needs_android_support: + shutil.copy2(os.path.join( + static_lib_dir, 'libandroid_support.a'), install_dir) shutil.copy2( os.path.join(static_lib_dir, 'libc++_static.a'), install_dir) @@ -114,12 +116,16 @@ def main(args): # properly even when we're not using ndk-build. The linker will read # the script in place of the library so that we link the unwinder and # other support libraries appropriately. - static_libs = ['-lc++_static', '-lc++abi', '-landroid_support'] + static_libs = ['-lc++_static', '-lc++abi'] + if needs_android_support: + static_libs.append('-landroid_support') if is_arm: static_libs.extend(['-lunwind', '-ldl', '-latomic']) make_linker_script(os.path.join(install_dir, 'libc++.a'), static_libs) - shared_libs = ['-landroid_support'] + shared_libs = [] + if needs_android_support: + shared_libs.append('-landroid_support') if is_arm: shared_libs.extend(['-lunwind', '-latomic']) shared_libs.append('-lc++_shared') diff --git a/tests/build/link_order/test.py b/tests/build/link_order/test.py index 1f16bcf7..dabe5c2a 100644 --- a/tests/build/link_order/test.py +++ b/tests/build/link_order/test.py @@ -59,7 +59,7 @@ def find_link_args(link_line): return args -def check_link_order(link_line, abi): +def check_link_order(link_line, abi, api): """Determines if a given link command has the correct ordering. Args: @@ -72,11 +72,11 @@ def check_link_order(link_line, abi): between the expected link order and the actual link order. """ libunwind_arg = ['libunwind.a'] if abi == 'armeabi-v7a' else [] + android_support_arg = ['libandroid_support.a'] if api < 21 else [] expected = [ 'crtbegin_so.o', 'foo.o', - 'libandroid_support.a', - ] + libunwind_arg + [ + ] + android_support_arg + libunwind_arg + [ # The most important part of this test is checking that libgcc comes # *before* the shared libraries so we can be sure we're actually # getting libgcc symbols rather than getting them from some shared @@ -129,5 +129,5 @@ def run_test(ndk_path, abi, platform, _toolchain, build_flags): if link_line is None: return False, 'Did not find link line in out:\n{}'.format(out) - result, diff = check_link_order(link_line, abi) + result, diff = check_link_order(link_line, abi, platform) return result, '' if diff is None else os.linesep.join(diff) diff --git a/tests/device/android_support/test_config.py b/tests/device/android_support/test_config.py new file mode 100644 index 00000000..717dfefb --- /dev/null +++ b/tests/device/android_support/test_config.py @@ -0,0 +1,4 @@ +def build_unsupported(_abi, api, _toolchain): + if api >= 21: + return 'android-{}'.format(api) + return None