NDK r8d linker error....

572 views
Skip to first unread message

Richard Schilling

unread,
Mar 25, 2013, 6:19:32 AM3/25/13
to andro...@googlegroups.com
I've been researching this error on the interweb for a few days now. A build system on top of gmake is a noble and worthwhile idea, but the Android NDK is, shall I say, very fussy.  I can't seem to find a problem reported that is similar enough.  So. I'm hoping someone can help.

My Android.mk files and compiler output are below.

I'm getting a linker error. Can anyone help me figure this one out?  It's really kinda urgent because I've spent so much time trying to wrestle with it.

I've got two modules - one is static (called filter), and another one is shared (called application).  

Module filter generates the file libfilter.a, Shared module application generates lib application.so.

libapplication.so relies on a function that is defined/implemented in libfilter.a.  It all compiles fine, but when the linker tries to do it's job it just chokes.

Here's Android.mk for module filter:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := filter
APP_SUBDIRS := $(patsubst $(LOCAL_PATH)/%, %, $(shell find $(LOCAL_PATH)/src -type d))
# Add more subdirs here, like src/subdir1 src/subdir2
LOCAL_CFLAGS := $(foreach D, $(APP_SUBDIRS), -I$(LOCAL_PATH)/$(D)) \
-I$(AVPLAYER_PATH)"/native/ffmpeg/ffmpeg-0.11.1" \
-I$(LOCAL_PATH)/include -DFT2_BUILD_LIBRARY 

