0

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?

drdot
  • 3,215
  • 9
  • 46
  • 81
  • Is your `gcc` set to compile for ARM? I don't think you can port x86 executables onto say a ARMv7a device and expect it to work. Try it on an x86 android device (the emulator for example) or try compiling for the target processor's architecture. – initramfs May 09 '15 at 05:36
  • @CPUTerminator, both are x86. – drdot May 09 '15 at 07:10
  • Well, I don't have much solid ground to go on, perhaps gcc is using unsupported instructions/extensions not on Android. Take a look at this [link](http://shareprogrammingtips.com/c-language-programming-tips/cross-compile-cc-based-programs-run-android-smart-phones/) whilst method 1 seems to be identical to how you did it, it is targeting the ARM architecture. Perhaps try method 2 and/or 3? – initramfs May 09 '15 at 07:17
  • @CPUTerminator, what extra information do you need? – drdot May 09 '15 at 18:04
  • Sorry I've phrased that badly. I meant to say I don't have much more in my head (as in I lack knowledge in this particular aspect/topic). The information you've provided is quite complete. Check out the link I've sent, it's describing (what I think) exactly what you want to do (but for ARM, should be the same for x86). – initramfs May 09 '15 at 18:06
  • "When you prepare your app f̵o̵r̵ ̵r̵e̵l̵e̵a̵s̵e̵, you must specify which platform CPU architectures your app supports. A single `APK` can contain **machine code** to support *multiple*, *different architectures*". See [CPU Architectures](https://docs.microsoft.com/en-us/xamarin/android/app-fundamentals/cpu-architectures?tabs=windows) and. `abiFilters "armeabi" , "mips", "x86 "` ...[abis](https://developer.android.com/ndk/guides/abis). shared object files (.so) are NOT executables, otherwise they would not be named such, even though they contain machine code("CPU responds to m/c *directly*."). – Jon Goodwin Oct 18 '19 at 22:54

2 Answers2

3

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.

ncarrier
  • 433
  • 3
  • 14
0

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.

Community
  • 1
  • 1
sabbir
  • 438
  • 3
  • 11
  • I am not asking how to generate shared object. I am asking compile shared object such that it can execute directly. – drdot May 09 '15 at 07:12