File
Il concetto di 'file' viene originariamente dal Linguaggio C, che è piuttosto antico. Il paradigma è molto potente, ma alquanto datato.
Altri sistemi operativi, come Mac e Windows, che sono scritti in C++, hanno una struttura interna differente dei file.
Un file è una sequenza indeterminata di bytes, uno stream, da zero a teoricamente infiniti, controllato da una struttura di controllo, in C: FILE.
La struttura di controllo si trova nella Per-Process File Table, nella u_area del processo corrente.

Tipi di file
Il sistema Unix nativamente non deduce il tipo di file dal suffisso del suo nome, o da etichette inserite nell'inode o nella entry di directory.
Unix deduce il tipo dai primi caratteri del file stesso, due o quattro o fino ad otto caratteri, che costituiscono l'etichetta o numero magico del file.

UNIX cerca
- il numero magico – primi 2 o 4 byte, che informa sul tipo di file
- il blocco di controllo – n bytes a seguire, dipende dal numero magico: fornisce dettagli sul tipo di file e sulle sue proprietà
Per esempio, tutti i file eseguibili cominciano con il numero magico\01FF E L F, ove \01FF è il primo byte scritto in ottale, e sono tutti bit settati ad uno.
Unix è così vecchio che non avevano ancora l'abitudine di indicare i byte in esadecimale. Si usava ancora la notazione ottale.
Ma per quanto vi sia un solo standard di produrre eseguibili in Unix, vi sono molti dettagli che dipendono dall'architettura specifica:
- 32 o 64 bit
- interi in formato Big Endian (MSB - IBM) o Little Endian (LSB - Intel)
- link statico o dinamico
- tipologia del link dinamico
- versione minima delle System Calls
- se vi sia o no la tabella dei simboli
Tutti questi dettagli sono contenuti nel blocco di controllo, che segue l'etichetta. E' in formato binario e occorre un disassemblatore specifico.
Comando file
Sintassi:
file nomefile
Disassembla il blocco di controllo quando applicato a un file eseguibile, dando informazioni dettagliate a video.
Esempio:
file /usr/bin/passwd
I file eseguibili
Creiamo un file in linguaggio C, editando ciao.c col nostro editor preferito:
#include <stdio.h>
int main(){
printf("Ciao\n");
return 0;
}
Compiliamolo con:
gcc -o ciao ciao.c
L'opzione -o ciao indica che l'eseguibile deve chiamarsi ciao. Senza tale opzione, l'eseguibile si chiamerebbe di default a.out.
Se non vi sono errori nel programma, non vengono dati messaggi.
Verifichiamo che l'eseguibile esiste con ls ciao.
Lanciamolo con ./ciao.
Guardiamo il contenuto del file col dumper ottale (antichissimo):
od -c ciao | less
L'opzione -c indica di tradurre in ASCII tutti i byte stampabili. Il filtro less ci mostra la prima pagina di output. Uscire da less col comando q quando abbiamo finito di ispezionarlo.
Si nota subito l'etichetta (o numero magico): \01FF E L F.
Nota di colore:
E L F in teoria vuol dire Extensible Link Format. In realtà è un silenzioso ringraziamento agli Elfi, senza l'aiuto dei quali, come ogni programmatore di un tempo sapeva, Unix non avrebbe mai visto la luce.
Proviamo a disassemblare il blocco di controllo:
file ciao
Il risultato è:
ciao: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
for GNU/Linux 2.6.32, BuildID[sha1]
=429459a48a14a82e318be5951f8df6f3ecd53e9e, not stripped
Qui not stripped si riferisce alla presenza della tabella dei simboli all'interno del codice. Serve per eventualmente:
- aiutare nel debugging e nel disassemblaggio
- usare il programma corrente come modulo di compilazione di un programma più grande
Per visualizzare la tabella dei simboli il comando è nm ciao:
0000000000601038 B __bss_start
0000000000601038 b completed.7585
0000000000601028 D __data_start
0000000000601028 W data_start
0000000000400460 t deregister_tm_clones
00000000004004e0 t __do_global_dtors_aux
0000000000600e18 t __do_global_dtors_aux_fini_array_entry
0000000000601030 D __dso_handle
0000000000600e28 d _DYNAMIC
...
Se non vogliamo che il file eseguibile sia ulteriormente compilato, o ispezionato, si può togliere la tabella dei simboli, col comando:
strip ciao
Ora il tentativo di ispezionarla fallisce:
$ nm ciao
nm: ciao: no symbols
E il comando file ciao ritorna:
ciao: ELF 64-bit LSB executable, x86-64, version 1 (SYSV),
dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2,
for GNU/Linux 2.6.32, BuildID[sha1]
=429459a48a14a82e318be5951f8df6f3ecd53e9e, stripped
Librerie dinamiche
Se un file è linkato dal compilatore con altre librerie, il link è dinamico di default:
- il codice delle altre librerie non è inserito all'interno del codice dell'eseguibile, solo dei riferimenti ad esse
- tutte le librerie dinamiche di sistema sono registrate ad un preciso indirizzo in Memoria Virtuale dal comando
ldconfiglanciato all'atto del boot di sistema - a runtime, il nostro eseguibile vede automaticamente caricate le librerie con cui è linkato, nel suo spazio di indirizzi virtuali, ovvero nella sua Mappa dei Segmenti.
- programmi diversi che sono linkati con la stessa libreria dinamica, la vedono allo stesso indirizzo virtuale
- la libreria è caricata una sola volta, dal sistema non dall'eseguibile: è uno Shared Object (oggetto condiviso)
- tutti i file di libreria dinamica hanno il suffisso
.so(+ numero di versione)[è un vago equivalente architettonico del.DLLdi Windows]
Se un file è linkato staticamente con delle librerie, all'atto della compilazione con opportune opzioni:
- tutte le funzioni di libreria riferite dal programma hanno il loro codice inserito nel codice del programma eseguibile
- ogni eseguibile statico a runtime le carica nel suo spazio privato di indirizzamento
- questo provoca un notevole spreco di RAM e di disco
- i programmi statici sono però molto più facilmente portabili ad altre istanze di Linux: non c'è da portare anche le librerie
Sia che un file non venga linkato con alcuna libreria, come nel nostro esempio, sia che venga linkato staticamente, ha sempre tre o quattro librerie dinamiche d'ufficio.
Nel nostro esempio le possiamo vedere col comando:
ldd ciao
che produce:
linux-vdso.so.1 => (0x00007fffe8ffe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f07279b0000)
/lib64/ld-linux-x86-64.so.2 (0x0000560915c47000)
Il numero esadecimale tra parentesi è l'indirizzo della libreria in memoria virtuale, preregistrato al boot da ldconfig.
Tali librerie sono:
libc- la libreria delle System Calls (Standard C Library), con cui il programma può compiere richieste di servizi al Kernellinux-vdso- (Virtual Dynamic Shared Object) la libreria di cittadinanza, con cui il programma può interagire con l'intero Ambiente Operativo - contiene dettagli che cambiano abbastanza spesso nelle release di Linuxld-linux- la libreria Loader: il suo compito è di trasformare un file eseguibile in un processo in esecuzione, caricando l'ambiente di schedulazione
Un esempio di file compilato staticamente, in Go, è il seguente:
$ ldd ~/bin/micro
linux-vdso.so.1 => (0x00007ffdc79f0000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0
(0x00007f630e1c6000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f630ddfd000)
/lib64/ld-linux-x86-64.so.2 (0x00005613dff8f000)
In questo esempio vi è anche la libreria libpthread, che dà il supporto al multithreading. Tutti i programmi Go sono multithread.