Ho dimenticato di aver scritto questa domanda.
Alcune spiegazioni sono in ordine prima:
- codice non-PIC può essere caricato dal sistema operativo in qualsiasi posizione in memoria in [di più?] Moderni sistemi operativi. Dopo che tutto è stato caricato, passa attraverso una fase che risolve il segmento di testo (dove finisce il file eseguibile) in modo che indirizzi correttamente le variabili globali; per rimuoverlo, il segmento di testo deve essere scrivibile.
- I dati eseguibili PIC possono essere caricati una volta dal sistema operativo e condivisi tra più utenti/processi. Per il sistema operativo, tuttavia, il segmento di testo deve essere di sola lettura, il che significa che non ci sono correzioni. Il codice è compilato per utilizzare un Global Offset Table (GOT) in modo che possa indirizzare globalmente rispetto al GOT, alleggerendo la necessità di correzioni.
- Se un oggetto condiviso viene creato senza PIC, sebbene sia fortemente consigliato, non sembra che sia strettamente necessario; se il sistema operativo deve aggiustare il segmento di testo, è costretto a caricarlo in memoria che è contrassegnato come read-write ... che impedisce la condivisione tra processi e utenti.
- Se un binario eseguibile è stato creato/con/PIC, non so cosa vada storto sotto il cofano, ma ho visto alcuni strumenti diventare instabili (i misteriosi arresti anomali dello & sono simili).
Le risposte:
- miscelazione PIC/non-PIC, o utilizzando PIC in eseguibili può causare difficili da prevedere e rintracciare instabilità. Non ho una spiegazione tecnica del perché.
- ... per includere segfault, errori di bus, danneggiamento dello stack e probabilmente anche altro.
- Non PIC negli oggetti condivisi probabilmente non causerà alcun problema serio, sebbene possa comportare più RAM utilizzata se la libreria viene utilizzata più volte tra processi e/o utenti.
aggiornamento (4/17)
allora ho scoperto la causa di alcuni degli incidenti che avevo visto in precedenza. Per illustrare:
/*header.h*/
#include <map>
typedef std::map<std::string,std::string> StringMap;
StringMap asdf;
/*file1.cc*/
#include "header.h"
/*file2.cc*/
#include "header.h"
int main(int argc, char** argv) {
for(int ii = 0; ii < argc; ++ii) {
asdf[argv[ii]] = argv[ii];
}
return 0;
}
... poi:
$ g++ file1.cc -shared -PIC -o libblah1.so
$ g++ file1.cc -shared -PIC -o libblah2.so
$ g++ file1.cc -shared -PIC -o libblah3.so
$ g++ file1.cc -shared -PIC -o libblah4.so
$ g++ file1.cc -shared -PIC -o libblah5.so
$ g++ -zmuldefs file2.cc -Wl,-{L,R}$(pwd) -lblah{1..5} -o fdsa
# ^^^^^^^^^
# This is the evil that made it possible
$ args=(this is the song that never ends);
$ eval ./fdsa $(for i in {1..100}; do echo -n ${args[*]}; done)
Questo particolare esempio non può finire per schiantarsi, ma è fondamentalmente la situazione che esisteva nel codice che di gruppo. Se si verifica arresto, è probabile che si trovi nel distruttore, di solito un errore double-free.
Molti anni prima hanno aggiunto -zmuldefs
alla loro build per sbarazzarsi degli errori di simboli definiti più volte. Il compilatore emette il codice per l'esecuzione di costruttori/distruttori su oggetti globali. -zmuldefs
obbliga a vivere nella stessa posizione in memoria ma esegue ancora i costruttori/distruttori una volta per l'exe e ogni libreria che includeva l'intestazione offendente, da cui il doppio libero.
fonte
2013-04-05 19:41:50