In the Makerules of glibc file:
And in csu/version.c
extern void __libc_print_version (void); void __libc_print_version (void) { __write (STDOUT_FILENO, banner, sizeof banner - 1); }
This is called __libc_main() .
A program interpreter is added to the shared object in the elf/interp.c file.
This works for me on 64-bit Linux: gcc -Wall -Wextra -fPIC -shared -o myso.so myso.c
//myso.c
The __invoke_dynamic_linker__[] necessary because without it it is not possible to dynamically bind write and _exit . The name of this variable is not important. There is an option for ld , -dynamic-linker , which will do the same, but it is ignored with -shared , but setting the line to the source code works.
Here is a non-dynamic link option:
#include <unistd.h> #include <sys/syscall.h> __asm__( #ifdef __LP64__ "syscall: mov %rdi,%rax;" " mov %rsi,%rdi;" " mov %rdx,%rsi;" " mov %rcx,%rdx;" " mov %r8,%r10;" " mov %r9,%r8;" " mov 0x8(%rsp),%r9;" " syscall;" " cmp $0xfffffffffffff001,%rax;" " jae __syscall_error;" " retq; " "__syscall_error: neg %rax;" " mov %eax,%fs:0xffffffffffffffd0;" " or $0xffffffffffffffff,%rax;" " retq;" #else "syscall: push %ebp;" " push %edi;" " push %esi;" " push %ebx;" " mov 0x2c(%esp),%ebp;" " mov 0x28(%esp),%edi;" " mov 0x24(%esp),%esi;" " mov 0x20(%esp),%edx;" " mov 0x1c(%esp),%ecx;" " mov 0x18(%esp),%ebx;" " mov 0x14(%esp),%eax;" " int $0x80;" " pop %ebx;" " pop %esi;" " pop %edi;" " pop %ebp;" " cmp $0xfffff001,%eax;" " jae __syscall_error;" " ret;" "__syscall_error: neg %eax;" " mov %gs:0x0,%ecx;" " mov %eax,-0x18(%ecx);" " mov $0xffffffff,%eax;" " ret;" #endif ); void _start(void) { static char const msg[] = "Hello world!\n"; syscall(SYS_write, STDOUT_FILENO, msg, sizeof msg - 1); syscall(SYS_exit, 0); }