How is GNU libc.so both a shared object and a standalone executable?

On Linux, the shared library of the GNU C standard library (libc.so) is not only a shared library, but can also be run as a standalone executable file that prints version information:

[ me@computer ~]$ /lib/libc.so.6 GNU C Library stable release version 2.12, by Roland McGrath et al. Copyright (C) 2010 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Compiled by GNU CC version 4.4.7 20120313 (Red Hat 4.4.7-11). Compiled on a Linux 2.6.32 system on 2015-01-07. Available extensions: The C stubs add-on version 2.1.2. crypt add-on version 2.1 by Michael Glad and others GNU Libidn by Simon Josefsson Native POSIX Threads Library by Ulrich Drepper et al BIND-8.2.3-T5B RT using linux kernel aio libc ABIs: UNIQUE IFUNC For bug reporting instructions, please see: <http://www.gnu.org/software/libc/bugs.html>. 

How did they do it? I tried to create a shared library that also had a main() function, but it was interrupted when I tried to run it.

What I tried:

 /* myso.c */ #include <stdlib.h> #include <stdio.h> static void foo(void) { printf("hello foo\n"); } int main(int argc, char *argv[]) { printf("hello world\n"); foo(); return 0; } 

Then:

 $ gcc -Wall -fPIC -shared -o myso.so myso.c $ ./myso.so Segmentation fault (core dumped) 

As a note: I really don't want to do this for any real purpose, I just want to know how the guys from GNU (and gals) did it.

+6
source share
1 answer

In the Makerules of glibc file:

 # Give libc.so an entry point and make it directly runnable itself. LDFLAGS-c.so += -e __libc_main 

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 #include <unistd.h> char const __invoke_dynamic_linker__[] __attribute__ ((section (".interp"))) #ifdef __LP64__ = "/lib64/ld-linux-x86-64.so.2"; #else = "/lib/ld-linux.so.2"; #endif void _start(void) { static char const msg[] = "Hello world!\n"; write(STDOUT_FILENO, msg, sizeof msg - 1); _exit(0); } 

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); } 
+4
source

Source: https://habr.com/ru/post/988452/


All Articles