Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

r11: libc++_static.a contains symbols in libc.a (causes multiple definition error) #17

Closed
chenxiaolong opened this issue Mar 15, 2016 · 18 comments
Assignees
Milestone

Comments

@chenxiaolong
Copy link

The libc++_static.a static library contains (at least) the following symbols which are defined in libc.a:

  • isxdigit
  • vfprintf
  • iswalpha
  • towlower
  • towupper
  • __hdtoa

This makes it impossible to create a statically linked executable that uses libc++. (I realize this is not officially supported, but we don't have this problem with the bundled libstdc++ library.)

Also, the static libc++ library depends on libdl, which is available only as a shared library.

Is there any chance we could see this fixed for the next release? Thanks in advance!


Here's a very simple project for testing:

Application.mk:

APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
APP_PLATFORM := android-24
APP_STL := c++_static
APP_CPPFLAGS := -std=c++11
NDK_TOOLCHAIN_VERSION := clang3.6

Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_SRC_FILES := main.cpp
LOCAL_MODULE := test
LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE)

main.cpp:

#include <string>

#include <cstdio>
#include <cstdlib>

int main(int argc, char *argv[])
{
    std::string blah("Hello world!");
    std::printf("%s\n", blah.c_str());
    return EXIT_SUCCESS;
}

To compile:

ndk-build NDK_PROJECT_PATH=. NDK_APPLICATION_MK=Application.mk APP_BUILD_SCRIPT=Android.mk
@DanAlbert DanAlbert added this to the r12 milestone Mar 15, 2016
@DanAlbert
Copy link
Member

This makes it impossible to create a statically linked executable that uses libc++. (I realize this is not officially supported

Do our docs say that somewhere? There's no reason we shouldn't support that.

Is there any chance we could see this fixed for the next release?

It will take some rearranging at the very least (the problem here is actually libandroid_support.a), but it might be doable.

@chenxiaolong
Copy link
Author

Thanks for the reply, @DanAlbert!

Do our docs say that somewhere? There's no reason we shouldn't support that.

That is my misunderstanding. I assumed it wasn't supported because in AOSP, static executables are created by specifying LOCAL_FORCE_STATIC_EXECUTABLE := true in Android.mk, whereas in the NDK, we have to use LOCAL_LDFLAGS := -static (which could be considered a workaround).

EDIT: Typo: should -> could

@DanAlbert
Copy link
Member

Nah, static executables definitely have their uses, so we should support them.

@DanAlbert DanAlbert self-assigned this Mar 24, 2016
@DanAlbert DanAlbert modified the milestones: r12, r13 May 24, 2016
@DanAlbert DanAlbert modified the milestones: r14, r13 Aug 9, 2016
@DanAlbert
Copy link
Member

Sorry to keep punting this. Been working on some of the prerequisites for this but it's not going to make r13.

@Zingam
Copy link

Zingam commented Nov 12, 2016

What are the unified headers I've seen mentioned a few times? Will libc++ be default in r14 already?

@enh
Copy link
Contributor

enh commented Nov 12, 2016

(unified headers bug: #120
NDK roadmap: https://android.googlesource.com/platform/ndk.git/+/master/docs/Roadmap.md)

@DanAlbert DanAlbert modified the milestones: r15, r14 Nov 17, 2016
@DanAlbert
Copy link
Member

libandroid_support is going to be the focus of r15, so this will be fixed as part of that effort.

@DanAlbert
Copy link
Member

Hmm. I'm actually not sure there's anything that can be done about this. libandroid_support by design provides symbols to override libc, so we can't remove them or make them weak. I think the only option is telling the linker to allow multiple definitions when using libandroid_support (probably via libc++) for a static executable, but that's probably not something we should be doing by default.

@chenxiaolong
Copy link
Author

chenxiaolong commented Mar 24, 2017

@DanAlbert Thanks for the reply! I agree that a hack like that probably shouldn't be enabled by default. However, if I wanted to enable it for a specific project, is it as simple as adding some LDFLAGS?

EDIT: One other question: is libc++ shipped with the NDK the same as the AOSP version? If so, how are static executables, like /init built?

@DanAlbert
Copy link
Member

However, if I wanted to enable it for a specific project, is it as simple as adding some LDFLAGS?

Should be as simple as LOCAL_LDFLAGS += -Wl,--allow-multiple-definition.

EDIT: One other question: is libc++ shipped with the NDK the same as the AOSP version? If so, how are static executables, like /init built?

Kind of. The libraries themselves are very similar, but we don't use libandroid_support in AOSP. libandroid_support is the library the provides all the things libc++ needs that were missing from Android until recently (most of the missing pieces were added in L, but a recent libc++ update actually required more so I think you don't have everything you need until you're targeting android-o). Since in AOSP we can just fix bionic to have the missing pieces, we don't need libandroid_support. In the NDK, where you want to run on older devices instead of only the absolute newest (unreleased) OS, you need libandroid_support.

@chenxiaolong
Copy link
Author

Should be as simple as LOCAL_LDFLAGS += -Wl,--allow-multiple-definition.

Thanks. This should be simple enough to add to my project.

Kind of. The libraries themselves are very similar, but we don't use libandroid_support in AOSP. libandroid_support is the library the provides all the things libc++ needs that were missing from Android until recently (most of the missing pieces were added in L, but a recent libc++ update actually required more so I think you don't have everything you need until you're targeting android-o). Since in AOSP we can just fix bionic to have the missing pieces, we don't need libandroid_support. In the NDK, where you want to run on older devices instead of only the absolute newest (unreleased) OS, you need libandroid_support.

That's very interesting. I'm curious as to why libc.a couldn't include all the newest changes. Wouldn't the kernel version be the only limiting factor when running on older versions of Android (eg. for newer syscalls)? If I target the android-24 NDK platform, for example, and link against libc.a and libc++.a, why would libandroid_support be necessary?

@DanAlbert
Copy link
Member

That's very interesting. I'm curious as to why libc.a couldn't include all the newest changes.

It could and in fact should, but that's actually a different issue. I've been needing to find some time to fix the AOSP build system to actually package those artifacts so we can ship them in the NDK again (you'll notice that we haven't shipped one for any recent API levels). Once we have an up to date libc.a in the NDK, you'd be able to use that with libc++_static.a and avoid libandroid_support entirely (though telling ndk-build to do that isn't intuitive, it is possible, essentially APP_STL := none and use LOCAL_STATIC_LIBRARIES instead).

The kernel version might be an issue depending on what you're doing, but that won't be a build time issue. If something isn't available you'll get ENOSYS, but that's something that could happen even on a bleeding edge device.

@chenxiaolong
Copy link
Author

That all makes sense. Thanks again for the explanation.

@DanAlbert DanAlbert added this to the r17 milestone Mar 6, 2018
@DanAlbert
Copy link
Member

FYI, despite the previous wontfix this actually ended up getting fixed in r17. In r17 libandroid_support is not used when you're targeting android-21 and above, which is fine for static executables (aside from netd being broken, but that's the norm for static executables for every release except the same one they were built against).

