这是一个32位的Windows程序随机崩溃。我用Visual Studio 2019调试了它。下面是我看到的。执行前,
点击“单步执行”后:
CPU似乎将指令分为3个部分:8B BB, 4C, F6 07 00。什么是8B BB?我确认地址[ebx +7F64C]有效且可访问。编辑:如果我点击“步过”,EDI没有像预期的那样改变,添加寄存器的截图。要关闭:我意识到这个问题是调试器特有的,断点在指令的中间。随机崩溃反正和这个无关。
8B BB, 4C, F6 07 00
8B BB
[ebx +7F64C]
EDI
oknrviil1#
正如Peter Cordes所解释的,在x86架构上,跳转到指令中间是可能的。%rip寄存器在解码指令时并不考虑其后面的内容,它只关注指令现在和将来的位置。因此,根据您开始查看的位置,代码可能完全不同。字节本身根本没有改变。只是对它们的诠释。有趣的是,这可以在许多CISC架构上被滥用来更有效地工作。我最喜欢的一个例子来自6502汇编:
%rip
LDA $30 ;load the byte at address 0x0030 CMP $31 ;compare that value to the byte at address 0x0031 BEQ skip ;if they're equal, return 32, otherwise return 16. LDA #$10 jmp done skip: LDA #$20 done: RTS
JMP指令需要3个字节来编码,当您试图在有限的机器上尽可能地保存空间,并且只想跳过2个字节的指令(在本例中为LDA #$20)时,这是很不幸的。
JMP
LDA #$20
LDA $30 CMP $31 BEQ skip LDA #$10 .byte $2c skip: LDA #$20 RTS
如果比较结果相等,则LDA #$20正常执行,否则,CPU将代码解释为:
LDA $30 CMP $31 BEQ skip ;branch not taken LDA #$10 BIT $20A9 ;set the flags as if we ANDed the accumulator with the byte at 0x20A9. ;no registers are changed. RTS
对于上下文,BIT $20A9的编码是2C A9 20,LDA #$20的编码是A9 20。实际上,我们破坏了下面的指令以避免分支,这在代码中节省了2个字节。这种技巧可能对内存Map硬件有副作用,因此,对于某些常量,您可能无法也可能无法安全地使用此方法(此示例不应在内斯上使用,但在Commodore 64或Apple II上应该可以。)
BIT $20A9
2C A9 20
A9 20
1条答案
按热度按时间oknrviil1#
正如Peter Cordes所解释的,在x86架构上,跳转到指令中间是可能的。
%rip
寄存器在解码指令时并不考虑其后面的内容,它只关注指令现在和将来的位置。因此,根据您开始查看的位置,代码可能完全不同。字节本身根本没有改变。只是对它们的诠释。有趣的是,这可以在许多CISC架构上被滥用来更有效地工作。我最喜欢的一个例子来自6502汇编:
JMP
指令需要3个字节来编码,当您试图在有限的机器上尽可能地保存空间,并且只想跳过2个字节的指令(在本例中为LDA #$20
)时,这是很不幸的。如果比较结果相等,则
LDA #$20
正常执行,否则,CPU将代码解释为:对于上下文,
BIT $20A9
的编码是2C A9 20
,LDA #$20
的编码是A9 20
。实际上,我们破坏了下面的指令以避免分支,这在代码中节省了2个字节。这种技巧可能对内存Map硬件有副作用,因此,对于某些常量,您可能无法也可能无法安全地使用此方法(此示例不应在内斯上使用,但在Commodore 64或Apple II上应该可以。)