Что означают перемещения R_X86_64_32S и R_X86_64_64?
Получилась следующая ошибка, когда я попытался скомпилировать приложение C в 64-битной FreeBSD:
перемещение R_X86_64_32S нельзя использовать при создании общего объекта; перекомпилировать с -fPIC
Что такое R_X86_64_32S
переезд и что R_X86_64_64
?
Я погуглил об ошибке, и это возможные причины - было бы здорово, если бы кто-нибудь мог сказать, что на самом деле означает R_X86_64_32S.
6 ответов
R_X86_64_32S
а также R_X86_64_64
являются именами типов перемещения для кода, скомпилированного для архитектуры amd64. Вы можете посмотреть их все в amd64 ABI. Согласно этому, R_X86_64_64
разбивается на:
- R_X86_64 - все имена имеют префикс этого
- 64 - прямое перемещение 64 бит
а также R_X86_64_32S
чтобы:
- R_X86_64 - префикс
- 32S - усечь значение до 32 бит и расширить знак
что в основном означает "значение символа, на которое указывает это перемещение, плюс любое добавление", в обоих случаях. За R_X86_64_32S
Затем компоновщик проверяет, что сгенерированное значение знака распространяется на исходное 64-битное значение.
Теперь в исполняемом файле сегментам кода и данных присвоен указанный виртуальный базовый адрес. Исполняемый код не является общим, и каждый исполняемый файл получает свое собственное свежее адресное пространство. Это означает, что компилятор точно знает, где будет раздел данных, и может ссылаться на него напрямую. Библиотеки, с другой стороны, могут знать только, что их секция данных будет с заданным смещением от базового адреса; значение этого базового адреса может быть известно только во время выполнения. Следовательно, все библиотеки должны создаваться с кодом, который может выполняться независимо от того, где он помещен в память, известный как независимый от позиции код (или сокращенно PIC).
Теперь, когда дело доходит до решения вашей проблемы, сообщение об ошибке говорит само за себя.
Чтобы все это имело смысл, вы должны сначала:
- см. минимальный пример перемещения: /questions/5420523/kak-rabotaet-svyazyivanie-c-na-praktike/5420531#5420531
- понять основную структуру файла ELF: /questions/6449069/kak-sdelat-ispolnyaemyij-fajl-elf-v-linux-s-pomoschyu-shestnadtsaterichnogo-redaktora/6449080#6449080
стандарты
R_X86_64_64
, R_X86_64_32
а также R_X86_64_32S
Все они определены в System V AMD ABI, которая содержит особенности AMD64 формата файла ELF.
Они все возможные значения для ELF32_R_TYPE
поле записи перемещения, указанное в System V ABI 4.1 (1997), которое определяет нейтральные части архитектуры формата ELF. Этот стандарт определяет только поле, но не его значения, зависящие от арки.
В разделе 4.4.1 "Типы перемещения" мы видим сводную таблицу:
Name Field Calculation
------------ ------ -----------
R_X86_64_64 word64 A + S
R_X86_64_32 word32 A + S
R_X86_64_32S word32 A + S
Мы объясним эту таблицу позже.
И примечание:
R_X86_64_32
а такжеR_X86_64_32S
перемещения сокращают вычисленное значение до 32 бит. Линкер должен проверить, что сгенерированное значение для перемещения R_X86_64_32 (R_X86_64_32S) расширяет ноль (знак расширяет) до исходного 64-битного значения.
Пример R_X86_64_64 и R_X86_64_32
Давайте сначала посмотрим на R_X86_64_64
а также R_X86_64_32
:
.section .text
/* Both a and b contain the address of s. */
a: .long s
b: .quad s
s:
Затем:
as --64 -o main.o main.S
objdump -dzr main.o
Содержит:
0000000000000000 <a>:
0: 00 00 add %al,(%rax)
0: R_X86_64_32 .text+0xc
2: 00 00 add %al,(%rax)
0000000000000004 <b>:
4: 00 00 add %al,(%rax)
4: R_X86_64_64 .text+0xc
6: 00 00 add %al,(%rax)
8: 00 00 add %al,(%rax)
a: 00 00 add %al,(%rax)
Проверено на Ubuntu 14.04, Binutils 2.24.
Пока игнорируйте разборку (что бессмысленно, поскольку это данные) и смотрите только на метки, байты и перемещения.
Первое переселение:
0: R_X86_64_32 .text+0xc
Что значит:
0
: действует на байт 0 (меткаa
)R_X86_64_
: префикс, используемый всеми типами перемещения системы AMD64 V ABI32
: 64-битный адрес меткиs
усекается до 32-битного адреса, потому что мы указали только.long
(4 байта).text
: мы находимся на.text
раздел0xc
: это дополнение, которое является полем записи перемещения
Адрес переезда рассчитывается как:
A + S
Куда:
A
: добавление, здесь0xC
S
: значение символа до перемещения, здесь00 00 00 00 == 0
Поэтому после перемещения новый адрес будет 0xC == 12 байт в .text
раздел.
Это именно то, что мы ожидаем, так как s
приходит после .long
(4 байта) и .quad
(8 байт).
R_X86_64_64
аналогично, но проще, так как здесь нет необходимости обрезать адрес s
, Это указано стандартом через word64
вместо word32
на Field
колонка.
R_X86_64_32S против R_X86_64_32
Разница между R_X86_64_32S
против R_X86_64_32
это когда компоновщик будет жаловаться "с перемещением, усеченным до соответствия":
32
: жалуется, если усеченное значение после перемещения не обнуляет, расширяет старое значение, т.е. усеченные байты должны быть равны нулю:Например:
FF FF FF FF 80 00 00 00
в80 00 00 00
генерирует жалобу, потому чтоFF FF FF FF
не ноль.32S
: жалуется, если усеченное после перемещения значение не подписывает, расширяет старое значение.Например:
FF FF FF FF 80 00 00 00
в80 00 00 00
хорошо, потому что последний бит80 00 00 00
и все усеченные биты равны 1.
См. Также: Что означает эта ошибка GCC "... перемещение усечено до соответствия..."?
R_X86_64_32S
может быть создан с помощью:
.section .text
.global _start
_start:
mov s, %eax
s:
Затем:
as --64 -o main.o main.S
objdump -dzr main.o
дает:
0000000000000000 <_start>:
0: 8b 04 25 00 00 00 00 mov 0x0,%eax
3: R_X86_64_32S .text+0x7
Теперь мы можем наблюдать "перемещение", усеченное до 32S
со скриптом компоновщика:
SECTIONS
{
. = 0xFFFFFFFF80000000;
.text :
{
*(*)
}
}
Сейчас:
ld -Tlink.ld a.o
Это хорошо, потому что: 0xFFFFFFFF80000000
усекается в 80000000
, который является расширением знака.
Но если мы изменим скрипт компоновщика на:
. = 0xFFFF0FFF80000000;
Теперь он генерирует ошибку, потому что это 0
сделал это больше не расширением знака.
Обоснование использования 32S
для доступа к памяти, но 32
для немедленных действий: когда ассемблеру лучше использовать расширенное перемещение знака, например, R_X86_64_32S, а не нулевое расширение, например, R_X86_64_32?
Это означает, что скомпилированный общий объект без использования -fPIC
пометить как следует:
gcc -shared foo.c -o libfoo.so # Wrong
Вам нужно позвонить
gcc -shared -fPIC foo.c -o libfoo.so # Right
Под платформой ELF (Linux) общие объекты компилируются с помощью независимого от позиции кода - кода, который может выполняться из любого места в памяти, если этот флаг не задан, сгенерированный код зависит от позиции, поэтому использовать этот общий доступ невозможно объект.
Я столкнулся с этой проблемой и обнаружил, что ответ не помог мне. Я пытался связать статическую библиотеку вместе с общей библиотекой. Я также исследовал размещение ключа -fPIC ранее в командной строке (как рекомендовано в ответах в другом месте). Единственное, что решило проблему, для меня это изменение статической библиотеки на общую. Я подозреваю, что сообщение об ошибке -fPIC может произойти по ряду причин, но в основном вы хотите посмотреть на то, как создаются ваши библиотеки, и с подозрением относиться к библиотекам, которые создаются различными способами.
В моем случае проблема возникла из-за того, что программа для компиляции ожидала найти общие библиотеки в удаленном каталоге, в то время как только соответствующие статические библиотеки были там по ошибке.
На самом деле, эта ошибка перемещения была скрытой ошибкой поиска файла.
Я подробно рассказал, как я справился с этим в этой другой теме /questions/21532176/sboj-kompilyatsii-peremeschenie-rx866432-protivrodatastr18ne-mozhet-byit-ispolzovano-pri-sozdanii-obschego-obekta/21532206#21532206
Приведенный выше ответ демонстрирует, что это за перемещения, и я обнаружил, что построение объектов x86_64 с флагом GCC -mcmodel = large может предотвратить R_X86_64_32S, поскольку компилятор не имеет предположений о перемещенном адресе в этой модели.
В следующем случае:
extern int myarr[];
int test(int i)
{
return myarr[i];
}
Построен с
gcc -O2 -fno-pie -c test_array.c
и разобрать с
objdump -drz test_array.o
, у нас есть:
0: 48 63 ff movslq %edi,%rdi
3: 8b 04 bd 00 00 00 00 mov 0x0(,%rdi,4),%eax
6: R_X86_64_32S myarr
a: c3 ret
С -mcmodel = large, т.е.
gcc -mcmodel=large -O2 -fno-pie -c test_array.c
, у нас есть:
0: 48 b8 00 00 00 00 00 movabs $0x0,%rax
7: 00 00 00
2: R_X86_64_64 myarr
a: 48 63 ff movslq %edi,%rdi
d: 8b 04 b8 mov (%rax,%rdi,4),%eax
10: c3 ret