#Change C++ file extension as appropriate
LOCAL_CPP_EXTENSION := .cpp
LOCAL_SRC_FILES := $(foreach F, $(APP_SUBDIRS), $(addprefix $(F)/,$(notdir $(wildcard $(LOCAL_PATH)/$(F)/*.cpp))))
# Uncomment to also add C sources
LOCAL_SRC_FILES += $(foreach F, $(APP_SUBDIRS), $(addprefix $(F)/,$(notdir $(wildcard $(LOCAL_PATH)/$(F)/*.c))))
LOCAL_SHARED_LIBRARIES :
LOCAL_STATIC_LIBRARIES :
LOCAL_LDLIBS :=
include $(BUILD_STATIC_LIBRARY)

And here's the Android.mk file for the module application (shared library):

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := application
APP_SUBDIRS := $(patsubst $(LOCAL_PATH)/%, %, $(shell find $(LOCAL_PATH)/src -type d))
APP_SUBDIRS += $(patsubst $(LOCAL_PATH)/%, %, $(shell find $(LOCAL_PATH)/resources -type d))
# Add more subdirs here, like src/subdir1 src/subdir2
# -I$(AVPLAYER_PATH)"/native/ffmpeg/ffmpeg-0.11.1" \
# -I$(AVPLAYER_PATH)"/native/ffmpeg/ffmpeg-0.10.4" \
LOCAL_CFLAGS := $(foreach D, $(APP_SUBDIRS), -I$(LOCAL_PATH)/$(D)) \
-D__STDC_CONSTANT_MACROS \
-D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE \
-I$(AVPLAYER_PATH)"/native/ffmpeg/ffmpeg-0.11.1" \
-I$(AVPLAYER_PATH)"/jni/sdl/include" \
-I$(AVPLAYER_PATH)"/jni/sdl_ttf" \
-I$(AVPLAYER_PATH)"/jni/sdl_image/include" \
-I$(AVPLAYER_PATH)"/jni/iconv/include" \
-I$(AVPLAYER_PATH)"/jni/iconv/srclib" \
-I$(AVPLAYER_PATH)"/jni/universalchardet/include" \
-I$(AVPLAYER_PATH)"/jni/yuv2rgb/include" \
-I$(AVPLAYER_PATH)"/jni/app/include" \
-I$(AVPLAYER_PATH)"/jni/filter/include"
LOCAL_CFLAGS += $(CC_OPTIMIZE_FLAG)
#ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
#   LOCAL_CFLAGS += -DHAVE_NEON=1
#endif
#Change C++ file extension as appropriate
LOCAL_CPP_EXTENSION := .cpp
LOCAL_SRC_FILES := $(foreach F, $(APP_SUBDIRS), $(addprefix $(F)/,$(notdir $(wildcard $(LOCAL_PATH)/$(F)/*.cpp))))
# Uncomment to also add C sources
LOCAL_SRC_FILES += $(foreach F, $(APP_SUBDIRS), $(addprefix $(F)/,$(notdir $(wildcard $(LOCAL_PATH)/$(F)/*.c))))
LOCAL_SRC_FILES += $(foreach F, $(APP_SUBDIRS), $(addprefix $(F)/,$(notdir $(wildcard $(LOCAL_PATH)/$(F)/*.S))))
#LOCAL_SHARED_LIBRARIES := ffmpeg sdl sdl_ttf sdl_image iconv universalchardet andprof yuv2rgb
#LOCAL_SHARED_LIBRARIES := ffmpeg sdl sdl_ttf sdl_image iconv universalchardet yuv2rgb
LOCAL_SHARED_LIBRARIES := sdl sdl_ttf sdl_image iconv universalchardet yuv2rgb
#LOCAL_STATIC_LIBRARIES := freetype bz2
LOCAL_STATIC_LIBRARIES := freetype filter
LIBS_WITH_LONG_SYMBOLS := $(strip $(shell \
for f in $(LOCAL_PATH)/../../libs/armeabi/*.so ; do \
if echo $$f | grep "libapplication[.]so" > /dev/null ; then \
continue ; \
fi ; \
if [ -e "$$f" ] ; then \
if nm -g $$f | cut -c 12- | egrep '.{128}' > /dev/null ; then \
echo $$f | grep -o 'lib[^/]*[.]so' ; \
fi ; \
fi ; \
done \
) )
ifneq "$(LIBS_WITH_LONG_SYMBOLS)" ""
$(foreach F, $(LIBS_WITH_LONG_SYMBOLS), \
$(info Library $(F): abusing symbol names are: \
$(shell nm -g $(LOCAL_PATH)/../../libs/armeabi/$(F) | cut -c 12- | egrep '.{128}' ) ) \
$(info Library $(F) contains symbol names longer than 128 bytes, \
YOUR CODE WILL DEADLOCK WITHOUT ANY WARNING when you'll access such function - \
please make this library static to avoid problems. ) )
$(error Detected libraries with too long symbol names. Remove all files under project/libs/armeabi, make these libs static, and recompile)
endif
LOCAL_LDLIBS := -lGLESv1_CM -ldl -llog 
ifeq ($(TARGET_ARCH_ABI),x86)
   LOCAL_LDLIBS += $(AVPLAYER_PATH)/"native/ffmpeg/ffmpeg-0.11.1/android/x86/libffmpeg.so"
else
ifeq ($(TARGET_ARCH_ABI),mips)
   LOCAL_LDLIBS += $(AVPLAYER_PATH)/"native/ffmpeg/ffmpeg-0.11.1/android/mips/libffmpeg.so"
else
   LOCAL_LDLIBS += $(AVPLAYER_PATH)/"native/ffmpeg/ffmpeg-0.11.1/android/armv6_vfp/libffmpeg.so"
   #LOCAL_LDLIBS += $(AVPLAYER_PATH)/"native/ffmpeg/ffmpeg-0.11.1/android/armv5te/libffmpeg.so"
endif
endif

include $(BUILD_SHARED_LIBRARY)




Here's the linker error:

rm -f ./libs/armeabi/lib*.so ./libs/armeabi-v7a/lib*.so ./libs/mips/lib*.so ./libs/x86/lib*.so
rm -f ./libs/armeabi/gdbserver ./libs/armeabi-v7a/gdbserver ./libs/mips/gdbserver ./libs/x86/gdbserver
rm -f ./libs/armeabi/gdb.setup ./libs/armeabi-v7a/gdb.setup ./libs/mips/gdb.setup ./libs/x86/gdb.setup
SharedLibrary  : libapplication.so
/Applications/SDK/android-ndk/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/bin/arm-linux-androideabi-g++ -Wl,-soname,libapplication.so -shared --sysroot=/Applications/SDK/android-ndk/platforms/android-8/arch-arm ./obj/local/armeabi-v7a/objs/application/src/broov_gui.o ./obj/local/armeabi-v7a/objs/application/src/broov_player.o ./obj/local/armeabi-v7a/objs/application/src/native_main.o ./obj/local/armeabi-v7a/objs/application/src/video_player.o ./obj/local/armeabi-v7a/objs/application/src/b_sdl.o ./obj/local/armeabi-v7a/objs/application/src/broov_font.o ./obj/local/armeabi-v7a/objs/application/src/broov_queue.o ./obj/local/armeabi-v7a/objs/application/src/ffplay.o ./obj/local/armeabi-v7a/objs/application/src/optimization.o ./obj/local/armeabi-v7a/objs/application/src/subreader.o ./obj/local/armeabi-v7a/objs/application/src/universalchardet.o ./obj/local/armeabi-v7a/objs/application/resources/bg_loading.o ./obj/local/armeabi-v7a/objs/application/resources/dejavu_sans.o ./obj/local/armeabi-v7a/libfreetype.a ./obj/local/armeabi-v7a/libfilter.a ./obj/local/armeabi-v7a/libstlport_static.a ./obj/local/armeabi-v7a/libsdl.so ./obj/local/armeabi-v7a/libsdl_ttf.so ./obj/local/armeabi-v7a/libsdl_image.so ./obj/local/armeabi-v7a/libiconv.so ./obj/local/armeabi-v7a/libuniversalchardet.so ./obj/local/armeabi-v7a/libyuv2rgb.so -no-canonical-prefixes -march=armv7-a -Wl,--fix-cortex-a8  -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -L/Applications/SDK/android-ndk/platforms/android-8/arch-arm/usr/lib -lGLESv1_CM -ldl -llog "/Users/richard/Development/repositories/git-remote/aphex-dolphin/AphexDolphin"/"native/ffmpeg/ffmpeg-0.11.1/android/armv6_vfp/libffmpeg.so" -lc -lm -o ./obj/local/armeabi-v7a/libapplication.so
/Applications/SDK/android-ndk/toolchains/arm-linux-androideabi-4.6/prebuilt/darwin-x86/bin/../lib/gcc/arm-linux-androideabi/4.6/../../../../arm-linux-androideabi/bin/ld: ./obj/local/armeabi-v7a/objs/application/src/broov_player.o: in function player_main(int, char**, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int):jni/app/src/broov_player.cpp:3414: error: undefined reference to 'filter_setup()'
collect2: ld returned 1 exit status
make: *** [obj/local/armeabi-v7a/libapplication.so] Error 1





David Turner

unread,
Mar 26, 2013, 5:52:33 AM3/26/13
to andro...@googlegroups.com
OK, there are several interesting things in these files, but let's start with the link error.

The error complains that 'filter_setup()' is not found. I assume that this function is built as part of libfilter.a.

First of all, libfilter.a appears after the broov_queue.o in the link command. This is absolutely correct, which means we can forget about a bug in ndk-build's ordering of objects and library files at link time.

Notice that the symbol name contains parentheses, i.e. it's "filter_setup()" and not "filter_setup", it means the linker is looking for a C++ symbol, not a C one.
Things that could explain the error then are:
- The function is defined by a C source file, and is thus exported as "filter_setup", not "filter_setup()"
- The function is defined by a C++ source file, but isn't exported because it's either static, or part of an anonymous namespace.
- The function is defined by a C or C++ source file that is never compiled.

The first thing you should do is list the symbols exported by libfilter.a to check its content:

  arm-linux-androideabi-nm -C --defined-only /path/to/libfilter.a | grep filter_setup

If the result contains a line that defines "filter_setup", and not "filter_setup()", then it's the first problem above.
The solution is to ensure that the C++ code that wants to call the function uses a declaration with C linkage (i.e. extern "C").

If the result contains a line that contains "filter_setup()" (and nothing more fancy), then there is something _really_ wrong with the linker, and I definitely wants to have access to these binaries to see why this fails.

If the result is empty, the symbol is not exported by libfilter.a. The simplest reason for this would be for the corresponding source file to not be listed properly when building libfilter.a. Your Android.mk uses filesystem globbing (i.e. $(wildcard ..)) which is generally a bad idea in build files, but should still work. This makes impossible however to check wether the source file containing the function is compiled or not.

I recommend you first check that this is the case (e.g. look under obj/local/ for the corresponding object file, or touch the file, and try to invoke ndk-build to see if it rebuilds anything, if not, there is something fishy).

Add a statement like:

  $(info SOURCES='$(LOCAL_SRC_FILES)')

inside your Android.mk for libfilter.a to dump the list or real source files that are sent to ndk-build, this will allow you to check that the globbing works.

If the source file is compiled, verify its content and its object file to ensure that the function is properly compiled into it and exported. For example, to disassemble the object file:

arm-linux-androideabi-objdump -d -C --wide /path/to/object.o

Then verify that the object file is part of libfilter.a too, with:

arm-linux-androideabi-ar t /path/to/libfilter.a

If the object file is not there, there is a bug in ndk-build though, and I'd like to have more details on your build to reproduce it though.


Apart from that, so general recommendations when writing your Android.mk:

I) Prefer

  LOCAL_C_INCLUDES := <directory1> <directory2>

to

  LOCAL_CFLAGS := -I<directory1> -I<directory2>

They are equivalent except that the first is a little cleaner, and ndk-build will automatically translate the paths for you if you're building under Cygwin.

II) Avoid filesystem globbing if you can:

This is generally a bad idea in a build file for several reasons:

- Invoking shell commands like "find" slows downs the build, sometimes very noticeably.
- It hides the list of source files, which in case of problem tends to make things harder to debug.
- It makes the build more fragile / less reproduceable when several developers are working on the same project. They need to be 100% sure to have exactly the same files in all directories to ensure they build the same thing. Again, this can bite you in nasty way when you least expect it.

Of course, listing all file manually can be very annoying, but it's a one-time effort (ongoing maintenance is typically much easier). Moreover, you can automate it. A common trick is to use a small script to generate a build file, e.g.:

#!/bin/sh
echo "my_sources := \\"
for SUBDIR in subdir1 subdir2; do
  SOURCES=$(find $SUBDIR -name '*.c' -o -name '*.cpp' -o -name '*.S')
  for SRC in $SOURCES; do
    echo "    $SRC \\"
  done
done
echo ""

III) Use module exports / dependencies properly:

Try using LOCAL_EXPORT_XXX variables to ensure you don't have to repeat include paths and other flags in every module that depends on another one.
In your example, your filter module should use:

  LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/filter/include

Which allows you to get rid of the "-I$(AVPLAYER_PATH)"/jni/filter/include"in application's Android.mk

You should also probably define a module for ffmpeg which contains all the ugly ffmpeg-stuff and only exports the minimum, for example:

include $(CLEAR_VARS)
LOCAL_MODULE := libffmpeg
LOCAL_EXPORT_C_INCLUDES := $(AVPLAYER_PATH)/native/ffmpeg/ffmpeg-0.11.1
ifneq ($(filter armeabi armeabi-v7a,$(TARGET_ARCH_ABI))
  FFMPEG_ARCH := armv6_vfp
else
  FFMPEG_ARCH := $(TARGET_ARCH_ABI)
endif
LOCAL_SRC_FILES := $(AVPLAYER_PATH)/native/ffmpeg/ffmpeg-0.11.1/android/$(FFMPEG_ARCH)/libffmpeg.so
include $(PREBUILT_SHARED_LIBRARY)

If you use a PREBUILT_SHARED_LIBRARY module, there is no need to use LOCAL_LDLIBS in the modules that need it (just add libffmpeg to their LOCAL_SHARED_LIBRARIES).

It's unclear whether you have module definitions for sdl, sdl_ttf and the rest, but if so I'd advice you to ensure these modules export the right include directories and dependencies to make your overall Android.mk much easier to understand and maintain.

Also, I don't know what the hell this LIBS_WITH_LONG_SYMBOLS stuff is being used for. It looks like you "inherited" that from some SDL build files, but I'm not aware of some issue with symbol names larger than 128, either in the static linker or the sytem's dynamic linker. It'd be interesting to have a repro case.







--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to android-ndk...@googlegroups.com.
To post to this group, send email to andro...@googlegroups.com.
Visit this group at http://groups.google.com/group/android-ndk?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Richard Schilling

unread,
Mar 26, 2013, 8:01:24 PM3/26/13
to andro...@googlegroups.com
You're exactly right.    the extern "C" {} was missing around the exported function signatures in the include file.

Now, I'm getting another error  and it's a really weird one ... posting that in a separate thread.

Thanks for your time!

Cheers,

Richard
Reply all
Reply to author
Forward
0 new messages