About binary file

Shaun OS support elf binary file loading, both static or dynamic linked. Loading static linked elf binary is quite easy,we do not cover it here.

As we all know,the dynamic library file(.so) is loaded by a userspace program called ld-linux.so.2 which is a soft-link that links to ld-2.*.so in linux. When kernel wants to run a dynamic linked app, the interpreter which is specified in the type of INTERP section of program header is loaded into memory first,then kernel sets the process entry point to interp’s entry point. The interp gets to run first, it locates the binary’s dynamic sections and loads libraries needed, does relocation stuff,.etc.
But in Shaun os, i do not take the same way as described above. All these stuff is done in kernel. The kernel is responsible for loading libraries, doing relocation, and so on. By putting it in kernel phase, we can benefit from two aspects:

1,saving the memory usage.
we do not need to allocate pages for interpreter any more. This may save 240K memories in linux. Take a consideration about this, every dynamic-linked app in linux will need an extra 240K memory usage for its interpreter, that’s a big memory cost when we have a hundred processes at the same time.

2,speed up app’s loading time.
To implement it, we should have a deep understanding of how the traditional interpreter resolve a symbol for an app. let’s take hello world as an example:

int main(int argc, char **argv)
    printf("hello worldn");
    return 0;

the printf() function is implemented in libc. the compiler generates asm codes of main()  like this:

0x08048426 <+9>:	movl   $0x80484d0,(%esp)
0x0804842d <+16>:	call   0x80482f0 <puts@plt>

when we make a printf call, we comes here,0x80482f0<puts@plt>, in fact, we jump to the plt table(procedure linkage table), let’s disassemble 0x80482f0

 disas 0x80482f0,+20
Dump of assembler code from 0x80482f0 to 0x8048304:
   0x080482f0 <puts@plt+0>:	jmp    *0x804a00c
   0x080482f6 <puts@plt+6>:	push   $0x0
   0x080482fb <puts@plt+11>:	jmp    0x80482e0

when we jump to 0x80482f0, we actually jump to *0x804a00c,let’s take a look at the value of 0x804a00c:

(gdb) x/x 0x804a00c
0x804a00c <puts@got.plt>:	0x080482f6

0x804a00c stores the address of 0x80482f6, that’s the address right after 0x80482f0,in 0x80482f6, we just push 0x0 into stack, and jump to 0x80482e0,actually 0x80482e0 is the address of the third entry in got(globe offset table),disassemble it:

(gdb) disas 0x80482e0,+20
Dump of assembler code from 0x80482e0 to 0x80482f4:
   0x080482e0:	pushl  0x804a004
   0x080482e6:	jmp    *0x804a008
   0x080482ec:	add    %al,(%eax)
   0x080482ee:	add    %al,(%eax)
   0x080482f0 <puts@plt+0>:	jmp    *0x804a00c
End of assembler dump.

push 0x804a004, 0x804a004 is the address of the second entry in got, then we jump to the address stored in 0x804a008:

(gdb) x/x 0x804a008
0x804a008:	0xb7ff24f0

when we jump to 0xbfff24f0, we jump to interpreter actually:

(gdb) disas 0xb7ff24f0,+20
Dump of assembler code from 0xb7ff24f0 to 0xb7ff2504:
   0xb7ff24f0 <_dl_runtime_resolve+0>:	push   %eax
   0xb7ff24f1 <_dl_runtime_resolve+1>:	push   %ecx
   0xb7ff24f2 <_dl_runtime_resolve+2>:	push   %edx
   0xb7ff24f3 <_dl_runtime_resolve+3>:	mov    0x10(%esp),%edx
   0xb7ff24f7 <_dl_runtime_resolve+7>:	mov    0xc(%esp),%eax
   0xb7ff24fb <_dl_runtime_resolve+11>:	call   0xb7fec0a0 <_dl_fixup>
   0xb7ff2500 <_dl_runtime_resolve+16>:	pop    %edx
   0xb7ff2501 <_dl_runtime_resolve+17>:	mov    (%esp),%ecx
End of assembler dump.

when we come here, the stack looks like this:

offset(pushed in plt)
module address (pushed in got)

the interpreter then find the symbol based on the symbol index pushed onto stack, if found , it will change the address in plt table,0x80482f0, the old value is a jump to the address stored in 0x804a00c,after relocating, the 0x804a00c stores the real address of function puts():

(gdb) x/x 0x804a00c
0x804a00c <puts@got.plt>:	0xb7e7d190

next time when we call printf(actually puts()), we will jump to puts() in plt table directly, there is no need to jump to got table and interpreter again. Okay, that’s the way traditional unix-like app take, but in Shaun OS, i export a system call sys_elf_resolv function, and setup the GOT table’s third entry to jump to a specific address(i call it elf trampoline), in trampoline asm procedure, just made a system call of sys_elf_resolve, with arguments remains unchanged. kernel will take over the rest.

Leave a Reply

Your email address will not be published. Required fields are marked *