3

I have some pre-compiled so files, so who knows what flags were used to generate them. I'm curious to know which runtime/version of the STL they were compiled with (gnustl_shared? stlport_shared?) to avoid clashes with my own code.

How can I find out from the compiled so files which runtime they were compiled to use?

This is all Android NDK C++ code.

Andy Krouwel
  • 1,309
  • 11
  • 21

2 Answers2

5

Like Alex said, you can use readelf to figure out some of this. The following can let you know if the library depends on a shared STL, and which one:

$ readelf -dW path/to/libfoo.so | grep NEEDED
 0x00000001 (NEEDED)                     Shared library: [libc++_shared.so]
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]
 0x00000001 (NEEDED)                     Shared library: [libstdc++.so]
 0x00000001 (NEEDED)                     Shared library: [libdl.so]

As you can see, this library depends on libc++_shared. Ignore the libstdc++.so that you also see. That's https://github.com/android-ndk/ndk/issues/105.

If it depends on a static STL (or your library is a static library) and wasn't built with -fvisibility=hidden, that method won't work. In this case you can still identify if the library was built against libc++ or not-libc++. All the libc++ symbols are in an internal namespace (std::__ndk1 instead of just std) to support versioning the STL and avoiding clashes with the system STL. If it's a very old (r10 or older) libc++, it will be just __1.

$ readelf -sW path/to/libfoo.so | grep __ndk1
     6: 000008bf    86 FUNC    WEAK   DEFAULT   12 _ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEE6__initEPKcj
     7: 000008a9    22 FUNC    WEAK   DEFAULT   12 _ZNSt6__ndk112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEED2Ev

If you're trying to figure out which version of the NDK a library was built with, you can use https://android.googlesource.com/platform/ndk/+/master/parse_elfnote.py.

$ python parse_elfnote.py foo/libs/armeabi-v7a/libfoo.so 
----------ABI INFO----------
ABI_NOTETYPE: 1
ABI_VENDOR: Android
ABI_ANDROID_API: 14
ABI_NDK_VERSION: r17-canary
ABI_NDK_BUILD_NUMBER: dev

Most of this information will only be available for libraries built with a relatively new NDK (r14? I don't remember when we added this).

Dan Albert
  • 10,079
  • 2
  • 36
  • 79
  • Dan, it's a nice tip, but what can be practical consequences of this rev. engineering? I mean, if we know which shared STL was used for third party libfoo, we will use same shared STL for libbar that we compile ourselves. And better use same version. And then we can even pass C++ objects back and forth. But if libfoo was built with stlport_static, you can use any STL for libbar, and make sure that no C++ objects pass the boundary. – Alex Cohn Nov 28 '17 at 21:53
  • Still would be useful if you have a static library dependency. If it's a shared library dependency then yeah, you already have your answer (or a broken dependency that used a static STL, and then you have other problems). – Dan Albert Nov 29 '17 at 00:13
  • *"have some pre-compiled so files"* rules static dependencies out – Alex Cohn Nov 29 '17 at 15:03
2

NDK comes with a utility named readelf (under NDK/toolchains). To see which shared STL library is needed for a prebuilt binary, you can run

$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/*/bin/arm-linux-androideabi-readelf -a 3rd-party-lib.so | grep NEEDED | grep _shared

Version is tricky. The STL shared binaries ship without version information, and I don't believe there exists a process that tests their backwards compatibility. Still, both stlport abd gnustl have been stable for at least two years now.

You can usually determine what compiler was used to build the binary, but no, NDK compilers are not always changed with each NDK release.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • Another trick that can help you identify if libc++ was used even if it was c++_static: `readelf -sW path/to/lib | grep __ndk1`. All the libc++ symbols are in an internal namespace (`std::__ndk1` instead of just `std`) to support versioning the STL and avoiding clashes with the system STL. If it's a very old (r10 or older) libc++, it will be just `__1`. I don't know of a good way to spot gnustl vs stlport, but that will help with libc++ vs not-libc++. – Dan Albert Nov 28 '17 at 19:48
  • ... unless the creator of the library followed the rules and used default visibility=hidden – Alex Cohn Nov 28 '17 at 21:03