2

I'm getting an "undefined reference to 'myfunction' trying to call a function in a static (.a) library from a shared (.so) library. (This is an NDK question on OS X). The function is part of the fft library called "ffts-android" (ffts-android). I'm able to generate libraries with the project just fine. The only change I made is from BUILD_SHARED_LIBRARY to BUILD_STATIC_LIBRARY so that I can incorporate it into the shared library we use in our org's Android app.

I'm pretty sure the function I need, ffts_init_1d(size_t, int), exists as it ought in the static library because I used the nm utility (output at bottom) to list what functions are available in the symbol table.

Then I thought I'd try to modify one of NDK's simple example programs in case my Android.mk is the problem. I took the "module-exports" sample and made the simplest changes necessary and get the same error. In its original state it builds a static library and then links it into a shared library, just as I'm trying to do. I just added to Android.mk a single line, the middle one below, so that it links to the very same library I built from the ffts-android project:

LOCAL_STATIC_LIBRARIES := foo
LOCAL_STATIC_LIBRARIES += libffts-neon
include $(BUILD_SHARED_LIBRARY)

then modified bar.c so that it would call the same method, ffts_init_1d(size_t, int) that I'm trying to call:

#include "bar.h"
#include "ffts.h"
#include "foo.h"

int  bar(int  x)
{
    /*return*/foo(x)-1;
    ffts_init_1d(1, 1);
    return 1;
}

and finally, I moved the ffts.h and the libffts-neon.a files to where they would be found by module-exports and then built the sample. It generates the exact same error I am getting.

