该程序总是通过在 eax 中放置一个系统调用代码然后调用 int 0x80 来结束(如果有人也能解释一下,我会很高兴)。
这是因为您只查看旧的 32 位 Linux 示例,而这正是 Linux 开发人员想做的事情。他们没有理由不能使用不同的寄存器,也没有太多理由不能使用半个寄存器(例如,aax
而不是eax
,或bx
或..)。以类似的方式,他们没有理由不能使用调用门或不同的中断号。当然,一旦 Linux 开发人员做出决定(“内核将期望 EAX 中的函数号并使用 int 0x80”),调用其内核的所有内容都必须遵守他们的决定;并且他们不能在不破坏所有现有软件的情况下轻易改变他们的决定(但可以并且确实支持替代方案 - 例如添加对sysenter
和syscall
当这些指令被发明时,同时确保它int 0x80
仍然有效)。
但是,据我了解, eax 是一个 32 位寄存器 - 为什么需要 32 位来存储系统调用(我确定没有 232 个值)
他们并不“需要” 32 位;但是您可以期望函数编号(在“值是否太大”健全性检查之后)最终会在call [table+eax*4]
指令中用于调用所选函数,并且因为它使用 32 位寻址,所以需要使用 32-位寄存器。使用寄存器的一半(或四分之一)将涉及零扩展(例如额外and eax,0x0000FFFF
或movzx eax,ax
指令)以将 16 位值转换为 32 位值。由于其他原因,使用所有 32 位通常也更快(例如mov ax,123
,设置 EAX 的最低 16 位并保持最高 16 位不变将取决于最高 16 位的先前值,这可能导致“依赖关系停顿” " 如果它需要等到 EAX 的先前值已知,则在 CPU 中)。
这是否意味着 eax 仅在您最终想要执行系统调用时才有特殊用途,而在其余时间您可以随意使用它?
这意味着当你调用别人的代码时,你必须遵守别人的调用约定,不管它们是什么。这可能意味着将其他寄存器(ebx
,ecx
等)用于他们决定的任何目的,并且可能意味着使用特定的堆栈布局(例如以特定顺序将事物推入堆栈)。
请注意,有各种指令确实希望以特定方式使用特定寄存器 - mul
, div
, stosd
, movsd
, loop
, in
, out
, enter
,leave
等;每个通用寄存器都有“罕见的特殊情况”。尽管如此; 它们仍然是“通用寄存器”,因为它们不是“特定用途寄存器”(例如eip
或标志,只能用于一个特定目的,而不能用于其他任何用途)。