使用单字节指令开始x64function是否合法?

根据masm的macamd64.inc, rex_push_reg

…当rex_push_reg作为函数中的第一条指令出现时,必须用来代替push_reg,因为调用标准规定函数不能以单字节指令开始。

但是,我无法find任何文件expression这一点。 这是真的? 它在哪里logging? 这是为什么?

这个要求的执行部分似乎是“呼叫标准” – 哪个标准叫? 这个笑话可能是一个古老的笑话,但它仍然是恰当的:关于标准的伟大的事情是有这么多的选择。

在这种情况下,由于您正在谈论MASM,我们可以假定目标平台是Windows,所以Windows 64位调用约定将被假设,而不是官方的AMD64规范中的东西。 然而,就像你一样,我也找不到任何说这个要求的东西。

不过,我认为这个评论所指的是微软的内部标准,旨在允许热补丁系统二进制文件。 “热修补”意味着能够动态地修补内存中的二进制文件 – 例如应用系统更新 – 而无需重新启动。

这个工作的最低要求是在每个函数的开始都有一个2字节的短JMP指令打补丁的空间。 (请注意,短跳转只允许执行从当前指令指针的-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,因为它隐含地将RDI寄存器的高32位RDI 。 你必须选择与NOP不同的指令,一旦你这样做,你不妨重新考虑整个业务。 其次,在那里有一个(轻微的)性能成本,因为在那里有很长的NOP指令,所以当通常的指令只有少数例外就足够了。