c++ - GCC-fPIC opción




options (6)

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.

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.


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.


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.


Una adición menor a las respuestas ya publicadas: los archivos de objeto no compilados para ser independientes de la posición son reubicables; Contienen entradas de la tabla de reubicación.

Estas entradas permiten que el cargador (ese bit de código que carga un programa en la memoria) vuelva a escribir las direcciones absolutas para ajustar la dirección de carga real en el espacio de direcciones virtuales.

Un sistema operativo intentará compartir una sola copia de una "biblioteca de objetos compartidos" cargada en la memoria con todos los programas que están vinculados a esa misma biblioteca de objetos compartidos.

Dado que el espacio de direcciones de código (a diferencia de las secciones del espacio de datos) no necesita ser contiguo, y debido a que la mayoría de los programas que enlazan a una biblioteca específica tienen un árbol de dependencia de biblioteca bastante fijo, esto sucede la mayor parte del tiempo. En los casos raros en los que hay una discrepancia, sí, puede ser necesario tener dos o más copias de una biblioteca de objetos compartidos en la memoria.

Obviamente, cualquier intento de aleatorizar la dirección de carga de una biblioteca entre programas y / o instancias de programas (para reducir la posibilidad de crear un patrón explotable) hará que estos casos sean comunes, no raros, por lo que cuando un sistema ha habilitado esta capacidad, se debe hacer todo lo posible para compilar todas las bibliotecas de objetos compartidos para que sean independientes de la posición.

Dado que las llamadas a estas bibliotecas desde el cuerpo del programa principal también se podrán reubicar, esto hace que sea mucho menos probable que una biblioteca compartida deba copiarse.


gcc y g++ son controladores de compilación de la colección de compiladores de GNU (que alguna vez fue solo el compilador de C de GNU).

A pesar de que determinan automáticamente a qué backends ( cc1 cc1plus ...) llamar según el tipo de archivo, a menos que se invalide con el -x language , tienen algunas diferencias.

La diferencia probablemente más importante en sus valores predeterminados es con qué bibliotecas se vinculan automáticamente.

De acuerdo con las opciones de enlace de la documentación en línea de GCC y cómo se invoca g++ , g++ es equivalente a gcc -xc++ -lstdc++ -shared-libgcc (la primera es una opción de compilador, la segunda es una opción de vinculador). Esto puede comprobarse ejecutando ambos con la opción -v (muestra los comandos de la cadena de herramientas backend que se están ejecutando).







c++ gcc options fpic