最近我在汇编中重写了一些 libc 函数,对于其中一些函数(不需要任何调用或系统调用指令的函数,比如 strlen),我放弃了序言和结尾,因为没有它我的测试不会失败(也许它们是不够复杂)。在同行评审期间,有人告诉我丢弃它们是不好的做法,但无法解释为什么。
那么,当我调用没有序言/结尾组合的 asm 函数时,我是否遇到了问题?
即使堆栈上不需要额外的空间,添加它是一个好习惯吗?
如果由于某些原因强制执行,为什么汇编程序(我使用 nasm)不处理它?
如果您没有设置正确的堆栈帧,调试器可能很难知道您现在所处的功能。在 ELF 目标上,如果您没有明确设置堆栈帧,则必须手动提供 CFI 数据(参见本文)。如果没有 CFI 数据,堆栈展开不起作用,调试器可能无法找出您所在的函数。除非您想手动添加 CFI 数据(这有点乏味且容易出错),否则我建议您接受轻微的性能损失并设置一个完整的堆栈框架。
编写汇编函数时,序言和尾声是强制性的吗?
对于纯汇编,您甚至不需要“函数” - 例如,您可以拥有一段具有多个不同入口点和单个“ret”的代码(这相当于在一个体面的编译器完成之后您可能最终得到的结果) “尾调用”优化)。
用于编写与其他人的调用约定兼容的函数;你必须遵守别人的调用约定。如果那些调用约定说(例如)某些寄存器必须由被调用者保留,那么被调用者必须保留这些寄存器(通过保存在序言中并在结尾处加载),如果不是这样,您最终可能会出现意外的数据损坏(因为编译器期望一个值保持不变但是......)。
请注意,对于 80x86,任何调用约定都不需要堆栈帧(如 EBP 或 RBP)——这只是由于古代调试器的糟糕设计而导致的历史纪念品,并且在大约 20 年前调试器切换到更好的技术时不再理智。
如果由于某些原因强制执行,为什么汇编程序(我使用 nasm)不处理它?
汇编程序通常不知道您要遵守哪个调用约定(如果有)。