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.
Related
This question is exploratory in nature, not sure if this fits stack overlflow Q&A.
Context:
I have a library written in golang that I need to compile for multiple services to use.
These services are in Kotlin android,Rust,Golang.
The only option I am aware of is using something like SWIG to compile the go library for different languages.
Problem:
I don't think SWIG works for Kotlin.
I am trying to fish for the best methods to do this and different approaches this can be done.
For any language that can generate a C shared library and header file you can use SWIG to wrap it. Equally for any language that runs within a JVM and can call Java classes you can make use of SWIG's auto generated Java bindings.
With that we can therefore do a sequence of things that looks like this:
Go -> C -> JNI -> Java -> Kotlin
It's actually fairly sane. I've put together an example below for this to show how it works since I was curious having never written Go nor Kotlin before. (Take this with a pinch of salt therefore, I've probably not hit "best practice" for either!)
This example assumes you have a working JDK/JRE, C compiler, Go installation and kotlinc.
My demo.go looks like this:
package main
import (
"C"
"fmt"
)
//export TestGoFunc
func TestGoFunc(str *C.char) *C.char {
fmt.Printf("Got string: %s\n", C.GoString(str))
return nil
}
func main() {}
And hello.kt looks like this:
fun main() {
println("Hello, World!")
test.TestGoFunc("Another string")
}
To wrap this I wrote the following SWIG interface:
%module test
%{
#include "golib.h"
%}
%include <typemaps.i>
%pragma(java) jniclasscode=%{
static {
System.loadLibrary("test");
}
%}
// Assuming you don't care about these in your library silence/neaten stuff
#define _Complex
%ignore _GoString_;
%ignore GoComplex64;
%ignore GoComplex128;
%ignore GoSlice;
%ignore GoInterface;
%include "golib.h"
This is a fairly standard SWIG interface for targeting Java - it hides some of the stuff in the generated header file we don't care about and autoloads the .so file inside Java using a pragma for us.
Then I put together a small Makefile to build everything since there's a bunch of steps to this build:
all: libtest.so hello.jar
golib.so: demo.go
go build -o golib.so -buildmode=c-shared demo.go
test_wrap.c: golib.so test.i
swig3.0 -java -Wall test.i
libtest.so: test_wrap.c
gcc -shared -Wall -Wextra test_wrap.c -o libtest.so ./golib.so -I/usr/lib/jvm/default-java/include/ -I/usr/lib/jvm/default-java/include/linux
hello.jar: hello.kt
javac *.java
kotlinc hello.kt -include-runtime -d hello.jar -cp .
jar uvf hello.jar *.class
If we build and run this then it all works nicely:
$ make
go build -o golib.so -buildmode=c-shared demo.go
swig3.0 -java -Wall test.i
gcc -shared -Wall -Wextra test_wrap.c -o libtest.so ./golib.so -I/usr/lib/jvm/default-java/include/ -I/usr/lib/jvm/default-java/include/linux
javac *.java
kotlinc hello.kt -include-runtime -d hello.jar -cp .
jar uvf hello.jar *.class
adding: test.class(in = 302) (out= 216)(deflated 28%)
adding: testJNI.class(in = 389) (out= 268)(deflated 31%)
$ LD_LIBRARY_PATH=. java -jar hello.jar
Hello, World!
Got string: Another string
I'd be tempted to use -buildmode=c-archive for Go to build a static library and then link that into the SWIG shared object instead just to keep things simpler in that regards though.
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've two files:
lib.c
#include<stdio.h>
void hi() {
printf("Hi i'm a library function in lib.so\n");
}
and main.c
#include<stdio.h>
#include<dlfcn.h>
/* based on Jeff Scudder's code */
int main() {
void *SharedObjectFile;
void (*hi)();
// Load the shared libary;
SharedObjectFile = dlopen("./lib.so", RTLD_LAZY);
// Obtain the address of a function in the shared library.
ciao = dlsym(SharedObjectFile, "hi");
// Use the dynamically loaded function.
(*hi)();
dlclose(SharedObjectFile);
}
And I've tried to build an executables using the following commands:
export LD_LIBRARY_PATH=pwd
gcc -c -fpic lib.c
gcc -shared -lc -o lib.so lib.o
gcc main.c -ldl
And it works pretty well.
Then I've tried to export my program on Android (Nexus One, with ARM-v7-0a arch) using the following commands:
export LD_LIBRARY_PATH=pwd
arm-none-linux-gnueabi-gcc -c -fpic lib.c
arm-none-linux-gnueabi-gcc -shared -lc -o lib.so lib.o
arm-none-linux-gnueabi-gcc main.c -ldl -o main
adb push main /system/app
The result of executing ./main on the correct folder on my smartphone is just:
./main: not found
even if my file is right there!
Am I missing anything during the cross-compile process? Any help?
I'm using the cross-compiler from CodeSourcery and it works well for static programs without .so libraries.
Thanks
EDIT: as Igor states below, that was a linker issue. This command fixes it:
arm-none-linux-gnueabi-gcc -o test main.c -Wl,--dynamic-linker=/system/bin/linker -ldl
in my very case I need other libraries because in /system/lib/ there are no many .so files.
The "not found" message refers not to the shared object but to the dynamic linker. Linux uses /lib/ld-linux.so.2 (or /lib64/ld-linux-x86-64.so.2 for x64) while Android uses /bin/linker. You can check which dynamic loader your program uses with readelf -l, e.g.:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x00100 0x00100 R E 0x4
INTERP 0x000134 0x08048134 0x08048134 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
You can specify a linker to use with ld's --dynamic-linker switch, but there are likely to be other differences. For example, Android uses a stripped-down libc implementation called bionic, and it may be missing functionality that your program relies on, or have different behavior.
You should use NDK or another Android-targeted toolchain when compiling programs for Android. Even though it's based on Linux kernel, the differences are large enough that Linux-targeted toolchains are not sufficient.
I'm experimenting with native libs cross compiled from ubuntu. What I really want is to be able to compile my c++ libraries and use them in a Xamarin.Android app.
First, I have the arm gcc compiler: arm-linux-gnueabi-gcc. I have a simple lib (libmr.so) that has one function void Print(); that prints something to the console. I'm compiling with:
arm-linux-gnueabi-gcc -Wall -shared -o libmr.so mr.c
When inspecting it using file libmr.so everything seems to be good. However when I'm including it with my android app and try to load it, it is as if it doesn't exist. I'm certain it is there, the path is absolutely correct as I tried to load another lib (libmonodroid.so) from the same folder and it worked.
I tried inspecting both libs to find some kind of a difference:
$ objdump -x libmr.so | grep NEEDED
NEEDED libc.so.6
$ objdump -x libmonodroid.so | grep NEEDED
NEEDED libc.so
... (in addition to other libs)
This is the only difference I'm finding between the two. libmonodroid.so loads properly but libmr.so acts as if it doesn't exist. (I'm using dlopen to load a lib)
EDIT:
I built an executable using the same toolchain, gave me a clue:
Static linking with libc: arm-linux-gnueabi-gcc -Wall -o hi source.c -static. Pushed hi to my android devices and executed it with adb. Result: SUCCESS!
Dynamic linking with libc: arm-linux-gnueabi-gcc -Wall -o hi source.c. Result: it's not even there! Meaning ./hi gives /system/bin/sh: ./hi: not found although it's absolutely there.
So, looks like libc is really the culprit? Maybe I need to link dynamically with not libc.so.6 but with libc.so just like libmonodroid.so is doing?
Check this out for anyone having the same problem. Download the ndk, there's a standalone toolchain for building native libs that run on android that you can extract (you won't need the whole ndk).
I was able to run a basic app on ubuntu 15.04 with this Makefile in the same dir as my hi.c:
$ cat hi.c # create hi.c with favorite editor
#include <stdio.h>
int main(int argc, char** argv) {
int uid = getuid();
int eid = geteuid();
printf("Hello world\n");
printf("You are uid: %d and eid; %d", uid, eid);
return 0;
}
$ cat Makefile # create two line Makefile
CC=arm-linux-gnueabi-gcc
LDFLAGS=-static
$ make hi # build arm based hi executable file
arm-linux-gnueabi-gcc -static hi.c -o hi
$ file hi # check file type
hi: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=17b65e60cdd32449ac237bfd1b8238bfa1d416a0, not stripped
$ adb push hi /data/local/tmp # copy to droid fon
4403 KB/s (593252 bytes in 0.131s)
$ adb shell /data/local/tmp/hi # run hi executable
adb shell /data/local/tmp/hi
Hello world
You are uid: 2000 and eid; 2000
$ uname -a
Linux lenny 3.19.0-28-generic #30-Ubuntu SMP Mon Aug 31 15:52:51 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux
Note that I do not have any NDK installed.
I've two files:
lib.c
#include<stdio.h>
void hi() {
printf("Hi i'm a library function in lib.so\n");
}
and main.c
#include<stdio.h>
#include<dlfcn.h>
/* based on Jeff Scudder's code */
int main() {
void *SharedObjectFile;
void (*hi)();
// Load the shared libary;
SharedObjectFile = dlopen("./lib.so", RTLD_LAZY);
// Obtain the address of a function in the shared library.
ciao = dlsym(SharedObjectFile, "hi");
// Use the dynamically loaded function.
(*hi)();
dlclose(SharedObjectFile);
}
And I've tried to build an executables using the following commands:
export LD_LIBRARY_PATH=pwd
gcc -c -fpic lib.c
gcc -shared -lc -o lib.so lib.o
gcc main.c -ldl
And it works pretty well.
Then I've tried to export my program on Android (Nexus One, with ARM-v7-0a arch) using the following commands:
export LD_LIBRARY_PATH=pwd
arm-none-linux-gnueabi-gcc -c -fpic lib.c
arm-none-linux-gnueabi-gcc -shared -lc -o lib.so lib.o
arm-none-linux-gnueabi-gcc main.c -ldl -o main
adb push main /system/app
The result of executing ./main on the correct folder on my smartphone is just:
./main: not found
even if my file is right there!
Am I missing anything during the cross-compile process? Any help?
I'm using the cross-compiler from CodeSourcery and it works well for static programs without .so libraries.
Thanks
EDIT: as Igor states below, that was a linker issue. This command fixes it:
arm-none-linux-gnueabi-gcc -o test main.c -Wl,--dynamic-linker=/system/bin/linker -ldl
in my very case I need other libraries because in /system/lib/ there are no many .so files.
The "not found" message refers not to the shared object but to the dynamic linker. Linux uses /lib/ld-linux.so.2 (or /lib64/ld-linux-x86-64.so.2 for x64) while Android uses /bin/linker. You can check which dynamic loader your program uses with readelf -l, e.g.:
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x08048034 0x08048034 0x00100 0x00100 R E 0x4
INTERP 0x000134 0x08048134 0x08048134 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
You can specify a linker to use with ld's --dynamic-linker switch, but there are likely to be other differences. For example, Android uses a stripped-down libc implementation called bionic, and it may be missing functionality that your program relies on, or have different behavior.
You should use NDK or another Android-targeted toolchain when compiling programs for Android. Even though it's based on Linux kernel, the differences are large enough that Linux-targeted toolchains are not sufficient.