Когда ассемблеру лучше использовать расширенное перемещение знака, например, 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 для обоих вышеперечисленных. Обновление: фиксация краевого назма 6377180 после 2.11 приводит к тому же выходу газа, который выглядел как ошибка, как упоминал Росс.

Я объяснил, что я думаю, что понимаю R_X86_64_32S по адресу: /questions/45357824/chto-oznachayut-peremescheniya-rx866432s-i-rx866464/45357846#45357846

2 ответа

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

С непосредственным перемещением данных ассемблер просто делает то, что вы написали. Запись в 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-адресация занимает меньше байтов команд, поэтому, когда это возможно, она должна быть предпочтительнее абсолютной адресации.

Другие вопросы по тегам