что такое инструкция jmpl в x86? - PullRequest
0 голосов
/ 27 января 2019

x86 сборка имеет суффикс инструкции, такой как l(long), w(word), b(byte).
Так что я подумал, что jmpl будет long jmp

Но это работало довольно страннокогда я его скомпилирую.
См. пример ниже.

Test1 : сборка

main:
  jmp main

Test1 : результат компиляции

eb fe     jmp 0x0804839b <main> 


Test2: сборка

main:
  jmpl main # added l suffix

Test2 : Результат компиляции

ff 25 9b 83 04 08   jmp *0x0804839b


По сравнению с Test1, Test2 результат неожиданный.
Я думаю, что он должен быть скомпилирован так же, как Test1.


Вопрос:
Является ли jmpl чем-то другим, инструкция в 8086дизайн?
(согласно здесь , jmpl в SPARK означает ссылку jmp. Это что-то вроде этого?)

... Или это просто ошибка в ассемблере gnu?

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Суффикс размера операнда l подразумевает косвенный jmp, в отличие от calll main, который все еще является относительным близким вызовом. Это несоответствие является чистым безумием в дизайне синтаксиса AT & T.

(А поскольку вы используете его с операндом, таким как main, он становится переходом в память, выполняющим загрузку данныхот main и используя его в качестве нового значения EIP.)

Вам никогда не нужно использовать мнемонику jmpl, вы можете и должны указывать косвенные переходы, используя * для операнда .Например, jmp *%eax для установки EIP = EAX, jmp *4(%edi, %ecx, 4) для индексации таблицы переходов или jmp *func_pointer.Использование jmpl необязательно во всех этих случаях.

Вы можете использовать jmpw *%ax для усечения EIP до 16-битного значения.Это собирается как 66 ff e0 jmpw *%ax)


Сравнить Что такое инструкция callq? и В чем разница между retq и ret? , это просто операнд-суффикс размера, ведущий себя так, как вы ожидали, такой же, как обычный call или простой ret.Но jmp отличается.


полу-связано: дальний JMP или вызов (к новому CS: [ER] IP) в синтаксисе AT & T равен ljmp / lcall.Они очень разные.


Также безумие, что ГАЗ принимает jmpl main как эквивалент jmpl *main.Он только предупреждает вместо ошибки .

$ gcc -no-pie -fno-pie -m32 jmp.s 
jmp.s: Assembler messages:
jmp.s:3: Warning: indirect jmp without `*'

И затем разбирает его, чтобы посмотреть, что мы получили, с помощью objdump -drwC a.out:

08049156 <main>:                                          # corresponding source line (added by hand)
 8049156:       ff 25 56 91 04 08       jmp    *0x8049156    # jmpl main
 804915c:       ff 25 56 91 04 08       jmp    *0x8049156    # jmp  *main
 8049162:       ff 25 56 91 04 08       jmp    *0x8049156    # jmpl *main

08049168 <foo>:
 8049168:       e8 fb ff ff ff          call   8049168 <foo> # calll foo
 804916d:       ff 15 68 91 04 08       call   *0x8049168    # calll *foo
 8049173:       ff 15 68 91 04 08       call   *0x8049168    # call  *foo

Мы получаем то же самое, если мызамените l на q в источнике и создайте без -m32 (используя значение по умолчанию -m64).Включая то же предупреждение о пропавшем *.Но в разборке есть явные jmpq и callq для каждой инструкции.(За исключением относительного прямого jmp, который я добавил, который использует мнемонику jmp в разборке.)

Это похоже на то, что objdump думает, что 32-битный размер операнда по умолчанию для jmp / call в обоих 32и в 64-битном режиме, поэтому он хочет всегда использовать суффикс q в 64-битном режиме, но оставляет его неявным в 32-битном режиме.В любом случае, это просто выбор дизассемблирования между неявными / явными суффиксами размера, без странностей для программиста, пишущего исходный код.


Другие ассемблеры AT & T-синтаксиса:

  • Встроенный ассемблер Clang отклоняет jmpl main, требуя jmpl *main.

    $ clang -m32 jmp.s
    jmp.s:3:8: error: invalid operand for instruction
      jmpl main
           ^~~~
    

    calll main - это то же самое, что и call main.call *main и calll *main оба допускаются для косвенных переходов.

  • Синтаксический режим YASM GAS объединяет jmpl main в почти относительный jmp, как jmp main! Так что он не согласен с gcc / clang по поводу jmpl, подразумевающего косвенный.(Очень мало людей используют YASM в режиме GAS; и в настоящее время его обслуживание не поспевает за NASM для новых инструкций, таких как AVX512. Мне нравятся хорошие значения по умолчанию YASM для длинных NOP, но в противном случае я бы рекомендовал NASM.)

0 голосов
/ 27 января 2019

Вы стали жертвой ужасного синтаксиса AT & T.

В сборке x86 есть суффикс инструкций, например, l (long), w (word), b (byte).

Нет, это не так.У мерзости, которая является синтаксисом AT & T, есть это.

Является ли jmpl чем-то другим.

Да, это косвенный переход к абсолютному адресу.-Near- переход к -long- адресу ljmp в синтаксисе gnu - -far-jump.
По умолчанию для перехода используется близкий переход к относительному адресу.
Обратите внимание, что синтаксис Intelдля этого скачка есть:

jmp dword [ds:0x0804839b]  //note the [] specifying the indirectness.
//or, this is the same
jmp [0x0804839b]
//or
jmp [main]
//or
jmp DWORD PTR ds:0x804839f  //the PTR makes it indirect.

Я предпочитаю [], чтобы подчеркнуть косвенность.

не переходит к 0x0804839b, но читает слово с указанного адреса и затем переходит на адрес, указанный в этом слове.В синтаксисе Intel косвенность явно.

Конечно, вы намеревались перейти к 0x0804839b (он же main :) напрямую, что делается с помощью:

Hm, most assembler do not allow absolute far jumps!  
It cannot be done.

См. Также: Как кодировать далеко абсолютный JMP / CALLинструкция в MASM?

Прыжок в ближнем / дальнем относительном направлении (почти) всегда лучше, потому что он все равно будет действителен, когда ваш код изменится, прыжок в длину может стать недействительным.Также более короткие инструкции обычно лучше, потому что они занимают меньше места в кеше команд.Ассемблер (в режиме Intel) автоматически выберет для вас правильную кодировку jmp.

SPARC
Это совершенно другой процессор, чем у x86.От другого производителя, используя другую парадигму.Очевидно, что документация SPARC не имеет никакого отношения к документам x86.

Здесь вы найдете правильную документацию для jmp.

https://www.felixcloutier.com/x86/jmp

Обратите внимание, что Intel не определяет разные синтаксисы для относительной и абсолютной формJMP.Это связано с тем, что Intel хочет, чтобы ассемблер всегда использовал короткий (относительный) прыжок, если цель не находится слишком далеко, и в этом случае используется дальний переход (jmpl в синтаксисе AT & T).
Красота заключается в том, чтоАссемблер автоматически использует правильный прыжок для вас.

Принудительное возвращение GNU к здравомыслию
Вы можете использовать

 .intel_syntax noprefix    <<-- as the first line in your assembly
 mov eax,[eax+100+ebx*2] 
 ....

Чтобы заставить GNU использовать синтаксис Intel, это вернет вещи к тому, как они были разработаныIntel и далеко от синтаксиса PDP7 , используемого GNU.

...