0

Im trying to build an executable which depends on a shared library in C for android. I want to run the executable on android shell. I first built the executable using android ndk (I'm using android-ndk-r16b), then using adb push I put the files generated by ndk on my android device then using adb shell i try to run the executable.

Im building on Ubuntu 14.04. I installed the android arm toolchain using apt-get:

sudo apt-get install gcc-arm-linux-androideabi

Here are my files:

Shared library: libcal.so

cal.c:

#include "cal.h"
int add(int a , int b)
{
    return (a + b);
}
int sub(int a, int b)
{
    return (a - b);
}

cal.h:

int add(int a , int b);

int sub(int a, int b);

Makefile:

CXX=arm-linux-androideabi-gcc
CXXFLAGS=-fPIC -Wall -I. -c
LDFLAGS=-shared 
SOURCES=./cal.c
OBJECTS=$(SOURCES:.c=.o)
TARGET_LIB=libcal.so

all: $(SOURCES) $(TARGET_LIB)

$(TARGET_LIB): $(OBJECTS) 
    $(CXX) -o $@ $(OBJECTS) $(LDFLAGS)

.c.o:
    $(CXX) $(CXXFLAGS) $< -o $@
.PHONY: clean
clean: 
    @rm -f $(TARGET_LIB) $(OBJECTS)

So this how I generate the shared library. Then in a folder called jni I have the following files:

test.c:

#include <stdio.h>
#include "cal.h"

int main()
{
    int sum = 0, diff = 0;

    sum = add(3,2);

    diff = sub(2,2);

    printf("sum = %d\ndiff = %d\n",sum, diff);
    return 0;
} 

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS) 
LOCAL_MODULE    := cal
LOCAL_SRC_FILES := $(LOCAL_PATH)/../../library/libcal.so 
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/../../library/
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE := test
LOCAL_SRC_FILES := test.c
LOCAL_SHARED_LIBRARIES := cal


include $(BUILD_EXECUTABLE)    

Application.mk

APP_ABI := armeabi-v7a
APP_PLATFORM := android-19

Then on running $ndk-build this is my output

rohith@rohith-Lenovo-G50-80:~/example2/hello/jni$ ndk-build
[armeabi-v7a] Prebuilt       : libcal.so <= jni/../../library/
[armeabi-v7a] Install        : libcal.so => libs/armeabi-v7a/libcal.so
[armeabi-v7a] Compile thumb  : test <= test.c
[armeabi-v7a] Executable     : test
[armeabi-v7a] Install        : test => libs/armeabi-v7a/test
rohith@rohith-Lenovo-G50-80:~/example2/hello/jni$ cd ../libs/armeabi-v7a/
rohith@rohith-Lenovo-G50-80:~/example2/hello/libs/armeabi-v7a$ ls
libcal.so  test
rohith@rohith-Lenovo-G50-80:~/example2/hello/libs/armeabi-v7a$ 

Thus my android compatible files are in the libs folder

NOTE: If i don't use the Application.mk the build fails with the following output

rohith@rohith-Lenovo-G50-80:~/example/hello/jni$ ndk-build
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-14.    
[arm64-v8a] Prebuilt       : libcal.so <= jni/../../library/
[arm64-v8a] Install        : libcal.so => libs/arm64-v8a/libcal.so
[arm64-v8a] Compile        : test <= test.c
[arm64-v8a] Executable     : test
/home/rohith/example/hello/obj/local/arm64-v8a/libcal.so: error adding symbols: File in wrong format
clang++: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [/home/rohith/example/hello/obj/local/arm64-v8a/test] Error 1

Anyways now after I put the shared library and executable on the android device and run the executable i get the following error:

rohith@rohith-Lenovo-G50-80:~/example2/hello/libs/armeabi-v7a$ ls
libcal.so  test
rohith@rohith-Lenovo-G50-80:~/example2/hello/libs/armeabi-v7a$ cd ..
rohith@rohith-Lenovo-G50-80:~/example2/hello/libs$ adb push armeabi-v7a /data/local/rohith
push: armeabi-v7a/libcal.so -> /data/local/rohith/libcal.so
push: armeabi-v7a/test -> /data/local/rohith/test
2 files pushed. 0 files skipped.
134 KB/s (11088 bytes in 0.080s)
rohitht@rohith-Lenovo-G50-80:~/example2/hello/libs$ adb shell
root@tcc897x:/ # cd /data/local/rohith
root@tcc897x:/data/local/rohith # ls
libcal.so
test
root@tcc897x:/data/local/rohith # export LD_LIBRARY_PATH=.
root@tcc897x:/data/local/rohith # ./test
CANNOT LINK EXECUTABLE: could not load library "/home/rohith/example2/hello/obj/local/armeabi-v7a/libcal.so" needed by "./test"; caused by library "/home/rohith/example2/hello/obj/local/armeabi-v7a/libcal.so" not found
1|root@tcc897x:/data/local/rohith # 

Why is it linking to paths with respect to my laptop?

I would appreciate it immensely if someone can tell what I'm doing wrong or give me advice on how they have gone about doing something similar.

My actual task requires me link much more complicated shared libraries and run an executable on android shell. Don't know how I am going to do that if I can get a simple .so working.

2 Answers2

1

The library was not built correctly. This kind of problem with SONAME path is not new. It has surfaced after one particular fix in Android at API 23.

If for whatever reason you cannot rebuild this library with latest NDK, you may try the patchelf utility to add SONAME to existing binary.

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

I tried one of the methods specified by Alex Cohen.

On running $readelf -d test to executable generated by NDK the output is as follows:

(...)
 0x00000004 (HASH)                       0x41c
 0x00000001 (NEEDED)                     Shared library: [/home/rohith/example2/hello/obj/local/armeabi-v7a/libcal.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]
(...)

The shared library's path got appended to its name. Thus inorder to change that to just libcal.so I used patchelf.

Here are the commands I used:

$patchelf --remove-needed /home/rohith/example2/hello/obj/local/armeabi-v7a/libcal.so test

$patchelf --add-needed libcal.so test

Note: This may not be the perfect fix. As on running readelf -d test after changing the library names I got another field (Not too bothered about it at the moment as the executable works)

rohith@rohith-Lenovo-G50-80:~/example2/hello/libs/armeabi-v7a$ readelf -d test

Dynamic section at offset 0x4000 contains 32 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libcal.so]
 0x0000001d (RUNPATH)                    Library runpath: [/home/rohith/example2/hello/obj/local/armeabi-v7a/libcal.so:./libcal.so]
 0x00000003 (PLTGOT)                     0x2fcc
 0x00000002 (PLTRELSZ)                   80 (bytes)
 0x00000017 (JMPREL)                     0x50c
 0x00000014 (PLTREL)                     REL
 0x00000011 (REL)                        0x4ac
 0x00000012 (RELSZ)                      96 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffa (RELCOUNT)                   10
 0x00000015 (DEBUG)                      0x0
 0x00000006 (SYMTAB)                     0x224
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000005 (STRTAB)                     0x4130
 0x0000000a (STRSZ)                      347 (bytes)
 0x00000004 (HASH)                       0x41c
 0x00000001 (NEEDED)                     Shared library: [libc.so]
 0x00000001 (NEEDED)                     Shared library: [libm.so]

That extra field type RUNPATH got added