| Dynamic-link library | |
|---|---|
| Magic number | MZ
|
| Tipo MIME | application/vnd.microsoft.portable-executable
|
| Uniform Type Identifier (UTI) | com.microsoft.windows-dynamic-link-library
|
| Sviluppatore | Microsoft |
| Tipo | Libreria condivisa |

In informatica il dynamic-link library (in acronimo DLL, lett. "libreria a collegamento dinamico") è un formato di libreria condivisa utilizzato nei sistemi operativi di Microsoft.
In Windows, una libreria dinamica è, a tutti gli effetti, un codice eseguibile. Ogni file eseguibile (EXE o DLL) dispone di un punto d'ingresso (entry point) che viene invocato dal sistema operativo subito dopo il caricamento. Per una DLL, il punto d'ingresso è mappato per convenzione sulla funzione DllMain (a discrezione, comunque, del compilatore).
La funzione DllMain, oltre che al momento del caricamento della DLL, viene invocata anche allo scaricamento, oppure quando un thread viene creato o distrutto nel processo in cui la DLL risiede.
A differenza di un file EXE, la DLL deve uscire dall'entry point non appena ha terminato le inizializzazioni necessarie.
Per semplificare, una libreria può essere pensata come una raccolta di funzioni. Ognuna di queste funzioni avrà il proprio indirizzo di base, calcolato come offset rispetto all'indirizzo di base assegnato dal sistema operativo durante il caricamento della libreria (vedi paragrafo suk caricamento). Ciò che distingue una libreria dinamica è che queste funzioni possono essere esportate, ovvero i loro nomi vengono posti in una lista in una sezione dell'eseguibile. Perciò, è possibile determinare il punto di ingresso di una funzione con una ricerca testuale basata sul nome della funzione. Questa operazione è svolta dall'API GetProcAddress che restituisce l'indirizzo della funzione il cui nome gli sia stato passato come parametro.
Le librerie dinamiche vengono caricate dal sistema operativo all'interno dello spazio di memoria del processo che le ha richieste. In questo modo l'accesso al codice della DLL avrà prestazioni quasi equivalenti a quelle del codice dell'applicazione stessa o del codice delle librerie statiche (salvo un piccolo aggravio dovuto a delle macchinosità e a un overhead, di cui si dirà in seguito nelle sezioni sul collegamento esplicito e implicito).
Per evitare che il codice dell'applicazione e quello della DLL occupino la stessa posizione in memoria, il linker dovrà predisporre la DLL per la rilocazione. In pratica, il sistema operativo determina un'area di memoria disponibile e rimappa ogni riferimento alla memoria contenuto nel codice della DLL. Siccome quest'operazione richiede tempo, ogni DLL dispone di un proprio indirizzo di base ideale: la rilocazione sarà necessaria solo se a questo indirizzo predeterminato è già stata mappata una precedente DLL. Per specificare l'indirizzo ideale si può usare una regola empirica, basata sulla lettera iniziale del nome della DLL, secondo la seguente tabella:
| Lettera iniziale | Indirizzo di base |
|---|---|
| A–C | 0x60000000
|
| D–F | 0x61000000
|
| G–I | 0x62000000
|
| J–L | 0x63000000
|
| M–O | 0x64000000
|
| P–R | 0x65000000
|
| S–U | 0x66000000
|
| V–X | 0x67000000
|
| Y–Z | 0x68000000
|
Il collegamento di un eseguibile a una libreria dinamica avviene durante l'esecuzione (a run time) e avviene tramite l'API LoadLibrary, che accetta in input il nome della libreria. Ad esempio, LoadLibrary(_T("MyLib.dll")) caricherà all'interno dello spazio di memoria dell'applicazione la DLL MyLib.dll.
Il collegamento può essere di due tipi: esplicito o implicito.
Il collegamento esplicito viene gestito direttamente dal codice del programma con l'utilizzo delle due API LoadLibrary e GetProcAddress descritte in precedenza. Se si usa il linguaggio C, si allocherà un puntatore alla funzione specificata, nel quale, al momento di utilizzare la funzione richiesta, si caricherà l'indirizzo con GetProcAddress. Questa tecnica permette di gestire in modo appropriato la condizione nella quale una DLL richiesta non è presente nel sistema, ma in generale è più macchinosa perché richiede l'utilizzo esplicito delle due API. Questa tecnica è indispensabile quando si usano alcuni linguaggi di programmazione, quali, ad esempio, Visual Basic.
Il collegamento implicito è gestito direttamente dal linker in fase di compilazione, ed è usato quando si assume che una DLL sia sempre presente nel sistema. Ogni volta che nel codice sorgente è richiamata una funzione contenuta in una DLL, il linker collegherà la chiamata a funzione a una funzione stub (ovvero a una funzione fittizia). All'interno dell'eseguibile vi sarà una tabella contenente gli stub a tutte le funzioni di DLL richieste. In fase di caricamento dell'eseguibile, il sistema operativo caricherà in automatico tutte le DLL richieste e mapperà ogni stub al punto di ingresso della relativa funzione nella relativa DLL. Qualora una DLL richiesta (o anche una singola funzione all'interno di una DLL) non dovesse venir trovata, il sistema operativo bloccherà l'avvio del programma con un messaggio di errore.
L'utilizzo del collegamento implicito ha uno svantaggio in termini di prestazioni, perché ogni volta che viene richiamata una funzione contenuta in una DLL vi è un doppio salto a funzione: prima allo stub e poi all'indirizzo della funzione; l'overhead generato è in realtà trascurabile.
Una variante al collegamento implicito prevista da alcuni compilatori è il collegamento ritardato. In questo caso viene utilizzato uno stub speciale, che non viene mappato al momento del caricamento dal sistema operativo. La mappatura di questo stub alla corrispondente funzione della DLL , avverrà, invece, la prima volta in cui verrà invocato (con la tecnica già vista del collegamento esplicito) . Questa tecnica ha il vantaggio di non richiedere la presenza della DLL per il caricamento dell'eseguibile, insieme alla comodità di non dover caricare esplicitamente da codice la libreria.