什么 x86 调用约定通过 ESI 传递第一个参数?

逆向工程 x86 调用约定
2021-06-15 06:43:02

我正在查看一些 x86 代码,我相信这些代码是使用 Microsoft 工具链构建的,并试图找出在此调用期间使用的调用约定:

   push esi ; save ESI (it gets restored later)
   lea esi, [ebp-0xC] ; set param 1 for call to FOO
   call FOO
   test eax, eax ; test return value
   jz somelabel

函数 FOO 是这样开始的:

   FOO:
   mov edi, edi
   push ebx
   xor ebx, ebx
   push ebx ; null
   push esi ; pass ESI in as second param to upcoming call, which has been set by caller
   push ptr blah
   mov [esi+0x8], ebx
   mov [esi+0x4], ebx
   mov [esi], ebx
   call InterlockedCompareExchange ; known stdcall func which takes 3 params
   test eax, eax
   ...

由于 ESI 没有在 FOO 的主体中初始化,我假设它是由调用者作为参数传入的。

这是什么调用约定?它看起来是 fastcall 的一个变种。这个公约有名字吗?

1个回答

没有像这样工作的“官方”调用约定。您所看到的很可能是链接时代码生成的结果,也称为 LTO(链接时优化)或 WPO(整个程序优化)。

启用后,优化和代码生成在链接时完成,此时编译器可以访问整个程序和所有编译单元的代码,并可以使用此信息进行更极端的优化。

MSDN

当 /LTCG 用于链接使用 /Og、/O1、/O2 或 /Ox 编译的模块时,将执行以下优化:

  • 跨模块内联

  • 过程间寄存器分配(仅限 64 位操作系统)

  • 自定义调用约定(仅限 x86)

  • 小 TLS 位移(仅限 x86)

  • 堆栈双对齐(仅限 x86)

  • 改进的内存消歧(更好的全局变量和输入参数的干扰信息)

在您引用的代码片段中,编译器检测到该函数FOO不是从程序外部调用的,因此它可以将调用约定自定义为使用已在调用位置设置的寄存器值的内容,或者以其他方式改进寄存器分配。使用大量模板化的代码,您甚至可以获得多个常用函数的副本,这些函数接受不同寄存器和/或堆栈组中的参数。