I am trying to link my c-program to a shared object library (libfoo.so) using the ARM cross-compiler arm-linux-gnueabi-gcc. I am compiling on a Ubuntu system, and I want to run the program on an Android device. The compiling works, but when I try to run the program on my android device I get an error.
I've created a simple test program containing the following files:
foo.c:
#include <stdio.h>
void foo(void){ puts("Hello, I am a shared library"); }
foo.h:
#ifndef foo_h__
#define foo_h__
extern void foo(void);
#endif
main.c:
#include <stdio.h>
#include "foo.h"
int main(void)
{
puts("This is a shared library test...");
foo();
return 0;
}
I have then created the shared object library using:
arm-linux-gnueabi-gcc -c -fPIC foo.c
arm-linux-gnueabi-gcc -shared -o libfoo.so foo.o
I then compile my program using:
arm-linux-gnueabi-gcc -L/home/foo -o test main.c -lfoo
After uploading the test-program to the Android device using adb, I am not able to run it. Instead I get the error: /system/bin/sh: ./test: No such file or directory
I am in the right directory and the test-file is present, so I assume that it is the shared library that cannot be found. I've tried uploading libfoo.so to the android device as well (to the same path as specified when compiling), but it still doesn't work to run the program.
I've gotten it to work with a static library (foo.o) using arm-linux-gnueabi-gcc -static -o test main.c foo.o, but not with a shared library.
How do I properly link a shared library when cross-compiling, to make sure that the program can then run on an Android device?
The linker isn't able to dynamically link to the library.
Define LD_LIBRARY_PATH environment variable to include the library:
export LD_LIBRARY_PATH=/home/foo
I made a simple android executable with CMake that links with the native zlib on the ndk. Everything compiles correctly, but when calling deflateInit I get a segmentation fault.
Here is the code:
main.cpp
#include <iostream>
#include <zlib.h>
int main()
{
z_stream strm;
deflateInit(&strm, Z_DEFAULT_COMPRESSION);
std::cout << "it works!" << std::endl;
}
And the corresponding CMakeLists.txt
cmake_minimum_required(VERSION 3.8)
project(zlib-on-android)
add_executable(main main.cpp)
find_package(ZLIB REQUIRED)
set_target_properties(main
PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_link_libraries(main PUBLIC ZLIB::ZLIB)
I then compile with the following command:
cmake -H. -Bbuild -DCMAKE_SYSTEM_NAME=Android -DCMAKE_ANDROID_NDK=~/android-ndk-r17b -DCMAKE_ANDROID_STL_TYPE=c++_static -DCMAKE_SYSTEM_VERSION=16 -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang
Everything works fine. Then I do the following:
cd build
adb push main /data/local/tmp/.
adb shell
shell#device:/ $ cd /data/local/tmp
shell#device:/ $ ./main
[1] + Stopped (signal) ./main
Does anyone know the reason? I am having a lot of trouble trying to hook up gdb with this executable. Since the same code works on my Macosx, I cannot understand why it does not work on android.
EDIT
For some reason the same code crashes on Macosx (the one that worked was a similar code). This is the error: bus error ./main
Well, turns out that setting z_stream to zero fixes the problem.
#include <iostream>
#include <zlib.h>
#include <cstdlib>
int main()
{
z_stream strm;
std::memset(&strm, 0, sizeof(z_stream));
deflateInit(&strm, Z_DEFAULT_COMPRESSION);
std::cout << "it works!" << std::endl;
}
I have .so file which I take from apk. With command nm -D libnative-lib.so I took list of functions. I need to call function named Java_com_example_nativelib_utils_Constants_getStringFromJNI . I wrote the code below:
#include <stddef.h>
#include <dlfcn.h>
#include <stdio.h>
int init_library() {
void* hdl = dlopen("./libnative-lib.so", RTLD_LAZY);
if(hdl == 0)
return 0;
else return 1;
}
int main() {
printf("%s\n", (init_library() ? "OK": "ERROR"));
}
and make file:
all: exe
exe: main.c
gcc main.c -fPIC -ldl -o main
but result of program is always "ERROR". I tried libnative-lib.so from x86 and armeabi-v7a architectures (others not present). How to load library and call function from it? C++ or C do not care.
It is not possible to use android .so files in non-android project. similar question is here
All discussions are for x86.
If I wrote a simple hello program such as the one below:
#include <stdio.h>
int main(){
printf("Hello\n");
return 0;
}
And compile it on my PC with ubuntu
$gcc -shared -mPIC -o hello_new hello.c
Then it will give me segmentation fault when I try to execute hello_new. Same error when I move this binary to an android phone. (But I can compile it as a binary with statically linked libc and run it on the android phone)
Yes, I want to execute a shared object directly.
The reason is below:
I recently get a linux file compiled by someone else. When I use linux command file and readelf to analyze the file. It says it is a shared object (32-bit, compiled with -m32). But I can execute the shared object like an executable in android on a phone:
$./hello
This really confuses me. This shared object file contains printf function calls, not sure if it is statically link or dynamically linked. But since it can run on Android through ADB, I assume it is statically linked against libc.
What kind of compilation technique can allow one to execute shared object directly?
It happens that I am currently working on this type of thing.
One of the main differences between executables and shared object under linux, is that an executable has an interpreter and a (valid) entry point.
For example, on a minimal program :
$ echo 'int main;' | gcc -xc -
If you look at it's elf program headers:
$ readelf --program-headers a.out
...
INTERP 0x0000000000000200 0x0000000000400200 0x0000000000400200
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
...
The interpreter program is responsible of the execution of the program, to achieve this, it will perform some initializations like loading the needed shared objects. In fact, it is quite analogous to a script shebang, but for elf files.
In this case, /lib64/ld-linux-x86-64.so.2 is the loader for amd64. You can have multiples loaders: e.g., one for 32bits, one for 64.
Now the entry point :
$ readelf --file-header a.out
ELF Header:
...
Entry point address: 0x4003c0
...
$ readelf -a a.out | grep -w _start
57: 00000000004003c0 0 FUNC GLOBAL DEFAULT 13 _start
By default, you can see that _start is defined as the entry point.
So if you consider the following minimal example :
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef INTERPRETER
const char interp[] __attribute__((section(".interp"))) = INTERPRETER;
#endif /* INTERPRETER */
void entry_point(void) {
fprintf(stderr, "hello executable shared object world !\n");
_exit(EXIT_SUCCESS);
}
If you compile it as a "normal" shared object and execute it :
$ gcc libexecutable.c -Wall -Wextra -fPIC -shared -o libexecutable.so
$ ./libexecutable.so
Erreur de segmentation
You can see it segfaults. But now if you define an interpreter (adapt it's path to what readelf --program-headers gave you before) and tell to the linker what is your entry point :
$ gcc libexecutable.c -Wall -Wextra -fPIC -shared -o libexecutable.so -DINTERPRETER=\"/lib64/ld-linux-x86-64.so.2\" -Wl,-e,entry_point
$ ./libexecutable.so hello executable shared object world !
now it works. Please note that the _exit() call is necessary to avoid a segfault at the end of the execution.
But in the end, remember that because you specify a custom entry point, you will bypass libc initialization steps which could be needed or not, depending on your needs.
I think your android and pc boths are x86 or arm at the same time, else executable should not run in both platform. Now to make a shared library executable at the same time you can use -pie command line option of gcc. Details can be found in this answer.
I am trying to compile Lame sound library with Android NDK for x86_64 architecture. I am getting the below link error for undefined references to bcopy and index:
jni/libmp3lame/encoder.c:471: error: undefined reference to 'bcopy'
jni/libmp3lame/encoder.c:476: error: undefined reference to 'bcopy'
jni/libmp3lame/id3tag.c:1125: error: undefined reference to 'index'
jni/libmp3lame/newmdct.c:1036: error: undefined reference to 'bcopy'
jni/libmp3lame/util.c:685: error: undefined reference to 'bcopy'
The code successfully compiles for x86 and arm architectures.
So I digged through NDK's libs a bit and noticed that bcopy and index are both exported in libc.so for x86 and arm platforms but not for x86_64 (see below objdump outputs).
$> objdump -d android-ndk-r10d/platforms/android-21/arch-arm/usr/lib/libc.so | grep bcopy -A 6
0000b000 <bcopy>:
b000: e52db004 push {fp} ; (str fp, [sp, #-4]!)
b004: e28db000 add fp, sp, #0
b008: e28bd000 add sp, fp, #0
b00c: e8bd0800 ldmfd sp!, {fp}
b010: e12fff1e bx lr
$> objdump -d android-ndk-r10d/platforms/android-21/arch-x86/usr/lib/libc.so | grep -A 6 bcopy
00009fb0 <bcopy>:
9fb0: 55 push %ebp
9fb1: 89 e5 mov %esp,%ebp
9fb3: 5d pop %ebp
9fb4: c3 ret
$> objdump -d android-ndk-r10d/platforms/android-21/arch-x86_64/usr/lib/libc.so | grep -A 6 bcopy
<<NOTHING FOUND>>
Any thoughts? Below are my Android.mk and Application.mk files.
Application.mk:
APP_ABI:=x86_64
APP_PLATFORM := android-21
Android.mk:
LOCAL_PATH := $(call my-dir)
APP_PLATFORM := android-21
include $(CLEAR_VARS)
LOCAL_MODULE := libmp3lame
LOCAL_SRC_FILES := \
...<list-of-.c-files>...
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)
You can fix this cleanly with a single line in Application.mk (docs):
APP_CFLAGS += -DSTDC_HEADERS
Why?
LAME assumes that certain symbols will be accessible without explicit inclusion via #include. However, it also provides a way to signal that explicit inclusion is necessary.
In my distribution, the conflictive files (machine.h and id3tag.c) have something like this:
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <string.h>
#endif
This is the block you need to trigger, by setting the STDC_HEADERS preprocessor variable. The line above, with the -D flag, tells the C compiler to create it.
To fix bcopy issue, I added #include <strings.h> in machine.h and id3tag.h.
To fix index issue, I ended up commenting out the #define strchar index line in both machine.h and id3tag.c:
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <string.h>
#else
# ifndef HAVE_STRCHR
//# define strchr index
# define strrchr rindex
# endif
char *strchr(), *strrchr();
# ifndef HAVE_MEMCPY
# define memcpy(d, s, n) bcopy ((s), (d), (n))
# define memmove(d, s, n) bcopy ((s), (d), (n))
# endif
#endif
I have it in strings.h, but its a #define:
$ cd /opt/android-ndk-r10d
$ grep -R bcopy * | grep x86_64
platforms/android-21/arch-x86_64/usr/include/linux/mroute6.h:#define IF_COPY(f, t) bcopy(f, t, sizeof(*(f)))
platforms/android-21/arch-x86_64/usr/include/strings.h:#define bcopy(b1, b2, len) \
platforms/android-21/arch-x86_64/usr/include/strings.h:#define bcopy(b1, b2, len) (void)(__builtin_memmove((b2), (b1), (len)))
Here's what it looks like (taken from the header strings.h):
#if defined(__BIONIC_FORTIFY)
# define bcopy(b1, b2, len) \
(void)(__builtin___memmove_chk((b2), (b1), (len), __bos0(b2)))
# define bzero(b, len) \
(void)(__builtin___memset_chk((b), '\0', (len), __bos0(b)))
#else
# define bcopy(b1, b2, len) (void)(__builtin_memmove((b2), (b1), (len)))
# define bzero(b, len) (void)(__builtin_memset((b), '\0', (len)))
#endif
Earlier version of the Android runtime provided it as a library call. See, for example, How to understand this code snippet in the bcopy.c of bionic?.
It sounds like its another case of the headers changing at android-21. That is, its a function that used to present as an export in a library, but now available in a headers. See, for example, Cannot load library: reloc_library[1285]: cannot locate 'rand'.
I think the workaround is to re-compile the Lame sound library with android-21, and not an earlier version of the toolchain.
Also, there are various config.h that have the following comment:
/* HAS_BCOPY:
* This symbol is defined if the bcopy() routine is available to
* copy blocks of memory.
*/
#define HAS_BCOPY /**/
You can find the config.h at, for example, android-ndk-r10d/prebuilt/darwin-x86_64/lib/perl5/5.16.2/darwin-2level/CORE/config.h.
If its not something obvious (like you already are compiling under android-21 and arch is correct), then we'll need to see how your project is setup (like what does Application.mk look like, or what --sysroot is being used).