Linking static libraries into component build .so libs - is this okay?

132 views
Skip to first unread message

Christopher Grant

unread,
Jul 31, 2018, 3:55:56 PM7/31/18
to Chromium-dev

I'm investigating ways to split code out of libchrome.so/libmonochrome.so on Android.  In some ways, this is similar to the component build, so I've been looking at exactly how that works.


Every component .so is linked against the components on which it depends, but also, a fixed set of additional libraries.  Some of these are static, including:


- unwind

- c++abi

- gcc


In theory, doesn't this risk C++ ODR (one definition rule) violations?  I'm wondering if the component build's approach here isn't correct, but is deemed acceptable since it's not used in production.  Ie, it's easier to include the libs in all components than to overcome the linking issues that arise from including those libraries in only one component.


torne@ took a look, and we're hoping a toolchain expert can weigh in...

Nico Weber

unread,
Jul 31, 2018, 4:13:29 PM7/31/18
to Christopher Grant, Chromium-dev
In general, this is not ok.

In some rare cases, it can work (while still technically being UB), if you're careful about visibility (all symbols from the static libs must be hidden) and state (don't share any data from the static libs across .sos).

For these specific three static libraries, it works well enough for component builds. Doing this for more libraries or in non-component builds wouldn't be a good idea though.

--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
---
You received this message because you are subscribed to the Google Groups "Chromium-dev" group.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/chromium-dev/edbea6be-2a06-4c58-8cdb-6c68cbe2672e%40chromium.org.

Primiano Tucci

unread,
Jul 31, 2018, 5:04:23 PM7/31/18
to tha...@chromium.org, Christopher Grant, Chromium-dev
Beyond libunwind/c++abi and the like, aren't you going to bundle two copies of chromium's base in two .so(s) loaded by the same executable?

If the answer is no, ignore everything else below.

If that is the case, you might still end up in an ODR problematic case, if you have some object that gets passed across the .so boundaries and is handled by some global state owned by base. To begin with every singleton in base will become a dual-thon. More subtly, if anything relies on some global state (e.g. any sort of registry in base) and you pass an object across the two .so(s) boundary, that will likely hit some check.

Another thing to keep in mind, is that the --Wl,-wrap=malloc trick that we do for trapping malloc calls will need to be re-thought or you are going to end up in some split-heap situation.
I assume that you won't be able to guarantee that memory allocated in one .so is going to be freed in the other one. In this case, if both .so do their own malloc/free wrapping, they are going to route it to their own allocator. By default nothing bad will happen, because they will both route it to the system (bionic's) allocator. However, if anybody enables some dynamic tooling via finch a-la asan that uses the shim, that might end up either misbehaving or crashing. If split .so is the long term way to go for android, we should figure out a way to make that wrapping logic work. 

Torne (Richard Coles)

unread,
Jul 31, 2018, 5:18:59 PM7/31/18
to prim...@chromium.org, tha...@chromium.org, Christopher Grant, Chromium-dev
No, there's a trick that's planned so that all the actual Chromium code is only in one library or the other. The second library will work like a plugin: it will link against the main library to provide all the symbols that have a definition in the main library. Except, currently that's not happening for the actual NDK static libraries because they're being passed into the linker with -l in both cases, since the build config does that for all linkable targets unconditionally right now.

The malloc wrapping should hopefully also work in this situation as long as both libs do actually get passed -Wl,-wrap=malloc, because the extra library will just have the special wrapped symbol undefined and get its definition from the main one.

David Turner

unread,
Aug 1, 2018, 4:27:56 AM8/1/18
to Christopher Grant, chromium-dev
Toolchain expert here :-)
  • libgcc.a is designed to be statically linked into any shared library, so there won't be any issue here. It only contains helper functions that are invoked by compiler-generated code (e.g. efficient integer division routines for 32-bit ARM binaries, since technically there is no CPU instruction to perform this in the armeabi-v7a ABI definition). Linking it statically is critical for performance of many low-level functions. None of the symbols defined here should be called directly by C or C++ source code anyway, only generated machine code. The Android platform and the Android NDK have been statically linking libgcc.a by default since the early days without any issues.

  • libc++abi contains low-level C++ runtime ABI support routines and data, mostly related to exceptions and RTTI (dynamic_cast<>) which are not used by Chromium. It has its own global state though, which normally should not be duplicated in a given process. But if you don't use exceptions, and do not try to change __cxa_new_handler or __cxa_terminate_handler, everything should be fine. However, it should probably be simpler if this was only provided by the base shared library. The only drawback would be very slightly slower calls to the __cxa_vec_ functions used to manage C++ array allocation / deallocation (but I suspect this will be nothing compared to the cost of the actual heap operations).

  • libunwind is used to unwind the stack and is now, I believe, a dependency of c++abi. I don't know if it has any global state that might create issues if it is duplicated, but I suspect it should probably only be linked into the base shared library all others depend on (probably much easier if libc++abi is only there too).
Generally speaking, you don't want to duplicate any other C/C++ library between native shared libraries unless it has been designed specifically to:
  • avoid any global state.
  • avoid comparing addresses of functions or global constants (easier said than done!).
  • avoid depending on any other library that isn't designed with the same constraints.
Proper candidates are thus very rare :)

torne@ took a look, and we're hoping a toolchain expert can weigh in...

--
--
Chromium Developers mailing list: chromi...@chromium.org
View archives, change email options, or unsubscribe:
http://groups.google.com/a/chromium.org/group/chromium-dev
---
You received this message because you are subscribed to the Google Groups "Chromium-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to chromium-dev...@chromium.org.
Reply all
Reply to author
Forward
0 new messages