Poiché le DLL sono essenzialmente le stesse EXE, la scelta di cui produrre come parte del processo di collegamento è per chiarezza, poiché è possibile esportare funzioni e dati da entrambi.
Non è possibile eseguire direttamente una DLL, poiché richiede un EXE per il sistema operativo per caricarlo attraverso un punto di ingresso, da qui l’esistenza di utility come RUNDLL.EXE o RUNDLL32.EXE che forniscono il punto di ingresso e il framework minimo per le DLL che contengono funzionalità sufficienti da eseguire senza molto supporto.,
Le DLL forniscono un meccanismo per codice e dati condivisi, consentendo a uno sviluppatore di codice / dati condivisi di aggiornare le funzionalità senza richiedere che le applicazioni vengano ricollegate o ricompilate. Dal punto di vista dello sviluppo delle applicazioni Windows e OS / 2 possono essere considerati come una raccolta di DLL che vengono aggiornate, consentendo alle applicazioni per una versione del sistema operativo di funzionare in una successiva, a condizione che il fornitore del sistema operativo abbia assicurato che le interfacce e le funzionalità siano compatibili.,
Le DLL vengono eseguite nello spazio di memoria del processo chiamante e con le stesse autorizzazioni di accesso, il che significa che c’è poco sovraccarico nel loro utilizzo, ma anche che non c’è protezione per l’EXE chiamante se la DLL ha qualche tipo di bug.
Gestione della memoriaedit
Nell’API di Windows, i file DLL sono organizzati in sezioni. Ogni sezione ha il proprio set di attributi, come essere scrivibile o di sola lettura, eseguibile (per il codice) o non eseguibile (per i dati) e così via.,
Il codice in una DLL viene solitamente condiviso tra tutti i processi che utilizzano la DLL; cioè, occupano un singolo posto nella memoria fisica e non occupano spazio nel file di paging. Windows non usa il codice indipendente dalla posizione per le sue DLL; invece il codice subisce il trasferimento mentre viene caricato, fissando gli indirizzi per tutti i suoi punti di ingresso in posizioni che sono libere nello spazio di memoria del primo processo per caricare la DLL., Nelle versioni precedenti di Windows, in cui tutti i processi in esecuzione occupavano un unico spazio di indirizzo comune, una singola copia del codice della DLL sarebbe sempre sufficiente per tutti i processi. Tuttavia, nelle versioni più recenti di Windows che utilizzano spazi di indirizzo separati per ogni programma, è possibile utilizzare la stessa copia trasferita della DLL in più programmi solo se ogni programma ha gli stessi indirizzi virtuali liberi di ospitare il codice della DLL., Se alcuni programmi (o la loro combinazione di DLL già caricate) non hanno tali indirizzi liberi, sarà necessario creare una copia fisica aggiuntiva del codice della DLL, utilizzando un diverso set di punti di ingresso trasferiti. Se la memoria fisica occupata da una sezione di codice deve essere recuperata, il suo contenuto viene scartato e successivamente ricaricato direttamente dal file DLL, se necessario.
A differenza delle sezioni di codice, le sezioni di dati di una DLL sono solitamente private; cioè, ogni processo che utilizza la DLL ha la propria copia di tutti i dati della DLL., Facoltativamente, le sezioni di dati possono essere rese condivise, consentendo la comunicazione tra processi tramite questa area di memoria condivisa. Tuttavia, poiché le restrizioni dell’utente non si applicano all’uso della memoria DLL condivisa, questo crea un buco di sicurezza; vale a dire, un processo può danneggiare i dati condivisi, che probabilmente causerà tutti gli altri processi di condivisione a comportarsi in modo indesiderabile. Ad esempio, un processo in esecuzione con un account guest può in questo modo corrompere un altro processo in esecuzione con un account privilegiato. Questo è un motivo importante per evitare l’uso di sezioni condivise nelle DLL.,
Se una DLL viene compressa da alcuni packers eseguibili (ad esempio UPX), tutte le sue sezioni di codice sono contrassegnate come lettura e scrittura e non saranno condivise. Le sezioni di codice di lettura e scrittura, proprio come le sezioni di dati privati, sono private per ogni processo. Pertanto le DLL con sezioni di dati condivisi non dovrebbero essere compresse se sono destinate ad essere utilizzate contemporaneamente da più programmi, poiché ogni istanza del programma dovrebbe portare la propria copia della DLL, con conseguente aumento del consumo di memoria.
Importa librariesEdit
Come le librerie statiche, le librerie di importazione per DLL sono annotate dal .,estensione del file lib. Ad esempio, kernel32.dll, la libreria dinamica primaria per le funzioni di base di Windows come la creazione di file e la gestione della memoria, è collegata tramite kernel32.lib. Il solito modo di dire a una libreria di importazione da una libreria statica corretta è per dimensione: la libreria di importazione è molto più piccola in quanto contiene solo simboli che si riferiscono alla DLL effettiva, da elaborare al momento del collegamento. Entrambi sono comunque file in formato ar Unix.
Il collegamento a librerie dinamiche viene solitamente gestito collegandosi a una libreria di importazione durante la creazione o il collegamento per creare un file eseguibile., L’eseguibile creato contiene quindi una tabella degli indirizzi di importazione (IAT) con cui vengono referenziate tutte le chiamate di funzione DLL (ogni funzione DLL di riferimento contiene la propria voce nello IAT). In fase di esecuzione, lo IAT viene riempito con indirizzi appropriati che puntano direttamente a una funzione nella DLL caricata separatamente.
In Cygwin/MSYS e MinGW, alle librerie di importazione viene convenzionalmente assegnato il suffisso.dll.a
, combinando sia il suffisso DLL di Windows che il suffisso ar di Unix., Il formato del file è simile, ma i simboli utilizzati per contrassegnare le importazioni sono diversi (_head_foo_dll vs __IMPORT_DESCRIPTOR_foo). Sebbene la sua toolchain GNU Binutils possa generare librerie di importazione e collegarsi ad esse, è più veloce collegarsi direttamente alla DLL. Uno strumento sperimentale in MinGW chiamato genlib può essere utilizzato per generare librerie di importazione con simboli in stile MSVC.
Symbol resolution and bindingEdit
Ogni funzione esportata da una DLL è identificata da un ordinale numerico e facoltativamente da un nome. Allo stesso modo, le funzioni possono essere importate da una DLL per ordine o per nome., L’ordinale rappresenta la posizione del puntatore dell’indirizzo della funzione nella tabella degli indirizzi di esportazione DLL. È comune che le funzioni interne vengano esportate solo per ordinale. Per la maggior parte delle funzioni API di Windows solo i nomi sono conservati in diverse versioni di Windows; gli ordinali sono soggetti a modifiche. Pertanto, non è possibile importare in modo affidabile le funzioni API di Windows con i loro ordinali.
L’importazione di funzioni per ordinale fornisce prestazioni solo leggermente migliori rispetto all’importazione per nome: le tabelle di esportazione delle DLL sono ordinate per nome, quindi una ricerca binaria può essere utilizzata per trovare una funzione., L’indice del nome trovato viene quindi utilizzato per cercare l’ordinale nella tabella ordinale di esportazione. In Windows a 16 bit, la tabella dei nomi non era ordinata, quindi il sovraccarico di ricerca dei nomi era molto più evidente.
È anche possibile associare un eseguibile a una versione specifica di una DLL, ovvero risolvere gli indirizzi delle funzioni importate in fase di compilazione. Per le importazioni associate, il linker salva il timestamp e il checksum della DLL a cui è associata l’importazione. In fase di esecuzione, Windows controlla se viene utilizzata la stessa versione della libreria e, in tal caso, Windows ignora l’elaborazione delle importazioni., Altrimenti, se la libreria è diversa da quella a cui era associata, Windows elabora le importazioni in modo normale.
Gli eseguibili associati si caricano un po ‘ più velocemente se vengono eseguiti nello stesso ambiente per cui sono stati compilati e esattamente allo stesso tempo se vengono eseguiti in un ambiente diverso, quindi non vi è alcun inconveniente per legare le importazioni. Ad esempio, tutte le applicazioni Windows standard sono associate alle DLL di sistema della rispettiva versione di Windows. Una buona opportunità per associare le importazioni di un’applicazione al suo ambiente di destinazione è durante l’installazione dell’applicazione., Ciò mantiene le librerie ‘vincolate’ fino al prossimo aggiornamento del sistema operativo. Tuttavia, modifica il checksum dell’eseguibile, quindi non è qualcosa che può essere fatto con programmi firmati o programmi gestiti da uno strumento di gestione della configurazione che utilizza i checksum (come i checksum MD5) per gestire le versioni dei file. Poiché le versioni più recenti di Windows si sono allontanate dall’avere indirizzi fissi per ogni libreria caricata (per motivi di sicurezza), l’opportunità e il valore di associare un eseguibile stanno diminuendo.,
Linkingedit runtime esplicitoedit
I file DLL possono essere caricati esplicitamente in fase di runtime, un processo chiamato semplicemente runtime dynamic linking da Microsoft, utilizzando la funzione APILoadLibrary
(oLoadLibraryEx
). La funzione APIGetProcAddress
viene utilizzata per cercare i simboli esportati per nome eFreeLibrary
– per scaricare la DLL. Queste funzioni sono analoghe a dlopen
, dlsym
e dlclose
nell’API standard POSIX.,
La procedura per il collegamento esplicito in fase di esecuzione è la stessa in qualsiasi lingua che supporti i puntatori alle funzioni, poiché dipende dall’API di Windows piuttosto che dai costrutti del linguaggio.
Caricamento ritardatoedit
Normalmente, un’applicazione collegata alla libreria di importazione di una DLL non si avvia se la DLL non viene trovata, perché Windows non eseguirà l’applicazione a meno che non trovi tutte le DLL di cui l’applicazione potrebbe aver bisogno. Tuttavia, un’applicazione può essere collegata a una libreria di importazione per consentire il caricamento ritardato della libreria dinamica.,In questo caso il sistema operativo non cercherà di trovare o caricare la DLL all’avvio dell’applicazione; invece, uno stub è incluso nell’applicazione dal linker che cercherà di trovare e caricare la DLL tramite LoadLibrary e GetProcAddress quando viene chiamata una delle sue funzioni. Se la DLL non può essere trovata o caricata, o la funzione chiamata non esiste, l’applicazione genererà un’eccezione, che può essere rilevata e gestita in modo appropriato. Se l’applicazione non gestisce l’eccezione, verrà rilevata dal sistema operativo, che terminerà il programma con un messaggio di errore.,
Il meccanismo di caricamento del ritardo fornisce anche ganci di notifica, consentendo all’applicazione di eseguire ulteriori elaborazioni o gestione degli errori quando viene caricata la DLL e/o viene chiamata qualsiasi funzione DLL.
Lascia un commento