APPROFONDIMENTI

E' possibile usare un semplicissimo programma per capire il meccanismo di base del load di un'esegubile da parte del
sistema operativo.

$ gcc -o main main.c
$ objdump -f main

main:     file format elf64-x86-64
architecture: i386:x86-64, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0000000000400400

cosa e' esattamente quello start address puo' essere compreso usando l'objdump.

$ objdump --disassemble main

...cut

0000000000400370 <_start>:
  400370:       31 ed                   xor    %ebp,%ebp
  400372:       49 89 d1                mov    %rdx,%r9
  400375:       5e                      pop    %rsi
  400376:       48 89 e2                mov    %rsp,%rdx
  400379:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
  40037d:       50                      push   %rax
  40037e:       54                      push   %rsp
  40037f:       49 c7 c0 40 04 40 00    mov    $0x400440,%r8
  400386:       48 c7 c1 50 04 40 00    mov    $0x400450,%rcx
  40038d:       48 c7 c7 28 04 40 00    mov    $0x400428,%rdi
  400394:       e8 c7 ff ff ff          callq  400360 <__libc_start_main@plt>
  400399:       f4                      hlt    
  40039a:       90                      nop    
  40039b:       90                      nop    

cut...

Questa e' il punto di "aggancio" del loader, "_start" si trova appunto all starting address 0000000000400370. Senza scendere
troppo nei dettagli e' interessante notare che tra i vari indirizzi che vengono "chiamati in gioco" da tale funzione c'e' 
anche quello della funzione main 0x400428:

0000000000400428 <main>:
  400428:       55                      push   %rbp
  400429:       48 89 e5                mov    %rsp,%rbp
  40042c:       b8 00 00 00 00          mov    $0x0,%eax
  400431:       c9                      leaveq 
  400432:       c3                      retq   
  400433:       90                      nop    
  400434:       90                      nop    
  400435:       90                      nop    
  400436:       90                      nop    
  400437:       90                      nop    
  400438:       90                      nop    
  400439:       90                      nop    
  40043a:       90                      nop    
  40043b:       90                      nop    
  40043c:       90                      nop    
  40043d:       90                      nop    
  40043e:       90                      nop    
  40043f:       90                      nop    

Altra cosa interessate, le due istruzioni:

<pre>
400375:       5e                      pop    %rsi
400376:       48 89 e2                mov    %rsp,%rdx
</pre>

servono a prendere i valori di argc ed argv.

Senza dettagliare ulteriormente vediamo descrittivamente cosa succede durante l'eseciuzione di un "programma":

    - La shell chiama ls system call del kernel "execve" passando argc/argv.

    - Il kernel una volta stabilito il tipo di eseguibile, ad esempio a.out o elf, da luogo ai setup del caso ed all'allocazione
      delle pagine di memoria necessarie (text/data/bss/stack). Preprara ed inizializza tutte le strutture dati necessarie, 
      prepara tutte le informazioni necessarie all'eseguibile nello stack.
    - A questo punto e' la "_start" dell'eseguibile che prende il controllo dell'esecuzione. Come prima cosa recupera i valori
      di argc ed argv. Prepara alcune informazioni necesserie nello stack e chiama la __libc_start_main.
    - __libc_start_main fa altre inizializzazioni e chiama il nostro main.
