2011-10-23 13 views
7

intentar compilar código no PIC en una biblioteca compartida en x64 con gcc resulta en un error, algo así como:¿Por qué gcc fuerza PIC para x64 libs compartidas?

/usr/bin/ld: /tmp/ccQ2ttcT.o: relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC 

Esta pregunta trata sobre por qué esto es así. Sé que x64 tiene un direccionamiento relativo al RIP que fue diseñado para hacer que el código PIC sea más eficiente. Sin embargo, esto no significa que la reubicación en tiempo de carga no pueda (en teoría) aplicarse a dicho código.

Algunas fuentes en línea, incluyendo this one (que es ampliamente citado en este tema) afirman que hay alguna limitación inherente que prohíbe código no PIC en bibliotecas compartidas, porque de RIP-direccionamiento relativo. No entiendo por qué esto es verdad.

Considere "viejo x86" - una instrucción call también tiene un operando relativo a IP. Y, sin embargo, el código x86 con call en él compila muy bien en una lib compartida sin PIC, pero usando el load-time relocationR_386_PC32. ¿No se puede hacer lo mismo con el direccionamiento relativo de RIP de datos en x64?

Tenga en cuenta que entiendo completamente los beneficios del código PIC, y que el direccionamiento relativo al RIP de penalización de rendimiento lo ayuda a aliviar. Aún así, tengo curiosidad sobre el motivo por el cual no se permite el uso de código que no sea PIC. ¿Hay un razonamiento técnico real detrás de esto, o es solo para animar a escribir el código PIC?

+0

@ user786653: Estoy familiarizado con ese (excelente) documento pero no creo que tenga una respuesta para mi pregunta. Si piensas lo contrario, dime dónde encontrarlo –

+0

@ user786653: gracias, eres extremadamente útil hoy. Mencioné este mismo enlace en mi pregunta. Me alegro de que lo hayas leído antes de contestar –

+0

Lo siento, me tropecé con eso más tarde y había olvidado que estaba enlazado en tu q. – user786653

Respuesta

12

Aquí es la mejor explicación que he leído de a post on comp.unix.programmer:

bibliotecas compartidas necesitan PIC en x86-64, o más exactamente, el código reubicable tiene que ser PIC. Esto se debe a que un operando de dirección inmediata de 32 bits utilizado en el código podría necesitar más de 32 bits después de la reubicación. Si sucede esto, no hay ningún lugar para escribir el nuevo valor.

+0

Hmm ... esto tiene sentido (aunque es difícil de creer que esto sería una verdadera limitación en estos días) –

+2

@EliBendersky Su comentario "limitación real en estos días" hace * no * sentido. Solo hay 32 bits disponibles en la codificación de instrucciones 'CALL absolute-address'. Si la llamada necesita ir más de 2GB (en cualquier dirección), entonces el cargador de tiempo de ejecución no tiene suficientes bits para escribir la nueva dirección de destino. Y cambiar la codificación de insturction x86 es lo que AMD trató de evitar cuando crearon x86_64. –

+0

@EmployedRussian: Lo que quise decir es que es difícil creer que haya programas que necesiten más de 2 GB de espacio de código. ¿Estás familiarizado con alguno? –

2

El problema es que el código PIC y el código no PIC siguen siendo diferentes.

fuente en C:

extern int x; 
void func(void) { x += 1; } 

Asamblea, no PIC:

addl $1, x(%rip) 

Asamblea, con PIC:

movq [email protected](%rip), %rax 
addl $1, (%rax) 

Por lo tanto, parece que el código PIC tiene que pasar por una tabla de reubicación para acceder a las variables globales. En realidad, tiene que hacer lo mismo para las funciones, pero puede hacer funciones a través de los resguardos creados en tiempo de enlace. Esto es transparente en el nivel de ensamblaje, mientras que el acceso a globales no lo es. (Si usted necesita la dirección de una función, sin embargo, a continuación, PIC y no PIC son diferentes, al igual que las variables globales.) Tenga en cuenta que si cambia el código de la siguiente manera:

__attribute__((visibility("hidden"))) extern int x; 

En este caso, dado GCC sabe que el símbolo debe residir en el mismo objeto que el código, emite el mismo código que la versión que no es PIC.

+0

Interesante, pero sinceramente no estoy seguro de cómo responde la pregunta. ¿Podrías aclararlo por favor? –

+0

Bastante seguro x86_64 Linux no realiza reubicaciones en tiempo de carga (a código), por lo que el PIC es diferente. ¿O estabas preguntando por qué x86_64 no realiza reubicaciones en tiempo de carga? Porque podría ... –

+0

Me preguntaba por qué x86_64 no hace las reubicaciones en tiempo de carga. Sé que se puede forzar a hacerlas, pero realmente no quiere hacerlo, por defecto. O para ser más precisos, 'gcc' no quiere –

1

Solo diga algo adicional.

En url provided in the question, menciona que puede pasar -mcmodel=large a gcc para indicar al compilador que genere un operando de dirección inmediata de 64 bits para su código.

Por lo tanto, gcc -mcmodel=large -shared a.c generará un objeto compartido no PIC.

-

Demos:

A.C.:

#include <stdio.h> 

void foo(void) 
{ 
    printf("%p\n", main); 
} 

32-bit de dirección de operando inmediato te impide la generación de objeto no PIC.

[email protected] ~ $ cc -shared -o a.so a.c 
/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/cck3FWeL.o: relocation R_X86_64_32 against `main' can not be used when making a shared object; recompile with -fPIC 
/tmp/cck3FWeL.o: error adding symbols: Bad value 
collect2: error: ld returned 1 exit status 

Use -mcmodel=large para resolverlo. (Las advertencias aparecen sólo en mi sistema debido a la modificación de .text está prohibido por mi kernel pax.)

[email protected] ~ $ cc -mcmodel=large -shared -o a.so a.c 
/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/ccZ3b9Xk.o: warning: relocation in readonly section `.text'. 
/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/../../../../x86_64-pc-linux-gnu/bin/ld: warning: creating a DT_TEXTREL in object. 

ahora se puede ver el tipo de la entrada de la reubicación es R_X86_64_64 en lugar de R_X86_64_32, R_X86_64_PLT32, R_X86_64_PLTOFF64.

[email protected] ~ $ objdump -R a.so 
a.so:  file format elf64-x86-64 
DYNAMIC RELOCATION RECORDS 
OFFSET   TYPE    VALUE 
... 
0000000000000758 R_X86_64_64  printf 
... 

Y en mi sistema, enlazar este objeto compartido a un código normal y ejecutar el programa emitirá errores como: ./a.out: error while loading shared libraries: ./a.so: cannot make segment writable for relocation: Permission denied

Esto demuestra cargador dinámico se Tring para hacer reubicaciones en .text cuales La biblioteca de PIC no lo hará.

Cuestiones relacionadas