2011-11-30 9 views
6

Esta pregunta está relacionada con this one, así como su respuesta.Mezcla de objetos PIC y no PIC en una biblioteca compartida

Acabo de descubrir algo de fealdad en una construcción en la que estoy trabajando. La situación se parece a la siguiente (escrita en formato gmake); en cuenta, esto se aplica específicamente a un modelo de memoria de 32 bits en SPARC y x86 de 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 ${@} ${^} 

Es evidente que puede funcionar para mezclar objetos compilados con y sin PIC en un objeto compartido (esto ha estado en uso desde hace años) . No sé lo suficiente sobre PIC para saber si es una buena idea/inteligente, y supongo que en este caso no es necesaria, sino que está sucediendo porque a alguien no le importó lo suficiente como para saber la forma correcta de hacerlo al hacer tachuelas en cosas nuevas para la construcción.

Mi pregunta es:

  1. Es esto seguro
  2. ¿Es una buena idea
  3. ¿Qué problemas potenciales pueden ocurrir como resultado
  4. Si cambio todo para PIC, ¿hay alguna que no -objetivos obvios que podría querer tener en cuenta.

Respuesta

4

Olvidé que incluso escribí esta pregunta.

Algunas explicaciones están en orden primero:

  • código PIC no puede ser cargado por el sistema operativo en cualquier posición de memoria en [más?] Sistema operativo moderno. Después de que todo está cargado, pasa por una fase que arregla el segmento de texto (donde termina el material ejecutable) para que aborde correctamente las variables globales; para lograr esto, el segmento de texto debe poder escribirse.
  • Los datos ejecutables PIC pueden ser cargados una vez por el SO y compartidos entre múltiples usuarios/procesos. Para que el sistema operativo haga esto, sin embargo, el segmento de texto debe ser de solo lectura, lo que significa que no hay reparaciones. El código se compila para usar una Tabla de Compensación Global (GOT) para que pueda abordar los asuntos globales relativos al GOT, aliviando la necesidad de arreglos.
  • Si un objeto compartido se crea sin PIC, aunque se recomienda enfáticamente, no parece que sea estrictamente necesario; si el sistema operativo debe reparar el segmento de texto, se lo fuerza a cargarlo en la memoria marcada como lectura-escritura ... lo que impide el uso compartido de procesos/usuarios.
  • Si se construye un binario ejecutable/con/PIC, no sé qué va mal debajo del capó, pero he sido testigo de algunas herramientas se vuelven inestables (misteriosos bloqueos & similares).

Las respuestas:

  • Mezcla PIC/no-PIC, o el uso de PIC en ejecutables puede causar difícil de predecir y localizar a inestabilidades. No tengo una explicación técnica de por qué.
    • ... para incluir segfaults, errores de bus, corrupción de pila, y probablemente más.
  • El no PIC en objetos compartidos probablemente no ocasione problemas graves, aunque puede generar más RAM si la biblioteca se utiliza muchas veces en procesos y/o usuarios.

actualización (4/17)

Desde entonces, he descubierto la causa de algunos de los accidentes que había visto con anterioridad. Para ilustrar:

/*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; 
} 

... entonces:

$ 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) 

Ese ejemplo particular puede no llegar a chocar, pero es básicamente la situación que existía en el código de ese grupo. Si se bloquea, es probable que esté en el destructor, por lo general un error doblemente libre.

Muchos años antes, agregaron -zmuldefs a su versión para deshacerse de los errores de símbolos definidos de forma múltiple. El compilador emite código para ejecutar constructores/destructores en objetos globales. -zmuldefs les obliga a vivir en el mismo lugar en la memoria, pero todavía ejecuta los constructores/destructores una vez para el archivo ejecutable y cada biblioteca que incluyó el encabezado ofensivo, de ahí el doble libre.

Cuestiones relacionadas