¿Por qué todo el código compilado no es independiente de la posición?

85

Al compilar bibliotecas compartidas en gcc, la opción -fPIC compila el código como independiente de la posición. ¿Hay alguna razón (rendimiento o de otro tipo) por la que no compilaría todo el código de forma independiente?

ojblass
fuente
2
Pero wowest no es del todo correcto. Muchas llamadas a funciones y saltos utilizan saltos relativos, por lo que ni siquiera necesitan una tabla de salto después de moverse.
Desconocido
mirando el código ensamblador generado, parece que la dirección de la función está cargada, mientras que un código no fpic parece que es simplemente un salto. ¿Estoy entendiendo mal su declaración?
ojblass
@ojblass lo que quiero decir es que algunos saltos son como "saltar 50 instrucciones por delante de aquí" o "saltar 5 instrucciones hacia atrás" en lugar de "saltar a 0x400000". Entonces, decir que tienes que cargar una dirección cada vez con -fPIC no es del todo cierto.
Desconocido
El artículo de Wikipedia proporciona una buena descripción. Básicamente, en algunas arquitecturas no hay una forma directa de saltar a una dirección relativa. Por lo tanto, PIC es más costoso de usar en esos arcos. Vea la respuesta de @EvanTeran para obtener más información.
Alexei Sholik

Respuestas:

67

Agrega una indirección. Con el código independiente de la posición, debe cargar la dirección de su función y luego saltar a ella. Normalmente, la dirección de la función ya está presente en el flujo de instrucciones.

más asombroso
fuente
33

Este artículo explica cómo funciona PIC y lo compara con la alternativa: reubicación del tiempo de carga . Creo que es relevante para tu pregunta.

Eli Bendersky
fuente
16
@Nick: No estoy de acuerdo. Si ayuda al que pregunta, es una respuesta. Señalar uno o dos artículos relevantes puede proporcionar una gran cantidad de información.
Eli Bendersky
5
No hay conclusión en esta publicación, solo un enlace a un artículo. Ni siquiera una pista de que PIC no se usa de forma predeterminada debido a problemas de rendimiento.
Nick
10
Si bien este enlace puede responder a la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden dejar de ser válidas si cambia la página enlazada.
Rob
4
@Rob: lo productivo sería sugerir una edición y no usar comentarios para quejarse. Esta respuesta tiene 4 años. En ese entonces, SO tenía reglas menos estrictas sobre cómo debería verse una respuesta
Eli Bendersky
6
Esta publicación apareció bajo "revisión" solicitando que lo hiciera y así lo hice. Alguien más lo marcó. El "comentario quejumbroso" es producido automáticamente por SO, no yo.
Rob
27

Sí, hay razones de rendimiento. Algunos accesos están efectivamente bajo otra capa de indirección para obtener la posición absoluta en la memoria.

También existe la GOT (tabla de compensación global) que almacena compensaciones de variables globales. Para mí, esto solo se ve como una tabla de reparación de IAT, que está clasificada como dependiente de la posición por wikipedia y algunas otras fuentes.

http://en.wikipedia.org/wiki/Position_independent_code

Desconocido
fuente
23

Además de la respuesta aceptada. Una cosa que perjudica mucho el rendimiento del código PIC es la falta de "direccionamiento relativo IP" en x86. Con el "direccionamiento relativo de IP" puede solicitar datos que sean X bytes del puntero de instrucción actual. Esto simplificaría mucho el código PIC.

Los saltos y las llamadas suelen ser relativos a EIP, por lo que en realidad no suponen un problema. Sin embargo, acceder a los datos requerirá un pequeño truco adicional. A veces, un registro se reservará temporalmente como un "puntero base" a los datos que requiere el código. Por ejemplo, una técnica común es abusar de la forma en que funcionan las llamadas en x86:

call label_1
.dd 0xdeadbeef
.dd 0xfeedf00d
.dd 0x11223344
label_1:
pop ebp            ; now ebp holds the address of the first dataword
                   ; this works because the call pushes the **next**
                   ; instructions address
                   ; real code follows
mov eax, [ebp + 4] ; for example i'm accessing the '0xfeedf00d' in a PIC way

Esta y otras técnicas añaden una capa de indirección a los accesos a los datos. Por ejemplo, la GOT (tabla de compensación global) utilizada por los compiladores de gcc.

x86-64 agregó un modo "relativo RIP" que hace las cosas mucho más simples.

Evan Terán
fuente
1
IIRC MIPS tampoco tiene direccionamiento relativo a PC, excepto para saltos relativos
phuclv
1
Esta es una técnica común utilizada en shellcode para obtener la dirección desde la que se ejecuta. He usado esto en algunas soluciones CTF.
sherrellbc
2

Porque la implementación de código completamente independiente de la posición agrega una restricción al generador de código que puede evitar el uso de operaciones más rápidas o agregar pasos adicionales para preservar esa restricción.

