.code16 和 .code32 x86 程序集的 Objdump
Objdump of .code16 and .code32 x86 assembly
我有这个汇编代码(在Linux):
.globl _start
_start:
cli
xorw %ax,%ax # Set %ax to zero
movw %ax,%ds
movw %ax,%es
movw %ax,%ss
我先在最上面加上.code16
生成一个16位的代码,然后用.code32
替换生成一个32位的代码。
我用这两个命令编译它们:
gcc -m32 -nostdinc -c file.s
ld -m elf_i386 -o file.exe file.o
然后我用
检查
objdump -d file.exe
对于第一种情况 (.code16
),我得到以下输出:
08048054 <_start>:
8048054: fa cli
8048055: 31 c0 xor %eax,%eax
8048057: 8e d8 mov %eax,%ds
8048059: 8e c0 mov %eax,%es
804805b: 8e d0 mov %eax,%ss
对于第二种情况 (.code32
) 我得到这个输出:
08048054 <_start>:
8048054: fa cli
8048055: 66 31 c0 xor %ax,%ax
8048058: 8e d8 mov %eax,%ds
804805a: 8e c0 mov %eax,%es
804805c: 8e d0 mov %eax,%ss
我理解 66
操作数前缀部分。令我困惑的是打印的汇编助记符。不应该为 .code32
案例打印 xor %eax, %eax
吗?或者,它应该为 .code16
案例打印 xor %ax, %ax
吗?有人可以澄清一下吗?
.code 16
告诉 assembler 假定代码在 16 位模式下将是 运行,例如为 32 位操作数大小使用 66
操作数大小前缀而不是默认的 16。但是,您将 assemble 和 link 转换为 elf32 二进制文件,这意味着文件元数据仍然表示 32 位代码。 (没有 x86-16 Linux ELF 文件这样的东西)。
Objdump disassembles根据文件元数据,因此为32位代码,unless you override和-m i8086
。您获得的大小与 32 位反汇编的二进制文件相匹配。
如果您 assemble 一条在 16 位模式下具有不同长度的指令,例如
,您实际上可能会看到破损
add 9, %ax # 129 doesn't fit in an imm8
如果assembled作为16位指令,它将没有前缀,并且是一个imm16源操作数。解码为 32 位指令后,它将有一个 imm32 源操作数,它在操作码后面占用更多的总字节数。对于任一模式,操作数大小前缀都会更改指令其余部分的长度(不包括前缀)。顺便说一句,对于这种特殊情况,(预)解码在 Intel CPU 上会变慢,其中前缀对于指令的其余部分是长度变化的。 (https://agner.org/optimize/)
无论如何,用错误的代码大小反汇编该指令将导致 disassembler 与指令边界不同步,因此它将最终测试它正在解释的模式。
如果您正在制作普通用户-space 代码(不是切换模式的内核,或者需要是 16 位的),.code32
和 .code64
是无用的。他们只是让您将机器代码放入错误类型的 ELF 文件中。 (Assembling 32-bit binaries on a 64-bit system (GNU toolchain))
顺便说一句,移动到 %ss
隐含地防止中断,直到 在 下一条指令之后。 (这应该设置堆栈指针)。你可以这样避免cli/sti
。
我有这个汇编代码(在Linux):
.globl _start
_start:
cli
xorw %ax,%ax # Set %ax to zero
movw %ax,%ds
movw %ax,%es
movw %ax,%ss
我先在最上面加上.code16
生成一个16位的代码,然后用.code32
替换生成一个32位的代码。
我用这两个命令编译它们:
gcc -m32 -nostdinc -c file.s
ld -m elf_i386 -o file.exe file.o
然后我用
检查objdump -d file.exe
对于第一种情况 (.code16
),我得到以下输出:
08048054 <_start>:
8048054: fa cli
8048055: 31 c0 xor %eax,%eax
8048057: 8e d8 mov %eax,%ds
8048059: 8e c0 mov %eax,%es
804805b: 8e d0 mov %eax,%ss
对于第二种情况 (.code32
) 我得到这个输出:
08048054 <_start>:
8048054: fa cli
8048055: 66 31 c0 xor %ax,%ax
8048058: 8e d8 mov %eax,%ds
804805a: 8e c0 mov %eax,%es
804805c: 8e d0 mov %eax,%ss
我理解 66
操作数前缀部分。令我困惑的是打印的汇编助记符。不应该为 .code32
案例打印 xor %eax, %eax
吗?或者,它应该为 .code16
案例打印 xor %ax, %ax
吗?有人可以澄清一下吗?
.code 16
告诉 assembler 假定代码在 16 位模式下将是 运行,例如为 32 位操作数大小使用 66
操作数大小前缀而不是默认的 16。但是,您将 assemble 和 link 转换为 elf32 二进制文件,这意味着文件元数据仍然表示 32 位代码。 (没有 x86-16 Linux ELF 文件这样的东西)。
Objdump disassembles根据文件元数据,因此为32位代码,unless you override和-m i8086
。您获得的大小与 32 位反汇编的二进制文件相匹配。
如果您 assemble 一条在 16 位模式下具有不同长度的指令,例如
,您实际上可能会看到破损add 9, %ax # 129 doesn't fit in an imm8
如果assembled作为16位指令,它将没有前缀,并且是一个imm16源操作数。解码为 32 位指令后,它将有一个 imm32 源操作数,它在操作码后面占用更多的总字节数。对于任一模式,操作数大小前缀都会更改指令其余部分的长度(不包括前缀)。顺便说一句,对于这种特殊情况,(预)解码在 Intel CPU 上会变慢,其中前缀对于指令的其余部分是长度变化的。 (https://agner.org/optimize/)
无论如何,用错误的代码大小反汇编该指令将导致 disassembler 与指令边界不同步,因此它将最终测试它正在解释的模式。
如果您正在制作普通用户-space 代码(不是切换模式的内核,或者需要是 16 位的),.code32
和 .code64
是无用的。他们只是让您将机器代码放入错误类型的 ELF 文件中。 (Assembling 32-bit binaries on a 64-bit system (GNU toolchain))
顺便说一句,移动到 %ss
隐含地防止中断,直到 在 下一条指令之后。 (这应该设置堆栈指针)。你可以这样避免cli/sti
。