x86汇编 - 为什么[e] bx保留在调用约定中?

the*_*box 5 x86 assembly calling-convention

我注意到很多调用约定都坚持为被调用者保留[e] bx.

现在,我可以理解为什么他们会保留像[e] sp或[e] bp这样的东西,因为这会弄乱被调用者的堆栈.我也可以理解为什么你可能想要保留[e] si或[e] di,因为如果他们不是特别小心的话可以打破被调用者的字符串指令.

但是[e] bx?究竟是什么对[e] bx如此重要?是什么让[e] bx如此特殊以至于多个调用约定都坚持在整个函数调用中保留它?

是否有某种微妙的错误/陷阱可能会因为[e] bx的混乱而产生?

修改[e] bx对被调用者的影响是否比修改[e] dx或[e] cx更大?

我只是不明白为什么这么多的召唤惯例单独列出[e] bx进行保存.

Aki*_*nen 7

并非所有寄存器都是保留的良好候选者:

no (e)ax -- Implicitly used in some instructions; Return value
no (e)dx -- edx:eax is implicity used in cdq, div, mul and in return values

   (e)bx -- generic register, usable in 16-bit addressing modes (base)
   (e)cx -- shift-counts, used in loop, rep

   (e)si -- movs operations, usable in 16-bit addressing modes (index)
   (e)di -- movs operations, usable in 16-bit addressing modes (index)

Must (e)bp -- frame pointer, usable in 16-bit addressing modes (base)
Must (e)sp -- stack pointer, not addressable in 8086 (other than push/pop)
Run Code Online (Sandbox Code Playgroud)

看一下这个表,两个寄存器有充分的理由被保留,两个寄存器有理由不被保留.accumulator =(e)ax eg是由于短编码而最常用的寄存器.SI,DI构成逻辑寄存器对 - 在REP MOVS和其他字符串操作上,都被删除.

在半被调用者/来电者保存范例中,只有当bx/cx优于si/di时,讨论基本上才会进行.在其他调用约定中,只有EDX,EAX和ECX可以被删除.

EBX确实有一些模糊的隐式用法仍然与现代代码相关(例如CMPXGH8B/CMPXGH16B),但它是32/64位代码中最不特殊的寄存器.

EBX是一个保留调用寄存器的不错选择,因为一个函数很少需要保存/恢复EBX,因为它们需要专门的EBX,而不仅仅是任何非易失性寄存器.正如Brett Hale的回答所指出的那样,它使EBX成为需要ABI的全局偏移表(GOT)指针的绝佳选择.

在16位模式下,寻址模式仅限于(任何子集)[BP|BX + DI|SI + disp8/disp16]),因此BX绝对是特殊的.


Dwa*_*ell 5

这是在不保存任何寄存器并将其全部保存之间的折衷方案.可以提出既不保存也不保存全部,但要么极端都会导致将内容复制到内存(堆栈)导致效率低下.选择允许保留一些寄存器而不保留一些寄存器,可以降低函数调用的平均成本.

  • 也许巧合,但bx,si,di和bp是可用于在16位代码中形成有效地址的寄存器. (3认同)
  • @FrankKotler倾向于同意这一点.原始的间接寻址只允许`mov tgt,[base + index + off]`,`base`为`bx` /`bp`(因此'b`')和`index``si` /`di `(因此'我'').所以它似乎保留了所有涉及寻址/堆栈访问的regs(那时就是`BX`,`BP`,`SP`,`SI`和`DI`)是足够可取的.如果你认为`BP` /`SP`是"保留",那么这只是巧妙地将一般的reg设置分成两半,`AX`,`CX`,'DX`是被叫者拥有的和'BX`,`SI `,`DI`是来电者所有. (2认同)
  • 我同意并理解 ebx,但我编写了很多从 C 调用的 16 位代码,而我们当时从未保留过 bx。向 32 位的迁移基本上消除了 ebx 和 edx 之间的区别,因此对我来说,这似乎比计划的更随意。(我愿意被启发。) (2认同)

Bre*_*ale 5

对于i386 ELF ABI 来说,主要原因之一是它ebx保存了位置无关代码 (PIC) 的全局偏移表 (GOT) 寄存器的地址。详见规范3-35。在极端情况下,如果共享库代码必须在每次函数调用返回后恢复 GOT,那将是破坏性的。

  • 保存`bx` 的传统当然要早于它——它是由DOS C 编译器完成的,然后是Win16/Win32 ABI。 (2认同)
  • @IgorSkochinsky 不,大多数 16 位调用约定,包括 MS-DOS C 编译器使用的那些约定,如 Microsoft 和 Borland 的,并没有跨函数调用保留 BX。一些 16 位调用约定将其用作寄存器参数和/或指针的返回值。 (2认同)