Jiw*_*won 6 x86 assembly gnu-assembler att
x86 汇编设计有指令后缀,如l(long), w(word), b(byte)。
所以我认为这jmpl是long jmp
但是当我编译它时它的工作很奇怪。
见下面的例子。
测试1:组装
main:
jmp main
Run Code Online (Sandbox Code Playgroud)
测试1:编译结果
eb fe jmp 0x0804839b <main>
Run Code Online (Sandbox Code Playgroud)
测试2:组装
main:
jmpl main # added l suffix
Run Code Online (Sandbox Code Playgroud)
测试2:编译结果
ff 25 9b 83 04 08 jmp *0x0804839b
Run Code Online (Sandbox Code Playgroud)
与Test1相比,Test2的结果出乎意料。
我认为它应该与Test1一样编译。
问:
是jmpl什么不同的指令在8086的设计?
(根据这里,jmpl在 SPARK 中意味着 jmp 链接。是这样的吗?)
...或者这只是 gnu 汇编程序上的错误?
你已经成为 AT&T 语法可怕的受害者。
x86汇编设计有指令后缀,如l(long)、w(word)、b(byte)。
不,它没有。AT&T 语法的可憎之处就是这样。
在理智的英特尔语法中,没有这样的后缀。
jmpl 有什么不同。
是的,这是到绝对地址的间接跳转。ljmpgnu 语法中-near- 跳转到-long- 地址是-far- 跳转。
跳转的默认值是近跳转,到相对地址。
请注意,此跳转的 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.
Run Code Online (Sandbox Code Playgroud)
我更喜欢[], 以突出间接性。
它并不会跳转到0x0804839b,但读取来自指定地址的DWORD,然后跳转到这个DWORD指定的地址。在 Intel 语法中,间接性是明确的。
当然,您打算直接跳转到 0x0804839b(又名 main:),这是通过以下方式完成的:
Hm, most assembler do not allow absolute far jumps!
It cannot be done.
Run Code Online (Sandbox Code Playgroud)
另请参阅:如何在 MASM 中编写远绝对 JMP/CALL 指令?
近/远相对跳转(几乎)总是更好,因为当您的代码更改时它仍然有效,长跳转可能变得无效。此外,较短的指令通常更好,因为它们在指令缓存中占用的空间较少。汇编器(在 Intel 模式下)将自动为您选择正确的 jmp 编码。
SPARC
这是一个与 x86 完全不同的处理器。来自不同的制造商,使用不同的范例。显然,SPARC 文档与 x86 文档无关。
正确的文档在jmp这里。
https://www.felixcloutier.com/x86/jmp
请注意,英特尔没有为 jmp 的相对和绝对形式指定不同的语法。这是因为 Intel 希望汇编器始终使用短(相对)跳转,除非目标太远,在这种情况下使用远跳转(AT&T 语法中的 jmpl)。
这样做的好处是汇编器会自动为您使用正确的跳转。
强制 gnu 恢复理智
您可以使用
.intel_syntax noprefix <<-- as the first line in your assembly
mov eax,[eax+100+ebx*2]
....
Run Code Online (Sandbox Code Playgroud)
为了让 gnu 使用 Intel 语法,这将使事情回到 Intel 设计的方式,并远离gnu 使用的PDP7 语法。
操作数大小后缀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 有什么区别?,这只是操作数大小后缀的行为就像您预期的那样,与 plaincall或 plain相同ret。但又jmp不同。
半相关:AT&T 语法中的远跳转或调用(到新的 CS:[ER]IP)是 ljmp / lcall。这些是非常不同的。
GAS 接受等同jmpl main于jmpl *main. 它只是警告而不是错误。
$ gcc -no-pie -fno-pie -m32 jmp.s
jmp.s: Assembler messages:
jmp.s:3: Warning: indirect jmp without `*'
Run Code Online (Sandbox Code Playgroud)
然后将其拆开看看我们得到了什么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
Run Code Online (Sandbox Code Playgroud)
如果我们在源代码中替换lwith ,然后构建 without (使用默认的),我们会得到同样的结果。包括关于失踪的相同警告。但反汇编在每条指令上都有明确的指示。(除了我添加的相对直接,它在反汇编中使用助记符。)q-m32-m64*jmpqcallqjmpjmp
就像 objdump 认为 32 位是 32 位和 64 位模式下 jmp/call 的默认操作数大小,因此它希望始终q在 64 位中使用后缀,但在 32 位模式中将其隐式保留。不管怎样,这只是隐式/显式大小后缀之间的反汇编选择,对于编写源代码的程序员来说并没有什么奇怪的。
Clang 的内置汇编器确实拒绝jmpl main,要求jmpl *main.
$ clang -m32 jmp.s
jmp.s:3:8: error: invalid operand for instruction
jmpl main
^~~~
Run Code Online (Sandbox Code Playgroud)
calll main是相同的call main。 call *main并且calll *main都被接受用于间接跳转。
YASM 的 GAS 语法模式汇编jmpl main为近乎相关的 jmp,例如jmp main! 所以它不同意 gcc/clang 关于jmpl暗示间接的观点。(很少有人在 GAS 模式下使用 YASM;如今,对于像 AVX512 这样的新指令,它的维护并没有跟上 NASM 的步伐。我喜欢 YASM 对于长 NOP 的良好默认设置,但除此之外我会推荐 NASM。)