ndk-build outputs ‘error adding symbols. File in wrong format’
Asked Answered
A

1

10

I want to use the exiv2 library written in C++ in my Android project. To do that, I try to cross-compile the library using the Android NDK.

For cross-compiling, I follow the presented below steps:

  1. Add the NDK path to variable PATH

    PATH="/home/patrycja/android-packages/ndk:${PATH}"
    export PATH
    
  2. Install the standard toolchain for cross-compiling C/C++ for Android.

    ./make-standalone-toolchain.sh --platform=android-21 --install-dir=/tmp/my-android-toolchain --ndk-dir='/home/patrycja/android-packages/ndk/' --toolchain=arm-linux-androideabi-4.9 --system=linux-x86_64
    

    Output:

    Copying prebuilt binaries...
    Copying sysroot headers and libraries...
    Copying c++ runtime headers and libraries...
    Copying files to: /tmp/my-android-toolchain
    Cleaning up...
    Done.
    
  3. Set some environment variables so that the configuration and build process will use the right compiler.

    export PATH=/tmp/my-android-toolchain/bin:$PATH
    export CC="arm-linux-androideabi-gcc"
    export CXX="arm-linux-androideabi-g++"
    export CFLAGS='-mthumb -O2'
    export CXXFLAGS='-mthumb -O2'
    export LDFLAGS='-Wl,--fix-cortex-a8'
    export LIBS='-lstdc++ -lsupc++'
    
  4. Build the static library and sufficient headers

    ./configure --prefix=$(pwd)/build --host=arm-linux-androideabi --disable-shared --disable-xmp --disable-nls
    

As a result, I have created ‘build’ category files:

    ├── bin
    │   └── exiv2
    ├── include
    │   └── exiv2
    │       ├── *.hpp
    │
    ├── lib
    │   ├── libexiv2.a
    │   ├── libexiv2.la
    │   └── pkgconfig
    │       └── exiv2.pc
    └── share
        └── man
            └── man1
                └── exiv2.1

I copied the created static library libexiv2.a and include folder to my Android project in appName/src/main/jni/prebuild.

Android.mk looks like:

LOCAL_PATH := $(call my-dir)

# Static library information
LOCAL_MODULE := exiv2
LOCAL_SRC_FILES := ../prebuild/libexiv2.a
LOCAL_EXPORT_C_INCLUDES := ../prebuild/include/
LOCAL_EXPORT_LDLIBS := -lz
include $(PREBUILT_STATIC_LIBRARY)

# Wrapper information
include $(CLEAR_VARS)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../prebuild/include/
LOCAL_MODULE    := helloJNI
LOCAL_SRC_FILES := helloJNI.cpp
LOCAL_STATIC_LIBRARIES := exiv2
include $(BUILD_SHARED_LIBRARY)

In my wrapper in Android, I try to use the library. It looks like as follows:

#include <string.h>
#include <jni.h>
#include <exiv2/exiv2.hpp>

extern "C" {

JNIEXPORT jstring JNICALL Java_com_example_patrycja_testndi2_MyActivity_helloJNI(JNIEnv *env, jobject thiz)
    {
        std::ostringstream os;
        std::string file("/storage/emmc/DCIM/100MEDIA/IMAG0021.jpg");
        Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open(file);
        return env->NewStringUTF("asldjaljd");
    }
}

However ndk-build outputs that it can’t find it.

[arm64-v8a] Compile++      : helloJNI &lt;= helloJNI.cpp
[arm64-v8a] SharedLibrary  : libhelloJNI.so
jni/../prebuild/libexiv2.a: error adding symbols: File in wrong format
collect2: error: ld returned 1 exit status
make: *** [obj/local/arm64-v8a/libhelloJNI.so] Error 1

I believe that there’s something wrong with flags in cross-compiling. I have tried several options, but something is still wrong.

I’ve followed these instructions: How to use an external C++ library from native Android code

Argentinaargentine answered 4/6, 2015 at 15:9 Comment(0)
D
8

You've compiled exiv2 for armv5+ devices running at least Android 5.0 (Lollipop). Here ndk-build fails, because it's trying to link it from an arm64-v8a library it's building.

Cross-compiling without using ndk-build is hard to get right on Android, especially as you should support not only armv5, but also armv7, x86, x86_64, arm64-v8a, etc.

You should first set the --platform option to the same level as your minimum SDK level. Then rebuild your library and place it under ../prebuild/armeabi.

Then also cross-compile your library for the x86 architecture:

./make-standalone-toolchain.sh --platform=android-9 --install-dir=/tmp/my-android-toolchain-x86 --ndk-dir='/home/patrycja/android-packages/ndk/' --arch=x86 --toolchain=x86-4.8 --system=linux-x86_64

export PATH=/tmp/my-android-toolchain-x86/bin:$PATH
export CC="i686-linux-android-gcc"
export CXX="i686-linux-android-g++"
export CFLAGS='-O2 -mtune=atom -mssse3 -mfpmath=sse'
export CXXFLAGS='-O2 -mtune=atom -mssse3 -mfpmath=sse'
export LDFLAGS=''
export LIBS='-lstdc++ -lsupc++'

./configure --prefix=$(pwd)/build-x86 --host=x86 --disable-shared --disable-xmp --disable-nls

And move the created .a to ../prebuild/x86.

Ideally, you should repeat the same process also for armeabi-v7a, mips, mips64, and arm64-v8a.

Finally, you can include the right .a inside your Android.mk using the TARGET_ARCH_ABI variable, like so:

LOCAL_PATH := $(call my-dir)

# Static library information
LOCAL_MODULE := exiv2
LOCAL_SRC_FILES := ../prebuild/$(TARGET_ARCH_ABI)/libexiv2.a
LOCAL_EXPORT_C_INCLUDES := ../prebuild/include/
LOCAL_EXPORT_LDLIBS := -lz
include $(PREBUILT_STATIC_LIBRARY)

# Wrapper information
include $(CLEAR_VARS)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/../prebuild/include/
LOCAL_MODULE    := helloJNI
LOCAL_SRC_FILES := helloJNI.cpp
LOCAL_STATIC_LIBRARIES := exiv2
include $(BUILD_SHARED_LIBRARY)

And inside Application.mk (create a new file if it doesn't exist), specify the architectures you're supporting and the minimum platform you're targeting:

APP_ABI := armeabi x86 # Ideally, this should be set to "all"
APP_PLATFORM := android-14 # Should be the same as -platform and your minSdkVersion.
Droshky answered 11/6, 2015 at 16:13 Comment(4)
I did like you said, but another problem occured. Doing ndk-build I receive: jni/../prebuild/armeabi-v7a/libexiv2.a(pngchunk.o):pngchunk.cpp:function Exiv2::Internal::PngChunk::parseChunkContent(Exiv2::Image*, unsigned char const*, long, Exiv2::DataBuf): error: undefined reference to 'std::__throw_out_of_range_fmt(char const*, ...)'Argentinaargentine
I have used wrong version of gcc. It works with 4.8.Argentinaargentine
This solution presumes the legacy ndk gradle build. With the new gradle-experimental build system, there is no Application.mk.Baudoin
the original question was about ndk-build so the answer is still accurate. If you have the same kind of issue with gradle and the experimental plugin, you should open a new question.Droshky

© 2022 - 2024 — McMap. All rights reserved.