我试图让 yasm 输出一个 16 位近相对 jmp。具体来说,它将是带有操作数大小覆盖前缀的 rel16/rel32 jmp 操作码。我知道jmp short labelwill 发出一个 8 位近相对 jmp,ajmp long label将发出一个 32 位近相对 jmp,但如何让它发出 16 位近相对 jmp?
具体来说我正在使用bits 32和cpu i686
请注意,这会将 EIP 截断为 16 位 IP。
\n\nYASM/NASM 语法是jmp word label. 两者都经过测试。
YASM 也错误地允许它处于 64 位模式1,但 NASM 只允许它处于 32 和 16 位模式。
\n\n您在 32 位代码(默认地址/操作数大小 = 32)中的选择是:
\n\njmp/jcc rel8。强制执行jmp shortjmp rel32或 6 字节jcc rel32(2 字节操作码 + 4 字节 rel32)。 jmp dword在任何模式下,并且jmp near在 32/64 位代码中。jmp rel16/5 字节的操作数大小前缀jcc rel16,将 EIP 的高 16 位清零。(在 64 位模式下不可编码,只能在 16 或 32 位模式下编码。)jmp word在 16/32 位模式下,jmp near在 16 位模式下。strict是可选的,例如jmp strict short foo,在所有这些覆盖中。即使没有 strict,如果 rel8 无法同时到达 NASM 和 YASM,这也是一个错误,而不仅仅是一个警告。我的例子也使用了jmp,但可以使用ja,,jle或任何其他jcc。请注意,call rel8不存在,只有rel16and rel32(和间接),使用相同的覆盖语法。
来自英特尔jmp文档的操作部分:
# near jump\n\n ... EIP <- EIP + DEST for non-64-bit mode relative jumps\n\n IF OperandSize = 32\n THEN\n EIP \xe2\x86\x90 tempEIP; # in 64-bit mode, this truncates RIP to EIP\n ELSE\n IF OperandSize = 16\n THEN (* OperandSize = 16 *)\n EIP \xe2\x86\x90 tempEIP AND 0000FFFFH; #### This line\n ELSE (* OperandSize = 64)\n RIP \xe2\x86\x90 tempRIP;\n FI;\n FI;\nRun Code Online (Sandbox Code Playgroud)\n\n因此,通过添加操作数大小前缀来获取 a 并不能有效地节省 1 个字节rel16,除非您的代码是从低 64kiB 的虚拟地址空间执行的。(或者使用非零 CS 基数,IP=EIP。)
只是为了好玩,我验证了这在我的 Skylake CPU 上是真实存在的:在 32 位 Linux 静态可执行文件中,0x8049000 <foo> jmpw 0x9000GDB 中的单步执行给出了Cannot access memory at address 0x9000. Binutilsobjdump将指令反汇编为:
# objdump -d -Mintel output from a 32-bit ELF executable\n08049000 <foo>:\n 8049000: 66 e9 fc ff jmpw 9000 <foo-0x8040000>\nRun Code Online (Sandbox Code Playgroud)\n\n所以真正的执行与这个反汇编相匹配,将EIP截断为IP。
\n\nYASM 错误地允许它处于 64 位模式,并且 GNU Binutils 错误地将其解码为jmp rel1664 位模式。jmp wordNASM在 64 位模式下正确拒绝。
但实际运行它(在 Skylake 上)会将其解码为jmp rel32,如英特尔所述。(rel16在长模式下编码被标记为 NE 不可编码)。
例如
\n\n foo: jmp word foo\n db 1, 1, 1, 1\nRun Code Online (Sandbox Code Playgroud)\n\n组装 + 链接到 Linux 静态可执行文件中,使用 GNU Binutils 2.31.1 进行反汇编objdump:
0000000000401000 <foo>:\n 401000: 66 e9 fc ff jmp 1000 <foo-0x400000>\n 401004: 01 01 add DWORD PTR [rcx],eax\n 401006: 01 01 add DWORD PTR [rcx],eax\nRun Code Online (Sandbox Code Playgroud)\n\nstarti实际上在 GDB ( / )中运行它表明我们在从,即从si获取代码时出错。0x14210020x401004 + 0x0101fffc
这与 ( 66 e9 fc ff 01 01) 忽略无意义的操作数大小前缀并将其解码为jmp +0x0101fffc.