Thursday, June 12, 2008

How to run a shared library on Linux

In my prevoius blog I have written how to run the shared libraries on Open-Solaris.
http://bhushanverma.blogspot.com/2008/06/how-to-run-shared-library-on-open.html

Shared object should have following entries to run:
1. +x permission that is by default is given by the static linker(program linker) when creating a shared object.
2. Entry point at which the program/shared library is starts to run.
3. Interpreter(Run time linker) that is used to run any shared library after loaded by kernel part exec().

Entry point at which the program/shared library is starts to run can be
given by passing -Wl,-e entry_point to the linker at command line:

To create .interp section by using GNU gcc, use the follwing line of code on linux:
const char my_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";

Where /lib/ld-linux.so.2 is the path of interpreter(Run time linker)  in linux.

In open solaris we passed -Wl,-I,/usr/lib/ld.so.1 to the sun linker to create this section.
I think in gnu linker this option is available but do other things.

Demo on Linux machine:
-------------------------
$ cat func.c
const char my_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
#include
void bar();

int
func()
{
printf("Hacking\n");
bar();
exit (0);
}

void
bar()
{
printf("Bye...\n");
}

$ gcc -fPIC -o func.so -shared -Wl,-e,func func.c

You can see that foo.so have .interp section and interp program header.
# readelf -l func.so
Elf file type is DYN (Shared object file)
Entry point 0x4dc
There are 7 program headers, starting at offset 52

Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00000034 0x00000034 0x000e0 0x000e0 R E 0x4
INTERP 0x0005a3 0x000005a3 0x000005a3 0x00013 0x00013 R 0x1
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x000000 0x00000000 0x00000000 0x005bc 0x005bc R E 0x1000
LOAD 0x0005bc 0x000015bc 0x000015bc 0x00104 0x0010c RW 0x1000
DYNAMIC 0x0005d4 0x000015d4 0x000015d4 0x000c0 0x000c0 RW 0x4
NOTE 0x000114 0x00000114 0x00000114 0x00024 0x00024 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x4

Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .interp .eh_frame
03 .ctors .dtors .jcr .data.rel.ro .dynamic .got .got.plt .bss
04 .dynamic
05 .note.gnu.build-id
06

You can cleary see, func.so have .interp section and
INTERP program header.
Now try to run func.so:
$ ./func.so
Hacking
Bye...


hua.. thats cool.
Happy hacking.

2 comments:

woja said...

Hello Mr Bhushan,

I am interested in a program that should be able to run stand alone
but should also be integrated as a library into a larger program.
Precisely, it is some sort of debugger, dbx and gdb and their clients
serve as the prototypes of the approach. In contrast to dbx and gdb
the client will not run as separate process. The client is
the program that should be able to run stand alone as well
should be callable from the debugger, and I have no control about the
design of the client (at best I can force the creation of a shared
library instead of an executable).

So I'm very interested in the work you did (in fact it seems to be
rather singular, a Google search did not offer more proposals).
The Sun version works very well but not the Linux version,
probably your version from 2008 did not follow all developments of Linux.

Here is the output when running your example on a
"Linux ubuntu 3.8.0-21-generic x86_64" platform:
bash: ./func.so: Accessing a corrupted shared library

And this is the output of "readelf -l func.so":

Elf file type is DYN (Shared object file)
Entry point 0x7e0
There are 9 program headers, starting at offset 64

Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000830 0x0000000000000830 0x0000000000000830
0x0000000000000013 0x0000000000000013 R 10
[Requesting program interpreter: /lib/ld-linux.so.2]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x00000000000008ec 0x00000000000008ec R E 200000
LOAD 0x0000000000000e00 0x0000000000200e00 0x0000000000200e00
0x0000000000000248 0x0000000000000250 RW 200000
DYNAMIC 0x0000000000000e18 0x0000000000200e18 0x0000000000200e18
0x00000000000001c0 0x00000000000001c0 RW 8
NOTE 0x0000000000000238 0x0000000000000238 0x0000000000000238
0x0000000000000024 0x0000000000000024 R 4
GNU_EH_FRAME 0x0000000000000844 0x0000000000000844 0x0000000000000844
0x0000000000000024 0x0000000000000024 R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 8
GNU_RELRO 0x0000000000000e00 0x0000000000200e00 0x0000000000200e00
0x0000000000000200 0x0000000000000200 R 1

Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .interp .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got


So what can I do to make the example (and then my program) running?
Unfortunately, my working place is the Linux machine
(I can use the Sun as guest only) and my knowledge of the ELF syntax
and semantics is very poor.

With regards
Wolfgang Jansen

Bhushan said...

cool. 32 bit and 64 bit version of run time linker are different. best way is you should build it under 64 bit compilation environment.