2011-11-30 13 views
6

Questa domanda è relativa a this one e alla sua risposta.Miscelazione di oggetti PIC e non PIC in una libreria condivisa

Ho appena scoperto un po 'di bruttezza in una build su cui sto lavorando. La situazione è simile alla seguente (scritta in formato gmake); nota, ciò 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 può funzionare mescolare oggetti compilati con e senza PIC in un oggetto condiviso (questo è stato in uso per anni) . Non ne so abbastanza su PIC per sapere se è una buona idea/intelligente, e la mia ipotesi è che in questo caso non è necessario, ma piuttosto sta accadendo perché a qualcuno non importava abbastanza per trovare il modo giusto per farlo su nuove cose per la costruzione.

La mia domanda è:

  1. Questa funzione è sicura
  2. E 'una buona idea
  3. Quali problemi potenziali possono verificarsi a seguito
  4. Se posso passare tutto per PIC, ce ne sono non -ottoci ovvi che potrei voler fare attenzione.

risposta

4

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.

Problemi correlati