编写汇编函数时序言和结语是强制性的吗?
Are the prologue and epilogue mandatory when writing assembly functions?
最近我在汇编中重写了一些 libc 函数,其中一些函数(不需要任何 call 或 syscall指令,如 strlen),我丢弃了序言和结语,因为没有它我的测试不会失败(也许它们不够复杂)。在同行评审期间,有人告诉我丢弃它们是不好的做法,但无法解释原因。
那么,当我调用没有 prologue/epilogue 组合的 asm 函数时,我 运行 会遇到问题吗?
即使在堆栈上不需要额外 space 时也添加它是一个好习惯吗?
如果出于某些原因强制要求,为什么汇编程序(我使用 nasm)不处理它?
如果您没有设置适当的堆栈帧,调试器可能很难知道您现在在哪个函数中。在 ELF-targets 上,如果您没有明确设置堆栈帧,则必须手动提供 CFI 数据(参见 this article)。如果没有 CFI 数据,堆栈展开将不起作用,调试器可能无法找出你在哪个函数中。除非你想手动添加 CFI 数据(这有点乏味且容易出错),我建议你接受轻微的性能损失并设置完整的堆栈框架。
Are the prologue and epilogue mandatory when writing assembly functions?
对于纯汇编,您甚至不需要 "functions" - 例如您可以拥有一段具有多个不同入口点和一个 "ret" 的代码(这相当于在一个体面的编译器进行 "tail call" 优化后您可能最终得到的代码)。
用于编写与其他人的调用约定兼容的函数;你必须遵守别人的调用约定。如果那些调用约定说(例如)某些寄存器必须由被调用者保留,那么被调用者必须保留这些寄存器(通过在序言中保存并在结语中加载),如果不这样做,你可能会以意外的数据损坏而告终(因为编译器希望值保持不变但是..)。
请注意,对于 80x86,none 的调用约定需要堆栈帧(如 EBP 或 RBP)- 这只是由于古代调试器设计不佳而导致的历史纪念品,并且在调试器切换时不再理智大约 20 年前更好的技术。
If mandatory for some reasons, why doesn't the assembler (I used nasm) take care of it?
汇编程序通常不知道您要遵守哪种调用约定(如果有的话)。
最近我在汇编中重写了一些 libc 函数,其中一些函数(不需要任何 call 或 syscall指令,如 strlen),我丢弃了序言和结语,因为没有它我的测试不会失败(也许它们不够复杂)。在同行评审期间,有人告诉我丢弃它们是不好的做法,但无法解释原因。
那么,当我调用没有 prologue/epilogue 组合的 asm 函数时,我 运行 会遇到问题吗?
即使在堆栈上不需要额外 space 时也添加它是一个好习惯吗?
如果出于某些原因强制要求,为什么汇编程序(我使用 nasm)不处理它?
如果您没有设置适当的堆栈帧,调试器可能很难知道您现在在哪个函数中。在 ELF-targets 上,如果您没有明确设置堆栈帧,则必须手动提供 CFI 数据(参见 this article)。如果没有 CFI 数据,堆栈展开将不起作用,调试器可能无法找出你在哪个函数中。除非你想手动添加 CFI 数据(这有点乏味且容易出错),我建议你接受轻微的性能损失并设置完整的堆栈框架。
Are the prologue and epilogue mandatory when writing assembly functions?
对于纯汇编,您甚至不需要 "functions" - 例如您可以拥有一段具有多个不同入口点和一个 "ret" 的代码(这相当于在一个体面的编译器进行 "tail call" 优化后您可能最终得到的代码)。
用于编写与其他人的调用约定兼容的函数;你必须遵守别人的调用约定。如果那些调用约定说(例如)某些寄存器必须由被调用者保留,那么被调用者必须保留这些寄存器(通过在序言中保存并在结语中加载),如果不这样做,你可能会以意外的数据损坏而告终(因为编译器希望值保持不变但是..)。
请注意,对于 80x86,none 的调用约定需要堆栈帧(如 EBP 或 RBP)- 这只是由于古代调试器设计不佳而导致的历史纪念品,并且在调试器切换时不再理智大约 20 年前更好的技术。
If mandatory for some reasons, why doesn't the assembler (I used nasm) take care of it?
汇编程序通常不知道您要遵守哪种调用约定(如果有的话)。