作者:jessiemiumiu_956 | 来源:互联网 | 2023-02-01 11:04
根据masm的macamd64.inc rex_push_reg
,
当rex_push_reg作为函数中的第一条指令出现时,必须使用rex_push_reg代替push_reg,因为调用标准规定函数不能以单字节指令开头.
但是,我无法找到任何表达此文件的文件.这是真的?它在哪里记录?为什么会这样?
1> Cody Gray..:
这项索赔的执行部分似乎是"呼叫标准" - 这标准是什么?这个笑话可能是旧的,但它仍然很贴切:关于标准的好处是有很多可供选择.
在这种情况下,由于您说的是MASM,我们可以假设目标平台是Windows,因此将假设Windows 64位调用约定,而不是官方AMD64规范中的内容.但是,和你一样,我找不到任何符合这一要求的东西.
但是,我认为此评论所指的是Microsoft的内部标准,旨在允许对系统二进制文件进行热修补."热修补"是指在内存中动态修补二进制文件的能力 - 例如应用系统更新 - 而无需重新启动.
这项工作的最低要求是JMP
在每个函数的开头都有一个2字节短指令的空间.(请注意,一个短跳转仅允许执行随时随地从当前指令指针-128到+127字节通过,不过这也够转移到具有长跳,然后转移到由更新提供的修补功能,在实际应用,跳转指令被修补到函数之间的填充.)
因此,函数不能以1字节指令开始,因为热补丁可能会导致指令指针指向指令的中间.(考虑多线程竞争条件.)所以规则是,如果你想用一个PUSH RBP
通常只有1个字节的序言指令开始一个函数,你需要添加一个1字节的REX前缀.这个不必要的REX前缀被CPU忽略,并且基本上起到1字节NOP的作用.
在32位版本中,2字节指令提供了热补丁MOV EDI, EDI
.这EDI
会将寄存器复制到自身而不会影响标志,因此它实际上是一个NOP.
对于32位版本,您必须专门将/hotpatch
开关传递给编译器以使其插入此指令.但是,在64位版本中,编译器始终表现为/hotpatch
已经指定,因此第一条指令长度为2个字节的要求实际上成为平台标准的一部分.
那么,为什么要制定这个复杂的规则而不是让编译器在每个函数的开头插入一个2字节的NOP,就像在32位版本中一样?好吧,我不能肯定地说,但我可以推测.一个问题是,MOV EDI, EDI
是不是在x64一个NOP因为它隐含的归零的高32位RDI
的寄存器.你必须选择一个不同的指令作为NOP,一旦你完成了这个,你不妨重新思考整个业务.其次,在那里有NOP需要支付(轻微)性能成本,而且由于长模式下的大多数指令长度至少为2个字节,所以当通常需要的指令时,要求无意义的NOP指令似乎不值得.只有少数例外情况就足够了.