Чтобы все это имело смысл, вы должны сначала:
Стандарты
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 ABI
32
: 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?
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
l
Я привел минимальный пример, объясняющий это, по адресу: Что такое параметр -fPIE для независимых от позиции исполняемых файлов в gcc и ld?
person
Ciro Santilli 新疆再教育营六四事件ۍ
schedule
22.10.2015
mov $symbol, %edi
вместоlea symbol(%rip), %rdi
. См. 32-разрядные абсолютные адреса больше не разрешены в x86-64 Linux? для получения информации об использованииgcc -no-pie -fno-pie
для создания традиционных исполняемых файлов, зависящих от позиции, если вы этого хотите. - person Peter Cordes   schedule 05.11.2017