【问题标题】:how c compiler treats a struct return value from a function, in ASM在 ASM 中,c 编译器如何处理函数的结构返回值
【发布时间】:2014-09-04 15:30:20
【问题描述】:

说到C函数的返回值,返回值存放在EAX寄存器中。假设我们谈论的是 32 位寄存器,欢迎使用整数,但是当我们返回这些类型时会发生什么: long longlong double,大于 32 位的 struct/union

【问题讨论】:

  • 哪个 ABI ? Linux,Windows,其他? 32位还是64位?
  • 对于struct 返回类型,典型的解决方案是调用者 为对象提供空间,例如,在EBX 中传递一个指向该空间的指针,或者有约定,该空间在函数调用之前位于堆栈的末尾,等等。然后被调用者将返回值写入该空间。
  • 如果返回值的大小不小(例如大于 4 字节的 32 位机器),则编译器可以在参数列表中添加“额外”参数并通过指针返回值。或者使用堆栈,这取决于实现。
  • @Medals:调用者在调用范围内分配一个缓冲区,并将指向该缓冲区的指针作为参数传递给被调用者。
  • 返回值存储在 x86 中的 eax 或 edx:eax 和 x86_64 中的 rax 或 rdx:rax 中,而不是 ebx

标签: c function assembly x86 return


【解决方案1】:

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

i386 Linux (SysV) 调用约定仅以这种方式返回 64 位整数。结构体(即使是由单个int32_t 组成的结构体)使用隐藏参数方法而不是打包eaxedx:eax。 64 位 Linux 和微软当前的标准 __vectorcall,都将结构体打包成 e/raxe/rdx:e/rax

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

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

【讨论】:

    【解决方案2】:

    考虑这个程序:

    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;
    }
    

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

    $ gcc -Wall -c -O2 -mno-80387 test.c -o test.o
    

    第一个函数:

    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
    

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

    第二个功能(sizeof(long long) == 8):

    00000020 <_test2>:
      20:   31 c0                   xor    eax,eax
      22:   31 d2                   xor    edx,edx
      24:   c3                      ret
    

    结果将通过eaxedx 两个寄存器返回,而不仅仅是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
    

    返回值将通过三个寄存器传递,eaxedxecx

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-10
      • 2011-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-07
      • 2018-08-06
      相关资源
      最近更新 更多