samples/module-exports/jni/bar/bar.c:8: undefined reference to `ffts_init_1d'

Both the module-exports project and ffts-android project are purely c code, whereas our codebase is c++, so I'm attempting to call a static library built from c code from c++. The header file, ffts.h, however already has the needed code to adapt to this, the standard:

#ifdef __cplusplus
extern "C"
{

Below is the output from the nm utility. It seems to me to list the function I'm trying to call, so why is it unavailable?

./nm -g libffts-neon.a

ffts.o:
         U _GLOBAL_OFFSET_TABLE_
         U __aeabi_uidiv
         U __aeabi_uidivmod
         U __aeabi_unwind_cpp_pr0
         U __aeabi_unwind_cpp_pr1
         U __android_log_print
         U __errno
         U cos
         U exit
00000000 T ffts_execute
00000000 T ffts_free
00000000 T ffts_free_1d
         U ffts_generate_func_code
00000000 T ffts_init_1d
         U ffts_init_is
         U ffts_init_offsets
         U firstpass_16_b
         U firstpass_16_f
         U firstpass_2
         U firstpass_4_b
         U firstpass_4_f
         U firstpass_8_b
         U firstpass_8_f
         U free
         U malloc
         U mprotect
         U munmap
         U perror
         U sin
         U valloc

ffts_real.o:
         U __aeabi_unwind_cpp_pr0
         U __aeabi_unwind_cpp_pr1
         U cos
00000000 T ffts_execute_1d_real
00000000 T ffts_execute_1d_real_inv
         U ffts_free
00000000 T ffts_free_1d_real
         U ffts_init_1d
00000000 T ffts_init_1d_real
         U free
         U malloc
         U sin
         U valloc
. . .

Here is what I get when I invoke ndk-build with V=1 (verbose output):

MacBook-Pro:module-exports myuser$ ndk-build V=1
rm -f ./libs/arm64-v8a/lib*.so ./libs/armeabi/lib*.so ./libs/armeabi-v7a/lib*.so ./libs/armeabi-v7a-hard/lib*.so ./libs/mips/lib*.so ./libs/mips64/lib*.so ./libs/x86/lib*.so ./libs/x86_64/lib*.so
rm -f ./libs/arm64-v8a/gdbserver ./libs/armeabi/gdbserver ./libs/armeabi-v7a/gdbserver ./libs/armeabi-v7a-hard/gdbserver ./libs/mips/gdbserver ./libs/mips64/gdbserver ./libs/x86/gdbserver ./libs/x86_64/gdbserver
rm -f ./libs/arm64-v8a/gdb.setup ./libs/armeabi/gdb.setup ./libs/armeabi-v7a/gdb.setup ./libs/armeabi-v7a-hard/gdb.setup ./libs/mips/gdb.setup ./libs/mips64/gdb.setup ./libs/x86/gdb.setup ./libs/x86_64/gdb.setup
[armeabi-v7a] SharedLibrary  : libbar.so
/Users/myuser/Documents/NDKDev/android-ndk-r10e/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-g++ -Wl,-soname,libbar.so -shared --sysroot=/Users/myuser/Documents/NDKDev/android-ndk-r10e/platforms/android-3/arch-arm ./obj/local/armeabi-v7a/objs/bar/bar/bar.o ./obj/local/armeabi-v7a/libfoo.a -lgcc -no-canonical-prefixes -march=armv7-a -Wl,--fix-cortex-a8  -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -mthumb  -L/Users/myuser/Documents/NDKDev/android-ndk-r10e/platforms/android-3/arch-arm/usr/lib -llog -lc -lm -o ./obj/local/armeabi-v7a/libbar.so
jni/bar/bar.c:8: error: undefined reference to 'ffts_init_1d'
collect2: error: ld returned 1 exit status
make: *** [obj/local/armeabi-v7a/libbar.so] Error 1

Here's the Android.mk file. Modified slightly, as described:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo/foo.c
LOCAL_CFLAGS := -DFOO=2
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/foo
LOCAL_EXPORT_CFLAGS := -DFOO=1
LOCAL_EXPORT_LDLIBS := -llog
include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := bar
LOCAL_SRC_FILES := bar/bar.c
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/bar
LOCAL_STATIC_LIBRARIES := libffts-neon
LOCAL_STATIC_LIBRARIES += foo
include $(BUILD_SHARED_LIBRARY)
Alyoshak
  • 2,696
  • 10
  • 43
  • 70
  • libffts_neon is, apparently, built for armeabi-v7a, but your so is built for armeabi. Try `ndk-build APP_ABI=armeabi-v7a`, with the same minimal Android.mk, maybe it will help. – Alex Cohn Nov 17 '15 at 19:01
  • Same error. The Application.mk was defined for ALL, so it was indeed trying to build some combinations that would not work. But I defined it as you indicated, solely for armeabi-v7a and got same error. To be clear I'm building the module-exports sample, modified as indicated in question. Fyi the sample builds its .a library in its objs/local/armeabi-v7a folder so that's where I put the libffts-neon.a file. – Alyoshak Nov 17 '15 at 20:07
  • Interesting. Just noticed the error msg did change slightly. Before it was samples/module-exports/jni/bar/bar.c:8: undefined reference to `ffts_init_1d'. After redefining APP_ABI it is now jni/bar/bar.c:8: error: undefined reference to 'ffts_init_1d' – Alyoshak Nov 17 '15 at 20:19
  • Maybe it's not looking for the lib in the proper directory? Run `ndk-build V=1` and paste the console output here (or on pastebin) – Alex Cohn Nov 17 '15 at 20:38
  • There you go. I thought it was finding the library ok, but just couldn't find the function. – Alyoshak Nov 18 '15 at 17:14
  • I added the V=1 console output to the question. – Alyoshak Nov 18 '15 at 17:39
  • I see `./obj/local/armeabi-v7a/libfoo.a` in the list, but not libffts-neon. What could have gone wrong? Most likely, the **Android.mk** file that defines the prebuilt static library named **libffts-neon**. – Alex Cohn Nov 18 '15 at 19:32
  • Gee whiz. I see it. I'm editing the question to include my Android.mk. Be advised I just switched the order of the two LOCAL_STATIC_LIBRARIES assignments, so it's been tried both ways: to load libFoo.a first and to load libffts-neon first. Same result. But the libraries are located side-by-side. How can it find one but not other? – Alyoshak Nov 18 '15 at 20:06
  • Cool. You build libfoo, but you don't build libffts-neon in the posted Android.mk. How can NDK-build find it? – Alex Cohn Nov 18 '15 at 20:17
  • Yes. I build it elsewhere, in a separate invocation of ndk-build, the ffts-android codebase. ndk-build should build it whenever one gives it the info needed to do so. Guess I haven't done so. Or are you saying it is required to build the libffts-neon.a in the very same Android.mk as libfoo.a??? – Alyoshak Nov 18 '15 at 20:23
  • The explanation can be found [on Android developers site](http://developer.android.com/ndk/guides/prebuilts.html) – Alex Cohn Nov 18 '15 at 20:28
  • 1
    It worked. Many thanks. Want to provide an answer I can select as correct? One question: the docs you sent confused me on issue of what/how to use LOCAL_MODULE. I got it to work by using "my-ffts" in the new prebuilt section, and then assigning "my-ffts" into LOCAL_STATIC-LIBRARIES in the final section that builds the shared library. But NO mention of "my-ffts" in the V=1 output. What use is LOCAL_MODULE? Only to provide renaming within .mk files? – Alyoshak Nov 18 '15 at 20:43

1 Answers1

2

You must define the prebuilt static library libffts-neon.a for your module to use. The idea is that every library (static or shared, prebuilt or compiled) should have its separate section and be assigned its separate LOCAL_MODULE name. So, you are missing the following enchantment:

include $(CLEAR_VARS)
LOCAL_MODULE := libffts-neon
LOCAL_SRC_FILES := obj/local/armeabi-v7a/libffts-neon.a
include $(PREBUILT_STATIC_LIBRARY)

This can be placed anywhere in your Android.mk, or use include directive and keep this a separate file.

Note that LOCAL_STATIC_LIBRARIES refer to the module name, not the name of the actual file. So, the following would also be OK:

include $(CLEAR_VARS)
LOCAL_MODULE := qqq
LOCAL_SRC_FILES := obj/local/armeabi-v7a/libffts-neon.a
include $(PREBUILT_STATIC_LIBRARY)
…
LOCAL_STATIC_LIBRARIES += qqq
include $(BUILD_SHARED_LIBRARY)

Another note, you can put your prebuilt library anywhere, and ndk-build will copy it as necessary to the appropriate directory.

Official documentation is surprisingly detailed, and addresses the issues of multi-ABI support and other darker corners of prebuilt libraries.

Alex Cohn
  • 56,089
  • 9
  • 113
  • 307