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?
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?
Respuestas:
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.
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.
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.
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.
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.
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.
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.
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".