Почему gcc вызывает PIC для общих библиотек x64? - PullRequest
10 голосов
/ 23 октября 2011

Попытка скомпилировать не-PIC-код в общей библиотеке на x64 с gcc приводит к ошибке, что-то вроде:

/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

Этот вопрос о , почему этотак.Я знаю, что x64 имеет RIP-относительную адресацию, которая была разработана, чтобы сделать код PIC более эффективным.Однако это не означает, что перемещение во время загрузки не может (теоретически) применяться к такому коду.

Некоторые онлайн-источники, включая этот (который широко цитируется напроблема) утверждают, что существует некоторое внутреннее ограничение, запрещающее не-PIC-код в совместно используемых библиотеках, из-за относительной RIP-адресации.Я не понимаю, почему это так.

Рассмотрим "старый x86" - инструкция call также имеет IP-относительный операнд.И все же код x86 с call в нем просто прекрасно компилируется в общую библиотеку без PIC, но с использованием перемещения во время загрузки R_386_PC32.Разве нельзя сделать то же самое для адресации RIP-данных в x64?

Обратите внимание, что я полностью понимаю преимущества кода PIC, и снижение производительности RIP-относительной адресации помогает уменьшить это.Тем не менее, мне любопытно узнать причину, по которой нельзя использовать код без PIC.За этим стоит реальная техническая аргументация, или это просто для того, чтобы поощрять написание кода PIC?

Ответы [ 3 ]

16 голосов
/ 23 октября 2011

Вот лучшее объяснение, которое я прочитал из поста на comp.unix.programmer :

Совместно используемым библиотекам требуется PIC на x86-64 или, точнее, перемещаемый код должен быть ПОС. Это потому, что 32-битный адресный операнд используемый в коде может потребоваться более 32 бит после перемещения. Если это происходит, некуда написать новое значение.

5 голосов
/ 25 мая 2015

Просто скажи что-нибудь дополнительное.

В url, указанном в вопросе , упоминается, что вы можете передать -mcmodel=large в gcc, чтобы сказать компилятору сгенерировать 64-битный операнд непосредственного адреса для вашего кода.

Итак, gcc -mcmodel=large -shared a.c создаст общий объект без PIC.

-

Демонстрация:

a.c:

#include <stdio.h>

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

32-битный операнд непосредственного адреса блокирует создание объекта без PIC.

xiami@gentoo ~ $ 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

Используйте -mcmodel=large, чтобы решить. (Предупреждения появляются только в моей системе, поскольку изменение в .text запрещено моим ядром PaX.)

xiami@gentoo ~ $ 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.

Теперь вы можете видеть тип записи перемещения: R_X86_64_64 вместо R_X86_64_32, R_X86_64_PLT32, R_X86_64_PLTOFF64.

xiami@gentoo ~ $ objdump -R a.so
a.so:      file format elf64-x86-64
DYNAMIC RELOCATION RECORDS
OFFSET           TYPE              VALUE 
...
0000000000000758 R_X86_64_64       printf
...

А в моей системе свяжите этот общий объект с обычным кодом и запустите программу, которая выдаст такие ошибки, как: ./a.out: error while loading shared libraries: ./a.so: cannot make segment writable for relocation: Permission denied

Это доказывает, что динамический загрузчик вынужден выполнять перемещения на .text , чего не будет в библиотеке PIC.

2 голосов
/ 23 октября 2011

Дело в том, что код PIC и код, отличный от PIC, все еще различен.

C источник:

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

Сборка, не PIC:

addl    $1, x(%rip)

Сборка с ПОС:

movq    x@GOTPCREL(%rip), %rax
addl    $1, (%rax)

Похоже, что код PIC должен пройти таблицу перемещения для доступа к глобальным переменным. Он фактически должен делать то же самое для функций, но он может выполнять функции через заглушки, созданные во время соединения. Это прозрачно на уровне сборки, а доступ к глобальным - нет. (Однако если вам нужен адрес функции, тогда PIC и не PIC отличаются, как глобальные переменные.) Обратите внимание, что если вы измените код следующим образом:

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

В этом случае, поскольку GCC знает, что символ должен находиться в том же объекте, что и код, он испускает тот же код, что и версия без PIC.

...