Esta podría ser una compensación aceptable para obtener multiprocesamiento sin un sistema de memoria virtual, donde confía en que los procesos no invadan la memoria de los demás y es posible que deba cargar una aplicación en particular en cualquier dirección base.

En muchos sistemas modernos, las compensaciones de rendimiento son diferentes, y un cargador de reubicación suele ser menos costoso (cuesta cada vez que se carga el código por primera vez) que lo mejor que puede hacer un optimizador si tiene rienda suelta. Además, la disponibilidad de espacios de direcciones virtuales oculta la mayor parte de la motivación para la independencia de posición en primer lugar.

RBerteig
fuente
1

Además, el hardware de memoria virtual en la mayoría de los procesadores modernos (utilizado por la mayoría de los sistemas operativos modernos) significa que una gran cantidad de código (todas las aplicaciones de espacio de usuario, salvo el uso peculiar de mmap o similares) no necesita ser independiente de la posición. Cada programa obtiene su propio espacio de direcciones que cree que comienza en cero.

smcameron
fuente
4
Pero incluso con un código PIC VM-MMU se necesita para garantizar que la misma biblioteca .so se cargue solo una vez en la memoria cuando sea utilizada por diferentes ejecutables.
mmmmmmmm
1

position-independent code tiene una sobrecarga de rendimiento en la mayoría de las arquitecturas, porque requiere un registro adicional.

Entonces, esto es para fines de rendimiento.

Eric Wang
fuente
0

Hoy en día, el sistema operativo y el compilador hacen que todo el código sea independiente de la posición. Intente compilar sin el indicador -fPIC, el código se compilará bien pero solo obtendrá una advertencia. Los sistemas operativos Windows usan una técnica llamada mapeo de memoria para lograr esto.

Govardhan Murali
fuente
-5

La pregunta data de 2009. Han pasado diez años y ahora todo el código es independiente de la posición. Eso ahora lo aplican los sistemas operativos y los compiladores. No hay forma de darse de baja. Todo el código se compila a la fuerza con PIE y se ignora el indicador -no-pic / -no-pie, como parte de esta excusa de ASLR. La razón de esto es ralentizar las aplicaciones que antes eran rápidas y vender hardware más nuevo, con el pretexto de una mayor seguridad. Eso es completamente irracional, porque ahora los grandes tamaños de memoria nos permiten deshacernos del infierno de los enlaces dinámicos, compilando todas las aplicaciones de forma estática.

Lo mismo sucedió antes, cuando la gente aceptó silenciosamente el modo real y otras libertades que les estaban quitando. Y les recuerdo, MMU incurre en una gran desaceleración, debido a los cambios de contexto y la latencia de traducción de direcciones. No encontrará MMU en sistemas de rendimiento crítico, como los que utilizan los científicos para muestrear experimentos de física.

No te quejas, porque ni siquiera sabes que tu código está siendo obstaculizado por todas estas ruedas de entrenamiento. ¿Qué puedo decir? ¡Disfruta de un software 2 veces más lento con su PIC ahora! Aún más, con la llegada de LLVM, pronto se aplicará JIT (código administrado), sin acceso al ensamblaje en línea x86, lo que ralentizará aún más cualquier código C / C ++. "Aquellos que sacrifican la libertad por la seguridad no se lo merecen".

SmugLispWeenie
fuente
Eso es solo una declaración de hechos: hace 10 años, el PIC era opcional, pero hoy es predeterminado y obligatorio. Dudo que el código que no sea PIE sea compatible con otras versiones del sistema operativo. Al igual que la compatibilidad con el modo real se eliminó después de Windows 9x. Por lo tanto, la cuestión de usar o no PIC se vuelve más un tema teórico de la informática, a menos que de alguna manera desbloquee su sistema operativo y vuelva a habilitar el soporte para él. Lo más importante que la gente debe saber sobre PIC es que es lo suficientemente lento como para que los compiladores admitieran hasta ahora la compilación estática, y había versiones estáticas de la mayoría de las DLL.
SmugLispWeenie
1
Sus primeras dos frases son solo una declaración de hechos. El resto es opinión, rozando la conspiración.
Mitch Lindgren
Bueno, solo habla con la gente, pide su opinión al respecto. Personalmente, descubrí que PIC vs no PIC también se convirtió en una cuestión de ideología. PIC es el equivalente de programación del comunismo, donde el código se produce en masa y todos obtienen la misma copia. Non-PIC es un equivalente de programación de Capitalism, donde hay muchas versiones en competencia del mismo código. Entonces, las personas con una mentalidad más izquierdista apoyan inconscientemente al PIC para demostrar que su ideología favorita podría funcionar al menos en la informática. Estas mismas personas le aconsejarían que no utilice libpng modificado personalmente.
SmugLispWeenie
2
¿No podemos tener diatribas políticas en un sitio web de programación por favor, gracias
Ryan McCampbell