Mescolando PIC e non-PIC oggetti in una libreria condivisa
-
26-10-2019 - |
Domanda
Questa domanda è legata a questo così come la sua risposta.
Ho appena scoperto qualche bruttura in una costruzione su cui sto lavorando. Gli sguardi situazione un po 'come il seguente (scritto in formato gmake); nota, questo vale in particolare per un modello di memoria a 32 bit su SPARC e x86 hardware:
OBJ_SET1 := some objects
OBJ_SET2 := some objects
# note: OBJ_SET2 doesn't get this flag
${OBJ_SET1} : CCFLAGS += -PIC
${OBJ_SET1} ${OBJ_SET2} : %.o : %.cc
${CCC} ${CCFLAGS} -m32 -o ${@} -c ${<}
obj1.o : ${OBJ_SET1}
obj2.o : ${OBJ_SET2}
sharedlib.so : obj1.o obj2.o
obj1.o obj2.o sharedlib.so :
${LINK} ${LDFLAGS} -m32 -PIC -o ${@} ${^}
Chiaramente si può lavorare a mescolare oggetti compilati con e senza PIC in un oggetto condiviso (questo è stato in uso per anni). Non so abbastanza di PIC per sapere se si tratta di una buona idea / intelligente, e la mia ipotesi è, in questo caso non è necessaria, ma piuttosto sta accadendo perché qualcuno non importava abbastanza per scoprire il modo giusto per farlo quando virata il nuovo materiale per la compilazione.
La mia domanda è:
- Questa funzione è sicura
- E 'una buona idea
- Quali potenziali problemi possono verificarsi a seguito
- Se posso passare tutto per PIC, ci sono trucchi non evidenti che potrei voler guardare fuori per.
Soluzione
Hai dimenticato Ho anche 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 tutto quello che è stato caricato, passa attraverso una fase che fissa il segmento di testo (dove la roba eseguibile finisce) quindi si rivolge correttamente le variabili globali; per tirare fuori questo, il segmento di testo deve essere scrivibile.
- PIC dati eseguibili possono essere caricati una volta dal sistema operativo e condivise tra più utenti / processi. Per il sistema operativo per fare questo, però, il segmento di testo deve essere di sola lettura - il che significa che nessun fix-up. Il codice viene compilato per utilizzare un globale Offset Table (GOT) in modo che possa affrontare globali relativi al GOT, eliminando la necessità per Fix-up.
- Se un oggetto condiviso è costruito senza PIC, anche se è fortemente incoraggiata non sembra che sia strettamente necessario; se il sistema operativo deve fissare-up del segmento di testo poi è costretto a caricare in memoria che è contrassegnato nella lettura-scrittura ... che impedisce la condivisione tra processi / utenti.
- Se un binario eseguibile è costruito / con / PIC, non so che cosa va storto sotto il cofano, ma ho assistito un paio di strumenti diventano instabili (arresti misteriosi e simili).
Le risposte:
- Mixing 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 segfaults, errori del bus, la corruzione dello stack, e probabilmente altro ancora.
- Non PIC in oggetti condivisi non è probabilmente andando a causare problemi seri, anche se può risultare in più RAM utilizzata se la libreria viene utilizzato più volte attraverso i processi e / o utenti.
update (4/17)
Da allora ho scoperto la causa di alcuni degli incidenti che avevano visto in precedenza. Facciamo un esempio:
/*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;
}
... allora:
$ 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 esempio particolare non può finire per schiantarsi, ma è fondamentalmente la situazione che esisteva nel codice che di gruppo. Se ha in crash è probabile che tu sia nel distruttore, di solito un errore di doppio libero.
Molti anni precedenti hanno aggiunto -zmuldefs
a loro costruzione di sbarazzarsi di errori simbolo si moltiplicano definiti. Il compilatore emette codice per l'esecuzione di costruttori / distruttori su oggetti globali. -zmuldefs
forze a vivere nella stessa posizione in memoria, ma lo gestisce ancora costruttori / distruttori una volta per l'exe e ogni libreria che comprendeva l'intestazione incriminato -. da qui il doppio libera