Opzione GCC -fPIC

Ho letto sulle opzioni di GCC per le convenzioni sulla generazione di codice , ma non sono riuscito a capire cosa significhi “generare codice indipendente dalla posizione (PIC)”. Per favore, fai un esempio per spiegarmi cosa significa.

Position Independent Code significa che il codice macchina generato non dipende dal fatto di trovarsi in un indirizzo specifico per funzionare.

Ad esempio i salti sarebbero generati come relativi piuttosto che assoluti.

Pseudo-assemblaggio:

PIC: funzionerebbe se il codice fosse all’indirizzo 100 o 1000

100: COMPARE REG1, REG2 101: JUMP_IF_EQUAL CURRENT+10 ... 111: NOP 

Non-PIC: funziona solo se il codice è all’indirizzo 100

 100: COMPARE REG1, REG2 101: JUMP_IF_EQUAL 111 ... 111: NOP 

EDIT: in risposta al commento.

Se il tuo codice è compilato con -fPIC, è adatto per l’inclusione in una libreria – la libreria deve essere in grado di essere trasferita dalla sua posizione preferita in memoria a un altro indirizzo, potrebbe esserci un’altra libreria già caricata all’indirizzo che la tua libreria preferisce.

Cercherò di spiegare ciò che è già stato detto in un modo più semplice.

Ogni volta che viene caricata una lib condivisa, il loader (il codice sul sistema operativo che carica qualsiasi programma eseguito) modifica alcuni indirizzi nel codice a seconda di dove è stato caricato l’object.

Nell’esempio precedente, il “111” nel codice non PIC viene scritto dal caricatore la prima volta che è stato caricato.

Per gli oggetti non condivisi, si potrebbe desiderare che sia così perché il compilatore può fare alcune ottimizzazioni su quel codice.

Per oggetti condivisi, se un altro processo vorrà “link” a quel codice, dovrà leggerlo agli stessi indirizzi virtuali o il “111” non avrà alcun senso. ma quello spazio virtuale potrebbe essere già in uso nel secondo processo.

Il codice che è incorporato nelle librerie condivise dovrebbe normalmente essere un codice indipendente dalla posizione, in modo che la libreria condivisa possa essere facilmente caricata su (più o meno) qualsiasi indirizzo in memoria. L’opzione -fPIC garantisce che GCC produca tale codice.

Aggiunta ulteriore …

Ogni processo ha lo stesso spazio di indirizzamento virtuale (se la randomizzazione dell’indirizzo virtuale viene interrotta utilizzando un flag nel sistema operativo Linux) (Per ulteriori dettagli Disabilitare e ritriggersre la randomizzazione del layout dello spazio degli indirizzi solo per me )

Quindi se è un exe senza collegamenti condivisi (scenario ipotetico), allora possiamo sempre dare lo stesso indirizzo virtuale alle stesse istruzioni asm senza alcun danno.

Ma quando vogliamo colbind l’object condiviso con l’exe, non siamo sicuri dell’indirizzo di partenza assegnato all’object condiviso poiché dipenderà dall’ordine in cui gli oggetti condivisi sono stati collegati. Ciò detto, l’istruzione asm all’interno di .so avrà sempre indirizzo virtuale diverso a seconda del processo a cui si collega.

Quindi un processo può dare l’indirizzo iniziale a .so come 0x45678910 nel proprio spazio virtuale e un altro processo allo stesso tempo può dare l’indirizzo iniziale di 0x12131415 e se non usano l’indirizzamento relativo, .so non funzionerà affatto.

Quindi devono sempre usare la relativa modalità di indirizzamento e quindi l’opzione fpic.

Il collegamento a una funzione in una libreria dynamic viene risolto quando la libreria viene caricata o in fase di esecuzione. Pertanto, sia il file eseguibile che la libreria dynamic vengono caricati in memoria quando viene eseguito il programma. L’indirizzo di memoria al quale viene caricata una libreria dynamic non può essere determinato in anticipo, poiché un indirizzo fisso potrebbe entrare in conflitto con un’altra libreria dynamic che richiede lo stesso indirizzo.


