Pregunta

Esta pregunta está relacionada con Éste así como su respuesta.

Acabo de descubrir algo de fealdad en una construcción en la que estoy trabajando. La situación se parece algo a la siguiente (escrita en formato Gmake); Tenga en cuenta que esto se aplica específicamente a un modelo de memoria de 32 bits en Hardware SPARC y X86:

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 ${@} ${^}

Claramente, puede funcionar para mezclar objetos compilados con y sin PIC en un objeto compartido (esto ha estado en uso durante años). No sé lo suficiente sobre PIC para saber si es una buena idea/inteligente, y supongo que en este caso no es necesario, sino que está sucediendo porque a alguien no le importó lo suficiente como para descubrir la forma correcta de hacerlo al agregar 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 a la foto, ¿hay algún gotchas no obvio que quiera tener en cuenta?
¿Fue útil?

Solución

Olvidé que incluso escribí esta pregunta.

Algunas explicaciones están en orden primero:

  • El sistema operativo no PIC puede ser cargado por el sistema operativo en cualquier posición en la memoria en [la mayoría?] OSS moderno. Después de cargar todo, pasa por una fase que fija el segmento de texto (donde termina el material ejecutable), por lo que aborda correctamente las variables globales; Para lograr esto, el segmento de texto debe ser escrito.
  • El sistema operativo PIC se puede cargar una vez por el sistema operativo y compartir en 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 correcciones. El código se compila para usar una tabla de compensación global (GOT) para que pueda abordar los globos en relación con los GOT, aliviando la necesidad de arreglos.
  • Si se construye un objeto compartido sin PIC, aunque se recomienda encarecidamente, no parece que sea estrictamente necesario; Si el sistema operativo debe arreglar el segmento de texto, entonces se ve obligado a cargarlo en la memoria marcada de lectura-escritura ... lo que evita compartir en todos los procesos/usuarios.
  • Si se construye un binario ejecutable / con / foto, no sé qué sale mal debajo del capó, pero he sido testigo de que algunas herramientas se vuelven inestables (misteriosos choques y similares).

Las respuestas:

  • Mezclar Pic/no PIC, o usar PIC en ejecutables puede causar difíciles de predecir y rastrear las inestabilidades. No tengo una explicación técnica de por qué.
    • ... para incluir Segfaults, errores de autobús, corrupción de pila y probablemente más además.
  • Probablemente no causará problemas de PIC en objetos compartidos, aunque puede resultar en más RAM si se usa la biblioteca muchas veces en todos los procesos y/o usuarios.

Actualización (4/17)

Desde entonces descubrí la causa de alguno de los accidentes que había visto anteriormente. 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;
}

... después:

$ 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 en particular puede no terminar bloqueando, pero es básicamente la situación que había existido en el código de ese grupo. Si se lo hace Crash probablemente esté en el destructor, generalmente un error de doble libre.

Muchos años antes agregaron -zmuldefs a su construcción para deshacerse de los errores de símbolos definidos multiplicados. El compilador emite código para ejecutar constructores/destructores en objetos globales. -zmuldefs Los obliga a vivir en la misma ubicación en la memoria, pero aún así ejecuta los constructores/destructores una vez para el EXE y cada biblioteca que incluía el encabezado ofensivo, de ahí el doble libre.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top