Когда лучше для ассемблера использовать расширенное перемещение знака, например R_X86_64_32S, вместо нулевого расширения, такого как R_X86_64_32?
В качестве конкретного примера, на GAS 2.24, перемещая адрес:
mov $s, %eax
s:
После:
as --64 -o a.o a.S
objdump -Sr a.o
Использует нулевое расширение:
0000000000000000 <s-0x5>:
0: b8 00 00 00 00 mov $0x0,%eax
1: R_X86_64_32 .text+0x5
Но доступ к памяти:
mov s, %eax
s:
Скомпилирует для подписания расширения:
0000000000000000 <s-0x7>:
0: 8b 04 25 00 00 00 00 mov 0x0,%eax
3: R_X86_64_32S .text+0x7
Есть ли смысл использовать либо в этом конкретном случае, либо вообще? Я не понимаю, как ассемблер мог лучше предположить, что в любом случае.
NASM 2.10.09 использует только R_X86_64_32
для обоих вышеперечисленных. Обновление: край nasm совершает 6377180 после того, как 2.11 производит тот же выход газа, который казался ошибкой, о которой говорил Росс.
Я объяснил, что, по-моему, я понимаю о R_X86_64_32S
по адресу: qaru.site/info/2439/...
Ответы
Ответ 1
Разница заключается в разрешенных адресах для символа s
. В первом случае с R_X86_64_32 символ должен находиться в диапазоне от 0x00000000'00000000 до 0x00000000'FFFFFFFF. Во втором случае с R_X86_64_32S адрес символа должен находиться между 0xFFFFFFFF'80000000 и 0x00000000'7FFFFFFF. Если s
заканчивается адресом за пределами этих диапазонов, тогда компоновщик выдаст ошибку.
Это соответствует тому, как ЦП интерпретирует 32-битное значение s
, закодированное в две инструкции. В первой инструкции, где это немедленный операнд, 32-битное значение равно нулю, расширенному в RAX. Во второй инструкции 32-битное значение представляет собой смещение в операнде памяти, и поэтому знак расширяется для формирования 64-разрядного адреса.
NASM не должен использовать неподписанное R_X86_64_32 перемещение для второй команды. Это не вопрос, какой из них лучше, использование R_X86_64_32 здесь просто неверно. NASM разрешил бы адрес s
быть 0x00000000'80000000, но вместо этого ЦП получал бы 0xFFFFFFFF'80000000.
Ответ 2
С немедленным движением mov ассемблер просто делает то, что вы написали. Запись в 32-битный регистр всегда равна нулю - расширяет верхнюю границу 32 в x86-64. Как указано в руководстве Intel insn ref:
-
MOV r/m64, imm32
означает: Переместить знак imm32
, расширенный до 64-бит до r/m64
.
-
MOV r/m32, imm32
означает: Переместите imm32
в r/m32
.
Если вы хотите, чтобы расширение знака соответствовало тому, как обрабатываются 32-разрядные адреса в режимах 32-разрядной абсолютной адресации, вы должны написать
mov $s, %rax
32-битные перемещения всегда расширены. Поэтому я считаю, что ответ Росса прав, что NASM 2.10.09 глючит. По-видимому, он говорит компоновщику, что адрес будет нулевым, если на самом деле он будет расширен. Конечно, RIP-относительная адресация занимает меньше байтов команд, поэтому, когда это возможно, должно быть предпочтительнее абсолютной адресации.