Search code examples
androidandroid-ndklibm

Linking errors when using functions from <complex.h> in using API level 22


I'm porting a C and C++ library that currently works on iOS to be used on an Android application. I'm down to these last 3 linker errors (obfuscated for privacy reasons):

/Users/fer662/projects/xxx/jni/xxx_preprocessing.c:10184: error: undefined reference to 'cexp' /Users/fer662/projects/xxx/jni/xxx_preprocessing.c:10184: error: undefined reference to 'cpowf' /Users/fer662/projects/xxx/jni/xxx_preprocessing.c:10285: error: undefined reference to 'cabs'

Now I understand these normally come from linking with libm.so (-lm), but i'm doing that already. If I go and check the offending so with nm:

nm -g /Users/fer662/Library/Android/sdk/ndk-bundle/platforms/android-22/arch-x86/usr/lib/libm.so | grep cpow

Nothing comes back. It DOES hoever, if I use api 28

nm -g  /Users/fer662/Library/Android/sdk/ndk-bundle/platforms/android-28/arch-x86/usr/lib/libm.so | grep cpow
00003900 T cpow
00003910 T cpowf
00003920 T cpowl

Also, in the static library it does show, even on api 22:

nm -g /Users/fer662/Library/Android/sdk/ndk-bundle/platforms/android-22/arch-x86/usr/lib/libm.a | grep cpow s_cpow.o: 00000000 T cpow s_cpowf.o: 00000000 T cpowf s_cpowl.o: 00000000 T cpowl

The inconsistency is puzzling. Shouldn't it be missing from the header altogether if not supported? Why does the static lib have it and the dylib not?

Would it make sense to statically link against it? And if so, how would I do it, taking into account the right path for the current api version?

My other option seems to go steal an implementation of libm (say http://openlibm.org/) or just these 3 functions I'm using from it.


Solution

  • tl;dr: yes, static linking libm.a should be fine

    Check the libm.map.txt file: https://android.googlesource.com/platform/bionic/+/master/libm/libm.map.txt#289

    These functions weren't added to Android until O.

    Also, in the static library it does show, even on api 22

    The static library isn't an API 22 static library. It's actually a ToT build from AOSP. If you're going to static link something, there's no point in using something old.

    The reason it (there's actually only one version of libc.a/libm.a per ABI) is duplicated into each API directory is because build systems made for old NDKs expect it. If you look at the unified toolchain in r19 (toolchains/llvm/prebuilts/$HOST), you'll see that there's only one copy per ABI.

    The inconsistency is puzzling. Shouldn't it be missing from the header altogether if not supported? Why does the static lib have it and the dylib not?

    The header has an ifdef guard that hides it: https://android.googlesource.com/platform/prebuilts/ndk/+/dev/platform/sysroot/usr/include/complex.h#237

    If you had a declaration for these functions and you think you were building for API 22, there's something wrong with your build system.

    Would it make sense to statically link against it? And if so, how would I do it, taking into account the right path for the current api version?

    In general for these sorts of problems this isn't a good solution since the Zygote has already loaded a libc, and loading another one can lead to all sorts of issues since they can conflict. Additionally, much of libc's networking is actually dispatched to netd, and the protocol between libc and netd has changed in the past (and is unfortunately not a versioned protocol).

    Building with libc.a is only viable with standalone executables (think strace and gdbserver) rather than apps, and even then only if you don't need networking.

    That said, libm.a is much simpler. The complex interactions that make libc.a unusable for apps don't affect libm. The only time you'll end up actually running code in libm is when the compiler somehow failed to inline the operation. Static linking libm.a into your application should be fine.