gcc linker

¿Cuál es la opción-fPIE para ejecutables independientes de posición en gcc y ld?



linker (2)

¿Cómo cambiará el código, por ejemplo, llamadas de función?


PIE es para apoyar la aleatoriedad de diseño de espacio de direcciones (ASLR) en archivos ejecutables.

Antes de que se creara el modo PIE, el ejecutable del programa no se podía colocar en una dirección aleatoria en la memoria, solo las bibliotecas dinámicas de código de posición independiente (PIC) podían reubicarse en un desplazamiento aleatorio. Funciona muy parecido a lo que PIC hace para las bibliotecas dinámicas, la diferencia es que no se crea una Tabla de vinculación de procedimientos (PLT), sino que se usa la reubicación relativa de PC.

Después de habilitar el soporte de PIE en gcc / linkers, el cuerpo del programa se compila y se vincula como código de posición independiente. Un vinculador dinámico realiza el proceso de reubicación completo en el módulo de programa, al igual que las bibliotecas dinámicas. Cualquier uso de datos globales se convierte en acceso a través de la Tabla de Compensaciones Globales (GOT) y se agregan las reubicaciones GOT.

PIE está bien descrito en esta presentación de OpenBSD PIE .

Los cambios a las funciones se muestran en esta diapositiva (PIE vs PIC).

x86 pic vs pie

Las variables y funciones globales locales se optimizan en forma circular

Las variables y funciones globales externas son lo mismo que pic

y en esta diapositiva (PIE vs enlace de estilo antiguo)

pastel x86 vs sin banderas (fijo)

Las variables y funciones globales locales son similares a las fijas

Las variables y funciones globales externas son lo mismo que pic

Tenga en cuenta que ese pastel puede ser incompatible con -static


Ejemplo ejecutable mínimo: GDB el ejecutable dos veces

Para aquellos que quieren ver algo de acción:

printf '' #include <stdio.h> int main() { puts("hello world"); } '' > main.c gcc -std=c99 -pie -fpie -ggdb3 -o pie main.c gcc -std=c99 -no-pie -fno-pie -ggdb3 -o nopie main.c echo 2 | sudo tee /proc/sys/kernel/randomize_va_space gdb -batch -nh -ex ''set disable-randomization off'' / -ex ''start'' -ex ''info line'' / -ex ''start'' -ex ''info line'' / ./pie gdb -batch -nh -ex ''set disable-randomization off'' / -ex ''start'' -ex ''info line'' / -ex ''start'' -ex ''info line'' / ./nopie

Para el que tiene -pie , vemos que la dirección de los cambios main entre ejecuciones:

Temporary breakpoint 1 at 0x7a9: file memory_layout.c, line 31. [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Temporary breakpoint 1, main (argc=1, argv=0x7ffe57d75318) at memory_layout.c:31 31 int main(int argc, char **argv) { Line 31 of "memory_layout.c" starts at address 0x55db0066b79a <main> and ends at 0x55db0066b7a9 <main+15>. Temporary breakpoint 2 at 0x55db0066b7a9: file memory_layout.c, line 31. [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Temporary breakpoint 2, main (argc=1, argv=0x7ffd03b67d68) at memory_layout.c:31 31 int main(int argc, char **argv) { Line 31 of "memory_layout.c" starts at address 0x563910ccd79a <main> and ends at 0x563910ccd7a9 <main+15>.

entonces en este ejemplo, la dirección para la primera ejecución fue 0x55db0066b79a y para la segunda 0x563910ccd79a .

Pero para el que tiene -no-pie , la dirección de main sigue siendo la misma 0x400627 para ambas ejecuciones:

Temporary breakpoint 1 at 0x400636: file ./memory_layout.c, line 28. Temporary breakpoint 1, main (argc=1, argv=0x7ffd5f69c8b8) at ./memory_layout.c:28 warning: Source file is more recent than executable. 28 int bss = 0; Line 28 of "./memory_layout.c" starts at address 0x400627 <main> and ends at 0x400636 <main+15>. Temporary breakpoint 2 at 0x400636: file ./memory_layout.c, line 28. Temporary breakpoint 2, main (argc=1, argv=0x7ffdd9f74bd8) at ./memory_layout.c:28 28 int bss = 0; Line 28 of "./memory_layout.c" starts at address 0x400627 <main> and ends at 0x400636 <main+15>.

echo 2 | sudo tee /proc/sys/kernel/randomize_va_space echo 2 | sudo tee /proc/sys/kernel/randomize_va_space asegura que ASLR está activado (el predeterminado en Ubuntu 17.10): ¿Cómo puedo deshabilitar temporalmente ASLR (aleatorización de diseño de espacio de direcciones)? | Pregunta a Ubuntu .

set disable-randomization off lo contrario, GDB, como su nombre indica, desactiva ASLR para el proceso de forma predeterminada para dar direcciones fijas en todas las ejecuciones para mejorar la experiencia de depuración: ¿ Diferencia entre direcciones gdb y direcciones "reales"? | Desbordamiento de pila

readelf diversión

Además, también podemos observar que:

readelf -s ./nopie | grep main

da la dirección de carga del tiempo de ejecución real:

69: 0000000000400627 370 FUNC GLOBAL DEFAULT 13 main

mientras:

readelf -s ./pie | grep main

solo da un desplazamiento:

70: 000000000000079a 401 FUNC GLOBAL DEFAULT 14 main

Al desactivar ASLR (con randomize_va_space o con set disable-randomization off ), GDB siempre da la dirección main : 0x5555555547a9 , por lo que deducimos que la dirección -pie se compone de:

0x555555554000 + random offset + symbol offset (79a)

¿TODO donde está 0x555555554000 codificado en el kernel de Linux / glibc loader / donde sea? ¿Cómo se determina la dirección de la sección de texto de un ejecutable de PIE en Linux?

Probado en Ubuntu 18.04.

Pregunta relacionada: ¿Cómo puedo decir, con algo como objdump, si un archivo de objeto se ha creado con -fPIC?