…rex_push_reg must be used in lieu of push_reg when it appears as
the first instruction in a function,as the calling standard dictates
that functions must not begin with a single byte instruction.
在这种情况下,由于您说的是MASM,我们可以假设目标平台是Windows,因此假设是Windows 64-bit calling convention,而不是官方AMD64规范中的内容.但是,和你一样,我找不到任何符合这一要求的东西.
但是,我认为此评论所指的是Microsoft的内部标准,旨在允许对系统二进制文件进行热修补. “热修补”是指在内存中动态修补二进制文件的能力 – 例如.应用系统更新 – 无需重新启动.
这项工作的最低要求是在每个函数的开头都有一个2字节短JMP指令的空间. (注意,短跳转只允许执行从当前指令指针的-128到127字节的任何位置传递,但这足以分支到一个长跳转,然后跳转到更新提供的补丁功能.实际上,跳转指令被修补到函数之间的填充.)
因此,函数不能以1字节指令开始,因为热补丁可能会导致指令指针指向指令的中间. (考虑多线程竞争条件.)所以规则是,如果你想用一个通常只有1个字节的PUSH RBP这样的序言指令开始一个函数,你需要添加一个1字节的REX前缀.这个不必要的REX前缀被cpu忽略,并且基本上起到1字节NOP的作用.
在32位版本中,通过2字节指令MOV EDI,EDI提供热修补.这会将EDI寄存器复制到自身而不会影响标志,因此它实际上是一个NOP.
对于32位版本,您必须专门将/hotpatch
switch传递给编译器以使其插入此指令.但是,在64位版本中,编译器始终表现为指定了/ hotpatch,因此第一条指令长度为2个字节的要求实际上成为平台标准的一部分.
那么,为什么要制定这个复杂的规则而不是让编译器在每个函数的开头插入一个2字节的NOP,就像在32位版本中一样?好吧,我不能肯定地说,但我可以推测.一个问题是MOV EDI,EDI不是x64上的NOP,因为它隐含地将RDI寄存器的高32位归零.你必须选择一个不同的指令作为NOP,一旦你完成了这个,你不妨重新考虑整个业务.其次,在那里使用NOP会产生(轻微)性能成本,并且由于长模式下的大多数指令长度至少为2个字节,因此在通常需要的指令时要求无意义的NOP指令似乎不值得.只有少数例外情况就足够了.