【问题标题】:How does System V amd64 handle very long return values?System V amd64 如何处理很长的返回值?
【发布时间】:2019-05-30 01:57:02
【问题描述】:

我正在简要研究用于 amd64 / x86-64 架构的 System V ABI,我很好奇它如何处理超过 128 位的返回值,其中 raxrdx 还不够。

我在 Ubuntu 18.04 64 位(更一般地说,任何兼容 amd64 POSIX 的系统)上编写了以下 C 代码:

struct big {
    long long a, b, c, d;
};

struct big bigfunc(void) {
    struct big r = {12, 34, 56, 78};
    return r;
}

编译为gcc -S -masm=intel t.c,检查t.s

        .file   "t.c"
        .intel_syntax noprefix
        .text
        .globl  bigfunc
        .type   bigfunc, @function
bigfunc:
.LFB0:
        .cfi_startproc
        mov     QWORD PTR -40[rsp], rdi
        mov     QWORD PTR -32[rsp], 12
        mov     QWORD PTR -24[rsp], 34
        mov     QWORD PTR -16[rsp], 56
        mov     QWORD PTR -8[rsp], 78
        mov     rcx, QWORD PTR -40[rsp]
        mov     rax, QWORD PTR -32[rsp]
        mov     rdx, QWORD PTR -24[rsp]
        mov     QWORD PTR [rcx], rax
        mov     QWORD PTR 8[rcx], rdx
        mov     rax, QWORD PTR -16[rsp]
        mov     rdx, QWORD PTR -8[rsp]
        mov     QWORD PTR 16[rcx], rax
        mov     QWORD PTR 24[rcx], rdx
        mov     rax, QWORD PTR -40[rsp]
        ret
        .cfi_endproc
.LFE0:
        .size   bigfunc, .-bigfunc
        .ident  "GCC: (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0"
        .section        .note.GNU-stack,"",@progbits

毫不奇怪,结构定义没有编译成任何指令,所以输出只包含函数bigfunc。输出程序集看起来非常简单,从堆栈中为 struct big r 分配内存并分配初始值,然后返回它。

如果我理解正确,在执行ret 之前,寄存器rax 在函数调用的开头包含rdi 的值(来自QWORD PTR -40[rbp])。根据 SysV,rdi 是提供给函数的第一个参数,这是不可能的,因为函数不接受任何参数。所以我在这里有几个问题:

  1. 当函数 bigfunc 不带参数时,rdi 是什么?
  2. 什么是rax(作为包含返回值的寄存器),当rdx在这个函数中没有被触及?
  3. 函数如何返回这个 256 位 C 结构?

【问题讨论】:

  • 如果启用优化,这段代码会更容易理解。 TL;DR:正如ABI document 中所指定的,通过将指向结构的指针作为隐藏的第一个参数传递来返回结构。
  • @fuz 浏览菜单但没有找到关键字“返回值”。介意指出节号吗?谢谢。
  • 它可能不在 amd64 特定的文档中 - 这是适用于所有架构的一般规则。
  • @iBug 见第 22 页。这是 §3.2.3 的全部内容。
  • @WumpusQ.Wumbley:什么?由每个架构上的每个调用约定/ABI 来指定返回多大的对象。例如如果在回退到内存之前使用了多个寄存器,以及内存是否传递隐藏指针(通常作为第一个 arg,但需要指定详细信息;它可以传入通常不是传递 arg 的寄存器的寄存器)或将值留在堆栈上(与 args 的 callee-pops 相反),或其他任何东西。

标签: assembly posix return-value x86-64 abi


【解决方案1】:

根据ABI(1),第22页

如果类型有 MEMORY 类,则调用者为返回提供空间 值并在 %rdi 中传递此存储的地址,就好像它是第一个一样 函数的参数。实际上,这个地址变成了“隐藏”的第一个地址 牙龈。此存储不得与被调用方可见的任何数据重叠 此参数以外的其他名称。 返回时 %rax 将包含由 %rdi 中的调用者

第 17、18 和 19 页描述了分类,我相信 以下第 19 页上的条款将您的 struct big 指定为 MEMORY 类。

(c) 如果聚合的大小超过两个八字节并且第一个 八字节不是 SSE 或任何其他八字节都不是 SSEUP,整个 参数在内存中传递。

即调用者必须为返回值分配内存,并在 %rdi 中传递一个指向该内存的指针(并且被调用函数在 %rax 中返回相同的地址)

(1) https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI 有更新的 ABI 官方版本,但链接目前无法正常工作。

【讨论】:

  • 在我的示例中,struct big 正好是 4 个八字节,你能解决这个问题吗?
  • 查看更新。您可能必须阅读第 17,18 和 19 页才能自己查看规则,它们并不那么简单。
猜你喜欢
  • 2021-10-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-30
  • 2018-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多