c++ - GCC-fPIC opción




options (4)

He leído acerca de las Opciones de GCC para Convenciones de Generación de Código , pero no pude entender lo que hace "Generar código independiente de posición (PIC)". Por favor, da un ejemplo para explicarme qué significa.


Añadiendo más ...

Todos los procesos tienen el mismo espacio de direcciones virtuales (si se detiene la asignación aleatoria de direcciones virtuales mediante el uso de un indicador en el sistema operativo Linux) (para obtener más detalles, deshabilite y vuelva a habilitar la distribución aleatoria del espacio de direcciones solo para mí )

Entonces, si es un exe sin enlace compartido (escenario hipotético), entonces siempre podemos dar la misma dirección virtual a la misma instrucción asm sin ningún daño.

Pero cuando queremos vincular el objeto compartido con el exe, no estamos seguros de la dirección de inicio asignada al objeto compartido, ya que dependerá del orden en que se vincularon los objetos compartidos. Dicho esto, la instrucción asm en el interior. Por lo tanto, siempre tendrá Dirección virtual diferente dependiendo del proceso al que se vincula.

Por lo tanto, un proceso puede dar una dirección de inicio a .so, ya que 0x45678910 en su propio espacio virtual y otro proceso al mismo tiempo puede dar una dirección de inicio de 0x12131415 y si no usan direccionamiento relativo, .so no funcionará en absoluto.

Así que siempre tienen que usar el modo de direccionamiento relativo y por lo tanto la opción fpic.


El código independiente de la posición significa que el código generado de la máquina no depende de estar ubicado en una dirección específica para que funcione.

Por ejemplo, los saltos se generarían como relativos en lugar de absolutos.

Pseudo-montaje:

PIC: Esto funcionaría si el código estaba en la dirección 100 o 1000

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL CURRENT+10
...
111: NOP

No PIC: Esto solo funcionará si el código está en la dirección 100

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL 111
...
111: NOP

EDITAR: En respuesta a un comentario.

Si su código se compila con -fPIC, es adecuado para incluirlo en una biblioteca; la biblioteca debe poder reubicarse de su ubicación preferida en la memoria a otra dirección, podría haber otra biblioteca ya cargada en la dirección que la biblioteca prefiere.


El enlace a una función en una biblioteca dinámica se resuelve cuando se carga la biblioteca o en tiempo de ejecución. Por lo tanto, tanto el archivo ejecutable como la biblioteca dinámica se cargan en la memoria cuando se ejecuta el programa. La dirección de memoria en la que se carga una biblioteca dinámica no se puede determinar de antemano, ya que una dirección fija podría entrar en conflicto con otra biblioteca dinámica que requiera la misma dirección.

Hay dos métodos comúnmente utilizados para tratar este problema:

1. Reubicación. Todos los punteros y direcciones en el código se modifican, si es necesario, para adaptarse a la dirección de carga real. La reubicación es realizada por el enlazador y el cargador.

2. Código independiente de la posición. Todas las direcciones en el código son relativas a la posición actual. Los objetos compartidos en sistemas similares a Unix usan código independiente de la posición por defecto. Esto es menos eficiente que la reubicación si el programa se ejecuta durante mucho tiempo, especialmente en el modo de 32 bits.

El nombre " código independiente de la posición " en realidad implica lo siguiente:

  • La sección de códigos no contiene direcciones absolutas que necesiten reubicación, sino solo direcciones relativas a sí mismas. Por lo tanto, la sección de código se puede cargar en una dirección de memoria arbitraria y compartir entre varios procesos.

  • La sección de datos no se comparte entre varios procesos porque a menudo contiene datos grabables. Por lo tanto, la sección de datos puede contener punteros o direcciones que necesitan reubicación.

  • Todas las funciones públicas y los datos públicos se pueden anular en Linux. Si una función en el ejecutable principal tiene el mismo nombre que una función en un objeto compartido, entonces la versión en main tendrá prioridad, no solo cuando se llama desde main, sino también cuando se llama desde el objeto compartido. Del mismo modo, cuando una variable global en main tiene el mismo nombre que una variable global en el objeto compartido, se utilizará la instancia en main, incluso cuando se acceda desde el objeto compartido.

Esta llamada interposición de símbolos pretende simular el comportamiento de las bibliotecas estáticas.

Un objeto compartido tiene una tabla de punteros a sus funciones, llamada tabla de vinculación de procedimientos (PLT) y una tabla de punteros a sus variables llamada tabla de compensación global (GOT) para implementar esta característica de "anulación". Todos los accesos a funciones y variables públicas pasan por esta tabla.

ps Cuando no se puede evitar la vinculación dinámica, hay varias formas de evitar las funciones que consumen tiempo del código independiente de la posición.

Puede leer más de este artículo: http://www.agner.org/optimize/optimizing_cpp.pdf


Trataré de explicar lo que ya se ha dicho de una manera más simple.

Cada vez que se carga una biblioteca compartida, el cargador (el código en el sistema operativo que carga cualquier programa que ejecute) cambia algunas direcciones en el código dependiendo de dónde se cargó el objeto.

En el ejemplo anterior, el cargador escribe el "111" en el código no PIC la primera vez que se cargó.

Para los objetos no compartidos, es posible que desee que sea así porque el compilador puede realizar algunas optimizaciones en ese código.

Para el objeto compartido, si otro proceso querrá "vincular" a ese código, debe leerlo en las mismas direcciones virtuales o el "111" no tendrá sentido. pero ese espacio virtual puede estar ya en uso en el segundo proceso.





fpic