Esistono due metodi comunemente usati per affrontare questo problema:

1.Relocation. Tutti i puntatori e gli indirizzi nel codice vengono modificati, se necessario, per adattarsi all’indirizzo di carico effettivo. Il riposizionamento viene eseguito dal linker e dal loader.

2. Codice indipendente dalla posizione. Tutti gli indirizzi nel codice sono relativi alla posizione corrente. Gli oggetti condivisi nei sistemi di tipo Unix usano il codice indipendente dalla posizione per impostazione predefinita. Questo è meno efficiente del trasferimento se il programma viene eseguito per un lungo periodo, specialmente in modalità a 32 bit.


Il nome ” codice indipendente dalla posizione ” implica in realtà:

  • La sezione del codice non contiene indirizzi assoluti che richiedono il riposizionamento, ma solo indirizzi auto relativi. Pertanto, la sezione del codice può essere caricata su un indirizzo di memoria arbitrario e condivisa tra più processi.

  • La sezione dati non è condivisa tra più processi perché spesso contiene dati scrivibili. Pertanto, la sezione dati può contenere puntatori o indirizzi che richiedono il trasferimento.

  • Tutte le funzioni pubbliche e i dati pubblici possono essere sovrascritti in Linux. Se una funzione nell’eseguibile principale ha lo stesso nome di una funzione in un object condiviso, la versione in main avrà la precedenza, non solo quando viene chiamata da main, ma anche quando viene chiamata dall’object condiviso. Allo stesso modo, quando una variabile globale in main ha lo stesso nome di una variabile globale nell’object condiviso, verrà utilizzata l’istanza in main, anche se si accede dall’object condiviso.


Questa cosiddetta interposizione di simboli ha lo scopo di imitare il comportamento delle librerie statiche.

Un object condiviso ha una tabella di puntatori alle sue funzioni, chiamata tabella di collegamento della procedura (PLT) e una tabella di puntatori alle sue variabili chiamate tabella di offset globale (GOT) al fine di implementare questa funzione di “override”. Tutti gli accessi a funzioni e variabili pubbliche passano attraverso queste tabelle.

ps Se il collegamento dinamico non può essere evitato, ci sono vari modi per evitare le caratteristiche del time-code del codice indipendente dalla posizione.

Puoi leggere altro da questo articolo: http://www.agner.org/optimize/optimizing_cpp.pdf

Una piccola aggiunta alle risposte già pubblicate: i file object non compilati come indipendenti dalla posizione sono rilocabili; contengono voci della tabella di rilocazione.

Queste voci consentono al caricatore (quel bit di codice che carica un programma in memoria) di riscrivere gli indirizzi assoluti per regolare l’indirizzo di carico effettivo nello spazio degli indirizzi virtuali.

Un sistema operativo tenterà di condividere una singola copia di una “libreria di oggetti condivisa” caricata in memoria con tutti i programmi che sono collegati alla stessa libreria di oggetti condivisa.

Poiché lo spazio di indirizzamento del codice (a differenza delle sezioni dello spazio dati) non deve essere contiguo, e poiché la maggior parte dei programmi che si collegano a una libreria specifica hanno una struttura di dipendenze della libreria abbastanza fissa, ciò avviene per la maggior parte del tempo. In quei rari casi in cui vi è una discrepanza, sì, potrebbe essere necessario avere due o più copie di una libreria di oggetti condivisa in memoria.

Ovviamente, qualsiasi tentativo di randomizzare l’indirizzo di carico di una libreria tra programmi e / o istanze di programma (in modo da ridurre la possibilità di creare uno schema sfruttabile) renderà tali casi comuni, non rari, quindi, se un sistema ha abilitato questa capacità, si dovrebbe fare ogni tentativo di compilare tutte le librerie di oggetti condivise per essere indipendenti dalla posizione.

Poiché anche le chiamate in queste librerie dal corpo del programma principale verranno rese trasferibili, è molto meno probabile che una libreria condivisa debba essere copiata.