Here is a minimal example for an "executable" shared library (assumed file name: mini.c
):
// Interpreter path is different on some systems
//+definitely different for 32-Bit machines
const char my_interp[] __attribute__((section(".interp")))
= "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2";
#include <stdio.h>
#include <stdlib.h>
int entry() {
printf("WooFoo!\n");
exit (0);
}
If one compiles it with e.g.: gcc -fPIC -o mini.so -shared -Wl,-e,entry mini.c
. "Running" the resulting .so
will look like this:
confus@confusion:~$ ./mini.so
WooFoo!
My question is now:
How do I have to change the above program to pass command line arguments to a call of the .so
-file? An example shell session after the change might e.g. look like this:
confus@confusion:~$ ./mini.so 2 bar
1: WooFoo! bar!
2: WooFoo! bar!
confus@confusion:~$ ./mini.so 3 bla
1: WooFoo! bla!
2: WooFoo! bla!
3: WooFoo! bla!
5: WooFoo! Bar!
It would also be nice to detect on compile time, wheter the target is a 32-Bit or 64-Bit binary to change the interpreter string accordingly. Otherwise one gets a "Accessing a corrupted shared library" warning. Something like:
#ifdef SIXTY_FOUR_BIT
const char my_interp[] __attribute__((section(".interp"))) = "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2";
#else
const char my_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
#endif
Or even better, to detect the appropriate path fully automatically to ensure it is right for the system the library is compiled on.
Add
to the top of your
entry
function. On x86_64 platforms, this will give you access to the arguments.The LNW article that John Bollinger linked to in the comments explains why this code works. It might interest you why this is not required when you write a normal C program, or rather, why it does not suffice do just give your
entry
function the two usualint argc, char **argv
arguments: The entry point for a C program normally is not themain
function, but instead an assembler function by glibc that does some preparations for you - among others fetch the arguments from the stack - and that eventually (via some intermediate functions) calls yourmain
function. Note that this also means that you might experience other problems, since you skip this initialization! For some history, the cdecl wikipedia page, especially on the difference between x86 and x86_64, might be of further interest.When you run your shared library,
argc
andargv
will be passed to your entry function on the stack.The problem is that the calling convention used when you compile your shared library on x86_64 linux is going to be that of the System V AMD64 ABI, which doesn't take arguments on the stack but in registers.
You'll need some ASM glue code that fetches argument from the stack and puts them into the right registers.
Here's a simple .asm file you can save as entry.asm and just link with:
That code copies the arguments from the stack into the appropriate registers, and then calls your
entry
function in a position-independent way.You can then just write your
entry
as if it was a regularmain
function:And this is how you would then compile your library:
The advantage is that you won't have inline asm statements mixed with your C code, instead your real entry point is cleanly abstracted away in a start file.
Unfortunately, there's no completely clean, reliable way to do that. The best you can do is rely on your preferred compiler having the right defines.
Since you use GCC you can write your C code like this:
And have two different start files.
One for 64bit:
And one for 32bit:
Which means you now have two slightly different ways to compile your library for each target.
For 64bit:
And for 32bit:
So to sum it up you now have two start files
entry.asm
andentry32.asm
, a set of defines in yourmini.c
that picks the right interpreter automatically, and two slightly different ways of compiling your library depending on the target.So if we really want to go all the way, all that's left is to create a Makefile that detects the right target and builds your library accordingly.
Let's do just that:
And we're done here. Just run
make
to build your library and let the magic happen.