gpt4 book ai didi

c - 在 SystemV ABI 中返回一个结构

转载 作者:太空宇宙 更新时间:2023-11-04 01:43:44 24 4
gpt4 key购买 nike

SystemV ABI 中只定义了 2 个返回寄存器:raxrdx,但是 struct 的大小可以远远超过 16字节并且有超过 2 个成员。所以我考虑了以下示例:

struct test{
unsigned long a;
char *b;
unsigned long c;
};

struct test get_struct(void){
return (struct test){.a = 10, .b = "abc", .c = 20};
}

void get_struct2(struct test *tst){
struct test tmp = {.a = 10, .b = "abc", .c = 20};
*tst = tmp;
}

O3 使用 gcc 为这些函数编译的代码看起来几乎相同:

Dump of assembler code for function get_struct:
0x0000000000000820 <+0>: lea rdx,[rip+0x2f6] # 0xb1d
0x0000000000000827 <+7>: mov rax,rdi
0x000000000000082a <+10>: mov QWORD PTR [rdi],0xa
0x0000000000000831 <+17>: mov QWORD PTR [rdi+0x10],0x14
0x0000000000000839 <+25>: mov QWORD PTR [rdi+0x8],rdx
0x000000000000083d <+29>: ret
End of assembler dump.

Dump of assembler code for function get_struct2:
0x0000000000000840 <+0>: lea rax,[rip+0x2d6] # 0xb1d
0x0000000000000847 <+7>: mov QWORD PTR [rdi],0xa
0x000000000000084e <+14>: mov QWORD PTR [rdi+0x10],0x14
0x0000000000000856 <+22>: mov QWORD PTR [rdi+0x8],rax
0x000000000000085a <+26>: ret
End of assembler dump.

因此 get_struct 函数签名被静默修改为接受指向结构的指针并返回该指针。

问题: 在返回结构的示例函数中,返回作为第一个参数传递的指针和第一个 lea rdx,[ rip+0x2f6] 这两种情况都相似吗?这种用法在 ABI 中是标准化的还是依赖于编译器?

lea rdx,[rip+0x2f6] 似乎代表了 char * 的加载,但它的汇编看起来有点困惑,因为它使用了rip with disposition(我猜这是在 rodata 部分显示元素地址的问题。)

如果结构包含 2 个可以放入寄存器的成员,我们可以看到 raxrdx 的预期用法。

最佳答案

返回在 rdi 中传递的指针(一个隐藏指针)对于调用者来说是一种便利
被调用者无法返回寄存器中的整个结构,只能返回内存中的结构。
然而,被调用者不能为结构分配缓冲区,因为这不仅效率低下,而且从所有权的角度来看也是有问题的(调用者如何释放一个它不知道如何分配的缓冲区?),所以它只能返回调用者给出的指针。
如果兼容(例如 f(g()))并且编译器已经知道如何处理函数返回指向结构的指针(即,没有任何特殊操作),这对于将值传递给其他函数也很有用).

隐藏指针的使用,以及在 rax 中返回它,都记录在 ABI 中:

Returning of Values

The returning of values is done according to the following algorithm:

  1. Classify the return type with the classification algorithm.
  2. If the type has class MEMORY, then the caller provides space for the return value and passes the address of this storage in %rdi as if it were the first argument to the function.
    In effect, this address becomes a “hidden” first argument. This storage must not overlap any data visible to the callee through other names than this argument.
    On return %rax will contain the address that has been passed in by the caller in %rdi.

lea rax,[rip+0x2d6] 只是指向 "abc" 的指针,这就是 PIE(不要与 PIC 混淆)必须做的访问他们的数据(只读与否)。

最后:

If the size of the aggregate exceeds two eightbytes and the first eight- byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument is passed in memory.

IMO 的措辞并非 100% 正确,更好的版本是:“整个论点都有 MEMORY 类”。但效果是一样的:小于 16B 的结构可以在寄存器中传递返回

Here in practice.

关于c - 在 SystemV ABI 中返回一个结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57932525/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com