Чтобы все это имело смысл, сначала необходимо:
Стандарты
R_X86_64_64
, R_X86_64_32
и R_X86_64_32S
определены в Система 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 ABI 32
: 64-битный адрес метки s
усекается до 32-битного адреса, потому что мы указали только .long
(4 байта) .text
: мы находимся в разделе .text
0xc
: это addend , которое является полем перемещенияпри вводе
Адрес перемещения вычисляется как:
A + S
Где:
A
: добавление, здесь 0xC
S
: значение символа до перемещения, здесь 00 00 00 00 == 0
Поэтому после перемещения новый адрес будет 0xC == 12 байт в .text
section.
Это именно то, что мы ожидаем, поскольку 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?
R_X86_64_32S и PIE (независимые от позиции исполняемые файлы
R_X86_64_32S не может использоваться в позиционно-независимых исполняемых файлах, например сделано с gcc -pie
, в противном случае ссылка завершится ошибкой:
relocation R_X86_64_32S against `.text' can not be used when making a PIE object; recompile with -fPIC
л
Я привел минимальный пример, объясняющий это по адресу: Что такое опция -fPIE для независимых от позиции исполняемых файлов в gcc и ld?