在ASM中,c编译器如何处理函数的结构返回值

Med*_*als 5 c x86 assembly return function

在谈到C函数的返回值时,返回值存储在EAX寄存器中.假设我们正在谈论32位寄存器,整数欢迎,但是当我们返回这些类型会发生什么: long long,long double,一struct/ union比32位大.

Pet*_*des 6

在常见的x86调用约定中,返回适合两个寄存器的对象RDX:RAX.这是与div和mul指令的隐式输入/输出相同的寄存器对,以及cdq/ cqo(符号扩展e/rax到e/rdx).

i386 Linux(SysV)调用约定只返回64位整数.结构(甚至是由单个结构组成的结构int32_t)使用隐藏参数方法而不是打包eaxedx:eax.64位Linux,以及微软目前的标准__vectorcall,都将结构包装成e/rax,或者e/rdx:e/rax.

许多调用约定通过添加一个隐藏的额外参数来处理更大的对象:一个指向空间的指针,用于存储返回值.有关您正在使用的特定ABI,请参阅ABI文档.(链接在维基).

与注释中讨论的其他调用约定相比(例如,隐式使用堆栈上的空间来存储返回的大对象),传递指针可以保存副本,因为指针可以指向最终目标而不是堆栈上的临时空间.


小智 5

考虑这个程序:

struct object_t
{
  int m1;
  int m2;
  int m3;
};

struct object_t
test1(void)
{
  struct object_t o = {1, 2, 3};
  return o;
}

long long
test2(void)
{
  return 0LL;
}

long double
test3(void)
{
  return 0.0L;
}
Run Code Online (Sandbox Code Playgroud)

在 Windows 上编译(目标文件,最少指令,没有 x87 指令):

$ gcc -Wall -c -O2 -mno-80387 test.c -o test.o
Run Code Online (Sandbox Code Playgroud)

第一个功能:

00000000 <_test1>:
   0:   8b 44 24 04             mov    eax,DWORD PTR [esp+0x4]
   4:   c7 00 01 00 00 00       mov    DWORD PTR [eax],0x1
   a:   c7 40 04 02 00 00 00    mov    DWORD PTR [eax+0x4],0x2
  11:   c7 40 08 03 00 00 00    mov    DWORD PTR [eax+0x8],0x3
  18:   c3                      ret
Run Code Online (Sandbox Code Playgroud)

调用者将提供一个指向他的结构在堆栈上的位置的指针作为第一个参数,test1并将使用该指针填充它。

第二个函数 ( sizeof(long long) == 8):

00000020 <_test2>:
  20:   31 c0                   xor    eax,eax
  22:   31 d2                   xor    edx,edx
  24:   c3                      ret
Run Code Online (Sandbox Code Playgroud)

结果将通过两个寄存器eax和返回edx,而不仅仅是eax.

第三个函数 ( sizeof(long double) == 12):

00000030 <_test3>:
  30:   31 c0                   xor    eax,eax
  32:   31 d2                   xor    edx,edx
  34:   31 c9                   xor    ecx,ecx
  36:   c3                      ret
Run Code Online (Sandbox Code Playgroud)

返回值将通过三个寄存器传递,eax, edx, ecx