2015-10-24 4 views
2

В качестве конкретного примера, на ГАЗ 2.24, перемещая адрес:Когда лучше для ассемблера использовать расширенное перемещение знака, например R_X86_64_32S, вместо нулевого расширения, такого как R_X86_64_32?

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 на: https://stackoverflow.com/a/33289761/895245

+0

Может быть, это просто видно, что значение мало (особенно ноль!), Но не важно оптимизировать адресные нагрузки? –

+0

@BoPersson, как он может знать, будут ли адреса мал (здесь 5) после ссылки? Если я использую скрипт компоновщика с '. = 0xFFFFFFFF80000000; 'как в этом примере http://stackoverflow.com/a/33289761/895245 он разбивается на' _32', но не '_32S'. –

ответ

6

Разница в разрешенных адресов для символа 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.

+0

А, хорошо, я забыл, что 'mov% eax' [zero extends] (http://stackoverflow.com/questions/11177137/why-do-most-x64-instructions-zero-the-upper-part-of -a-32-bit-register) ... и я понятия не имел, что 32-битный адрес расшифровывается! –

1

С движком немедленных данных ассемблер просто делает то, что вы написали. Запись в 32-битный регистр всегда zero-extends upper32 в x86-64. Как указано в Intel insn иого руководства:

  • MOV r/m64, imm32 означает: Переместить imm32 знака продлен до 64-битого r/m64.
  • MOV r/m32, imm32 средства: Переместить imm32 в r/m32.

Если вы хотите подписать-расширение, чтобы соответствовать как 32-битные адреса обрабатываются в 32-битном-абсолют режимов адресации, вы должны иметь письменные

mov $s, %rax 

32bit смещения всегда знаковое расширение. Поэтому я считаю, что ответ Росса прав, что NASM 2.10.09 глючит. По-видимому, он говорит компоновщику, что адрес будет нулевым, если на самом деле он будет расширен. Конечно, RIP-относительная адресация занимает меньше байтов инструкций, поэтому, когда это возможно, должно быть предпочтительнее абсолютной адресации.