El código de posición independiente significa que el código de máquina generado no depende de estar ubicado en una dirección específica para funcionar.
Por ejemplo, los saltos se generarían como relativos en lugar de absolutos.
Pseudoensamblaje:
PIC: esto funcionaría si el código estaba en la dirección 100 o 1000
Si su código se compila con -fPIC, es adecuado para su inclusión en una biblioteca: la biblioteca debe poder reubicarse desde su ubicación preferida en la memoria a otra dirección, podría haber otra biblioteca ya cargada en la dirección que su biblioteca prefiere.
Este ejemplo es claro, pero como usuario, ¿cuál será la diferencia si creo un archivo de laboratorio compartido (.so) sin la opción? ¿Hay algunos casos en los que sin -fPIC mi lib no será válida?
Narek
16
Sí, construir una biblioteca compartida que no sea PIC podría ser un error.
John Zwinck
92
Para ser más específico, se supone que la biblioteca compartida se comparte entre procesos, pero no siempre es posible cargar la biblioteca en la misma dirección en ambos. Si el código no fuera independiente de la posición, cada proceso requeriría su propia copia.
Simon Richter
19
@Narek: el error ocurre si un proceso quiere cargar más de una biblioteca compartida en la misma dirección virtual. Dado que las bibliotecas no pueden predecir qué otras bibliotecas podrían cargarse, este problema es inevitable con el concepto tradicional de biblioteca compartida. El espacio de direcciones virtuales no ayuda aquí.
Philipp
66
Puede omitir -fPICal compilar un programa o una biblioteca estática, porque solo existirá un programa principal en un proceso, por lo que no es necesario reubicar el tiempo de ejecución. En algunos sistemas, los programas aún se hacen independientes de la posición para una mayor seguridad.
Simon Richter
61
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 que no es PIC la primera vez que se cargó.
Para los objetos no compartidos, es posible que desee que sea así porque el compilador puede hacer algunas optimizaciones en ese código.
Para el objeto compartido, si otro proceso desea "enlazar" a ese código, debe leerlo en las mismas direcciones virtuales o el "111" no tendrá sentido. pero ese espacio virtual ya puede estar en uso en el segundo proceso.
Whenever a shared lib is loaded, the loader changes some addresses in the code depending on where the object was loaded to.Creo que esto no es correcto si se compila con -fpic y la razón por la cual existe -fpic, es decir, por razones de rendimiento o porque tiene un cargador que no puede reubicarse o porque necesita varias copias en diferentes ubicaciones o por muchas otras razones.
robsn
¿Por qué no usar siempre -fpic?
Jay
1
@ Jay - porque requerirá un cálculo más (la dirección de la función) para cada llamada a la función. Entonces, en cuanto al rendimiento, si no es necesario, es mejor no usarlo.
Roee Gavirel
45
El código integrado en las bibliotecas compartidas normalmente debería ser un código independiente de la posición, de modo que la biblioteca compartida se pueda cargar fácilmente en (más o menos) cualquier dirección en la memoria. La -fPICopción asegura que GCC produzca dicho código.
¿Por qué no se cargaría una biblioteca compartida en cualquier dirección en la memoria sin tener el -fPICindicador activado? ¿No está vinculado al programa? cuando el programa se está ejecutando, el sistema operativo lo carga en la memoria. ¿Me estoy perdiendo de algo?
Tony Tannous el
1
¿Se -fPICutiliza la bandera para garantizar que esta lib se pueda cargar en cualquier dirección virtual en el proceso que la vincula? perdón por los comentarios dobles 5 minutos transcurridos no se puede editar el anterior.
Tony Tannous el
1
Distinga entre construir la biblioteca compartida (crear libwotnot.so) y vincularla ( -lwotnot). Mientras se vincula, no necesita preocuparse -fPIC. Solía ser el caso de que al compilar la biblioteca compartida, tenía que asegurarse de que -fPICse utilizara para todos los archivos de objetos que se construirían en la biblioteca compartida. Las reglas pueden haber cambiado porque los compiladores compilan con el código PIC de forma predeterminada en estos días. Entonces, lo que era crítico hace 20 años, y podría haber sido importante hace 7 años, es menos importante en estos días, creo. Las direcciones fuera del núcleo o / s son 'siempre' direcciones virtuales '.
Jonathan Leffler
Así que anteriormente tenía que agregar el -fPIC. Sin pasar esta bandera, ¿el código generado al compilar el .so debe cargarse en direcciones virtuales específicas que podrían estar en uso?
Tony Tannous
1
Sí, porque si no usó el indicador PIC, el código no era confiablemente reubicable. Cosas como ASLR (asignación al azar del diseño del espacio de direcciones) no son posibles si el código no es PIC (o, al menos, es tan difícil de lograr que son efectivamente imposibles).
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 un objeto compartido al exe, entonces 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 as dentro de .so siempre tendrá dirección virtual diferente según el proceso al que se vincula.
Por lo tanto, un proceso puede proporcionar una dirección de inicio a .so como 0x45678910 en su propio espacio virtual y otro proceso al mismo tiempo puede proporcionar una dirección de inicio de 0x12131415 y, si no utilizan el direccionamiento relativo, .so no funcionará en absoluto.
Por lo tanto, siempre tienen que usar el modo de direccionamiento relativo y, por lo tanto, la opción fpic.
¿Alguien puede explicar cómo esto no es un problema con una biblioteca estática, por qué no tiene que usar -fPIC en una biblioteca estática? Entiendo que la vinculación se realiza en tiempo de compilación (o inmediatamente después), pero si tiene 2 bibliotecas estáticas con código dependiente de la posición, ¿cómo se vincularán?
Michael P
3
El archivo de objetos @MichaelP tiene una tabla de etiquetas dependientes de la posición y cuando se vincula un archivo obj particular, todas las etiquetas se actualizan en consecuencia. Esto no se puede hacer a la biblioteca compartida.
Slava
16
El enlace a una función en una biblioteca dinámica se resuelve cuando la biblioteca se carga 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, porque una dirección fija puede chocar con otra biblioteca dinámica que requiere la misma dirección.
Existen 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 ajustarse a la dirección de carga real. La reubicación se realiza mediante el vinculador 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 tipo Unix usan código independiente de la posición de forma predeterminada. Esto es menos eficiente que la reubicación si el programa se ejecuta durante mucho tiempo, especialmente en modo de 32 bits.
El nombre " código independiente de la posición " en realidad implica lo siguiente:
La sección de código no contiene direcciones absolutas que necesiten reubicación, sino solo direcciones relativas. Por lo tanto, la sección de código puede cargarse en una dirección de memoria arbitraria y compartirse entre múltiples procesos.
La sección de datos no se comparte entre múltiples procesos porque a menudo contiene datos que se pueden escribir. 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 pueden anularse 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 llame desde main, sino también cuando se llame 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 accede desde el objeto compartido.
Esta llamada interposición de símbolos pretende imitar 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 desplazamiento global (GOT) para implementar esta función de "anulación". Todos los accesos a funciones y variables públicas pasan por estas tablas.
ps Cuando no se puede evitar el enlace dinámico, hay varias formas de evitar las características que consumen tiempo del código independiente de la posición.
Una pequeña adición a las respuestas ya publicadas: los archivos de objetos no compilados para ser independientes de la posición son reubicables; contienen entradas de tabla de reubicación.
Estas entradas permiten al cargador (ese bit de código que carga un programa en la memoria) reescribir 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 dirección 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 se vinculan a una biblioteca específica tienen un árbol de dependencia de biblioteca bastante fijo, esto tiene éxito la mayor parte del tiempo. En esos casos raros donde 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, uno 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 reubicarán, esto hace que sea mucho menos probable que se deba copiar una biblioteca compartida.
Respuestas:
El código de posición independiente significa que el código de máquina generado no depende de estar ubicado en una dirección específica para funcionar.
Por ejemplo, los saltos se generarían como relativos en lugar de absolutos.
Pseudoensamblaje:
PIC: esto funcionaría si el código estaba en la dirección 100 o 1000
No PIC: esto solo funcionará si el código está en la dirección 100
EDITAR: en respuesta al comentario.
Si su código se compila con -fPIC, es adecuado para su inclusión en una biblioteca: la biblioteca debe poder reubicarse desde su ubicación preferida en la memoria a otra dirección, podría haber otra biblioteca ya cargada en la dirección que su biblioteca prefiere.
fuente
-fPIC
al compilar un programa o una biblioteca estática, porque solo existirá un programa principal en un proceso, por lo que no es necesario reubicar el tiempo de ejecución. En algunos sistemas, los programas aún se hacen independientes de la posición para una mayor seguridad.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 que no es PIC la primera vez que se cargó.
Para los objetos no compartidos, es posible que desee que sea así porque el compilador puede hacer algunas optimizaciones en ese código.
Para el objeto compartido, si otro proceso desea "enlazar" a ese código, debe leerlo en las mismas direcciones virtuales o el "111" no tendrá sentido. pero ese espacio virtual ya puede estar en uso en el segundo proceso.
fuente
Whenever a shared lib is loaded, the loader changes some addresses in the code depending on where the object was loaded to.
Creo que esto no es correcto si se compila con -fpic y la razón por la cual existe -fpic, es decir, por razones de rendimiento o porque tiene un cargador que no puede reubicarse o porque necesita varias copias en diferentes ubicaciones o por muchas otras razones.El código integrado en las bibliotecas compartidas normalmente debería ser un código independiente de la posición, de modo que la biblioteca compartida se pueda cargar fácilmente en (más o menos) cualquier dirección en la memoria. La
-fPIC
opción asegura que GCC produzca dicho código.fuente
-fPIC
indicador activado? ¿No está vinculado al programa? cuando el programa se está ejecutando, el sistema operativo lo carga en la memoria. ¿Me estoy perdiendo de algo?-fPIC
utiliza la bandera para garantizar que esta lib se pueda cargar en cualquier dirección virtual en el proceso que la vincula? perdón por los comentarios dobles 5 minutos transcurridos no se puede editar el anterior.libwotnot.so
) y vincularla (-lwotnot
). Mientras se vincula, no necesita preocuparse-fPIC
. Solía ser el caso de que al compilar la biblioteca compartida, tenía que asegurarse de que-fPIC
se utilizara para todos los archivos de objetos que se construirían en la biblioteca compartida. Las reglas pueden haber cambiado porque los compiladores compilan con el código PIC de forma predeterminada en estos días. Entonces, lo que era crítico hace 20 años, y podría haber sido importante hace 7 años, es menos importante en estos días, creo. Las direcciones fuera del núcleo o / s son 'siempre' direcciones virtuales '.-fPIC
. Sin pasar esta bandera, ¿el código generado al compilar el .so debe cargarse en direcciones virtuales específicas que podrían estar en uso?Añadiendo más ...
Todos los procesos tienen el mismo espacio de direcciones virtuales (si la aleatorización de la dirección virtual se detiene mediante el uso de un indicador en el sistema operativo Linux) (para obtener más detalles, deshabilite y vuelva a habilitar la aleatorización del diseño 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 un objeto compartido al exe, entonces 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 as dentro de .so siempre tendrá dirección virtual diferente según el proceso al que se vincula.
Por lo tanto, un proceso puede proporcionar una dirección de inicio a .so como 0x45678910 en su propio espacio virtual y otro proceso al mismo tiempo puede proporcionar una dirección de inicio de 0x12131415 y, si no utilizan el direccionamiento relativo, .so no funcionará en absoluto.
Por lo tanto, siempre tienen que usar el modo de direccionamiento relativo y, por lo tanto, la opción fpic.
fuente
El enlace a una función en una biblioteca dinámica se resuelve cuando la biblioteca se carga 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, porque una dirección fija puede chocar con otra biblioteca dinámica que requiere la misma dirección.
Existen 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 ajustarse a la dirección de carga real. La reubicación se realiza mediante el vinculador 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 tipo Unix usan código independiente de la posición de forma predeterminada. Esto es menos eficiente que la reubicación si el programa se ejecuta durante mucho tiempo, especialmente en modo de 32 bits.
El nombre " código independiente de la posición " en realidad implica lo siguiente:
La sección de código no contiene direcciones absolutas que necesiten reubicación, sino solo direcciones relativas. Por lo tanto, la sección de código puede cargarse en una dirección de memoria arbitraria y compartirse entre múltiples procesos.
La sección de datos no se comparte entre múltiples procesos porque a menudo contiene datos que se pueden escribir. 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 pueden anularse 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 llame desde main, sino también cuando se llame 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 accede desde el objeto compartido.
Esta llamada interposición de símbolos pretende imitar 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 desplazamiento global (GOT) para implementar esta función de "anulación". Todos los accesos a funciones y variables públicas pasan por estas tablas.
ps Cuando no se puede evitar el enlace dinámico, hay varias formas de evitar las características 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
fuente
Una pequeña adición a las respuestas ya publicadas: los archivos de objetos no compilados para ser independientes de la posición son reubicables; contienen entradas de tabla de reubicación.
Estas entradas permiten al cargador (ese bit de código que carga un programa en la memoria) reescribir 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 dirección 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 se vinculan a una biblioteca específica tienen un árbol de dependencia de biblioteca bastante fijo, esto tiene éxito la mayor parte del tiempo. En esos casos raros donde 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, uno 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 reubicarán, esto hace que sea mucho menos probable que se deba copiar una biblioteca compartida.
fuente