@chenxiaolong
Copy link
Author

@DanAlbert I may be misunderstanding how things are working, but it seems that even in r17, libc++.a is unconditionally pulling in libandroid_support.a. Is this intended?

[armeabi-v7a] cat libc++.a
INPUT(-lc++_static -lc++abi -landroid_support -lunwind -ldl -latomic)

@rprichard
Copy link
Collaborator

That was unintentional: #672

ndk-build and android.toolchain.cmake don't use the libc++.a or libc++.so linker scripts. They link in the actual libraries directly, and they omit libandroid_support.a for 21 and up. In r17, the make_standalone_toolchain.py script strips out -landroid_support if the --api option is 21 or up.

In r18, the two linker scripts in sources/cxx-stl/llvm-libc++/libs/ABI are replaced by per-API linker scripts, so libc++.a.19 has -landroid_support but libc++.a.21 doesn't.

@chenxiaolong
Copy link
Author

@rprichard Thanks! That's exactly the info I was looking for.

Looks like I need to figure out why my CMake build is including libc++.a instead of libc++_static.a and libc++abi.a.

@chenxiaolong
Copy link
Author

Yep, my issue was completely my fault. I had a static library that provided a dummy dladdr() function (required by libunwind on armeabi-v7a) since in older versions of the NDK, there wasn't a libdl.a. I was adding it to CMAKE_CXX_STANDARD_LIBRARIES with:

string(APPEND CMAKE_CXX_STANDARD_LIBRARIES " ${MBP_LIBCXX_HACK_PATH}")

Apparently, when I do this, the variable gets set to include libc++.a and ignores the libc++_static.a and libc++abi.a that was in CMAKE_CXX_STANDARD_LIBRARIES_INIT. Not sure what CMake magic did this.

Anyway, since r17 includes libdl.a, I removed the hack and everything seems to be fine now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants