Я уже скомпилирован с -fPIC, так что не так?
Эта часть сообщения об ошибке предназначена для людей, которые связывают сгенерированный компилятором код.
Вы пишете asm вручную, поэтому, как правильно написал datenwolf, при написании разделяемой библиотеки в сборке вы должны позаботиться о том, чтобы код не зависел от позиции.
Это означает, что файл не должен содержать никаких32-битные абсолютные адреса (поскольку перемещение на произвольную 64-битную базу невозможно).64-разрядные абсолютные перемещения поддерживаются , но обычно их следует использовать только для таблиц переходов.
mov var1, %rcx
использует 32-разрядный режим абсолютной адресации .Обычно вы никогда не должны делать этого, даже в позиционно-зависимом коде x86-64.Обычные сценарии использования для 32-разрядных абсолютных адресов: помещение адреса в 64-разрядный регистр с mov $var1, %edi
(расширение нуля в RDI)
и индексация статических массивов: mov arr(,%rdx,4), %edx
mov var1(%rip), %rcx
использует относительное RIP 32-битное смещение .Это эффективный способ обращения со статическими данными, и компиляторы всегда используют его даже без -fPIE
или -fPIC
для статических / глобальных переменных.
У вас есть в основном две возможности:
Обычные частные библиотечные статические данные , подобно компиляторам C, будут иметь для __attribute__((visibility("hidden"))) long var1;
, то же самое, что и для -fno-PIC
.
.data
.globl var1 # linkable from other .o files in the same shared object / library
.hidden var1 # not visible for *dynamic* linking outside the library
var1:
.quad 0x012345
.text
.globl func1
func1:
xor %eax, %eax # return 0
mov var1(%rip), %rcx
ret
полный код с учетом взаимного расположения символов, который генерируют компиляторы для -fPIC
.
Вам необходимо использовать таблицу глобальных смещений.Вот как это делает компилятор, если вы скажете ему создать код для разделяемой библиотеки.Обратите внимание, что это приводит к снижению производительности из-за дополнительной косвенности.
См. Извините за состояние динамических библиотек в Linux для получения дополнительной информации о взаимозаменяемости символов и накладных расходах, которые это накладывает на генерацию кода для разделяемых библиотек, если вы не осторожны с ограничением видимости символов, чтобыinlining.
var1@GOTPCREL
- это адрес указателя на ваш var1
, сам указатель доступен с помощью относительной rip-адресации, а содержимое (адрес var1
) заполняется компоновщикомво время загрузки библиотеки.Это поддерживает случай, когда программа, использующая вашу библиотеку, определила var1
, поэтому var1
в вашей библиотеке должен разрешаться в эту область памяти вместо той, которая находится в .data
или .bss
(или .text
) вашего .so
.
.section .data
.globl var1
# without .hidden
var1:
.quad 0x012345
.section .text
.globl func1
func1:
xor %eax, %eax
mov var1@GOTPCREL(%rip), %rcx
mov (%rcx), %rcx
ret
Дополнительную информацию см. В http://www.bottomupcs.com/global_offset_tables.html
Пример использования проводника компилятора Godbolt из -fPIC
против -fPIE
показывает разницу между символами и интерполяцией для получения адреса скрытых глобальных переменных :
movl $x, %eax
5 байт, -fno-pie
leaq x(%rip), %rax
7 байтов, -fPIE
и скрытые глобалы или static
с -fPIC
y@GOTPCREL(%rip), %rax
7 байтов и нагрузкой вместо просто ALU, -fPIC
с нескрытые глобалы.
На самом деле при загрузке всегда используются x(%rip)
, за исключением не скрытых / не static
переменных с -fPIC
, где он должен сначала получить адрес времени выполнения из GOT,потому что это не постоянное смещение времени соединения относительно кода.
Связано: 32-битные абсолютные адресабольше не разрешено в x86-64 Linux? (исполняемые файлы PIE).
В предыдущей версии этого ответа говорилось, что сегменты DATA и BSS могут перемещаться относительно TEXT при загрузке динамической библиотеки.Это неверно, перемещается только базовый адрес библиотеки.RIP-относительный доступ к другим сегментам в той же библиотеке гарантированно будет в порядке, и компиляторы выдают код, который делает это.Заголовки ELF определяют, как сегменты (которые содержат разделы) должны быть загружены / отображены в память.