.code16与.code32的区别及i386保护模式相关执行疑问
i386保护模式与.code16/.code32相关问题解答
1. 保护模式下执行.code16代码是否有效?
分两种情况:
- 如果当前代码段是16位保护模式段(段描述符的D位设为0),.code16生成的代码可以正常执行,此时CPU按16位指令规则解码。
- 如果当前是32位保护模式段(D位设为1),直接执行.code16代码会出错。因为.code16是按16位模式编码的指令,而32位模式下CPU默认按32位规则解码,会把16位指令识别成错误的32位指令,导致程序崩溃。
2. 汇编器生成的.code16与.code32机器码有何差异?
核心差异体现在默认操作数/地址大小、指令前缀、跳转偏移长度这几点:
- 默认操作数与前缀:
- .code16下,默认使用16位寄存器(如ax、bx),要使用32位寄存器(如eax、ebx)或32位操作数,需要加
0x66前缀。 - .code32下,默认使用32位寄存器,要使用16位寄存器或操作数,需要加
0x66前缀。
- .code16下,默认使用16位寄存器(如ax、bx),要使用32位寄存器(如eax、ebx)或32位操作数,需要加
- 默认地址大小与前缀:
- .code16下默认是16位寻址(比如用
bx+si这种16位基址+变址),要使用32位寻址(如eax+ebx),需要加0x67前缀。 - .code32下默认是32位寻址,要使用16位寻址需加
0x67前缀。
- .code16下默认是16位寻址(比如用
- 跳转指令偏移:
- 近跳转(如
jmp near):.code16下是2字节偏移,.code32下是4字节偏移。 - 远跳转(如
ljmp):.code16下是2字节段选择子+2字节偏移,.code32下是2字节段选择子+4字节偏移。
- 近跳转(如
3. 设置CR0寄存器后、跳转前可执行.code16代码的原因是什么?
CR0的PE位置1只是开启了保护模式的硬件支持,但CPU的指令解码模式由当前代码段的**D位(默认操作数大小位)**决定。在执行远跳转前,我们仍处于原来的16位段(实模式下的段本质是隐含的16位描述符,D位为0),CPU依然按16位指令规则解码,所以.code16生成的代码能正常运行。只有当执行ljmp跳转到32位代码段时,CPU才会加载新的段描述符(D位为1),正式切换到32位指令解码模式。
4. .code16和.code32的具体含义是什么?
这两个是汇编器的伪指令,用来告诉汇编器生成对应模式下的指令编码:
- .code16:让汇编器生成适配16位执行模式的机器码。默认用16位寄存器、16位寻址方式,跳转指令生成16位偏移;如果要使用32位操作数或寻址,会自动添加必要的前缀(
0x66/0x67)。 - .code32:让汇编器生成适配32位执行模式的机器码。默认用32位寄存器、32位寻址方式,跳转指令生成4字节偏移;如果要使用16位操作数或寻址,自动添加对应前缀。
简单说,就是给汇编器指定“默认的操作数/地址大小”,让它生成符合对应模式解码规则的机器码。
内容的提问来源于stack exchange,提问作者Markity
相关产品推荐
相关产品推荐

