3

特别是在 SysV x86-64 ABI的上下文中

如果我有一个只有两个字段的结构,例如:

typedef struct {
    void *foo;
    void *bar;
} foobar_t;

我将它传递给一个定义如下的函数:

foobar_t example_function(foobar_t example_param);

ABI 似乎说每个八字节字段都应该传递INTEGER给函数,因此rdi == fooand rsi == bar。同样,在返回时我们应该能够使用raxand rdx,因为我们不需要在rdi. Ifexample_function被简单地定义为:

foobar_t example_function(foobar_t example_param) {
    return example_param;
}

一个有效的程序集实现,忽略序言和结尾,将是:

example_function:
   mov rax, rdi
   mov rdx, rsi
   ret

可以想象,一个精神上有缺陷的编译器可以用填充填充结构NO_CLASS并以某种方式使该程序集无效。我想知道它是否写在必须以这种方式处理只有两个八字节字段的结构的任何地方。

我的问题的更大背景是我正在编写一个简单的 C11 任务切换器以供我自己启迪。我主要基于 boost.context ,这正是 boost 传递两个字段结构的方式。我想知道它是否在所有情况下都是犹太洁食,或者 boost 是否有点作弊。

4

2 回答 2

3

编译器同意结构布局以及它们如何作为函数参数按值传递是 ABI 的关键部分。否则他们不能调用彼此的函数。

手写 asm 与编译器生成的 asm 没有区别;它不必来自同一编译器的同一版本即可正确互操作。这就是为什么稳定和正确的 ABI 如此重要的原因。

与手写 asm 的兼容性与与很久以前编译的机器代码的兼容性非常相似,并且多年来一直存在于二进制共享库中。如果当时是正确的,那么现在也是正确的。除非源中的结构发生了变化,否则新编译的代码可以调用现有指令并被现有指令调用。


如果编译器与所编写的标准不匹配,则它已损坏。

或者更准确地说,如果它与 gcc 不匹配,它就坏了。如果标准措辞没有描述 gcc/clang/ICC 做什么,那么标准文档就被破坏了。

void*如果您有一个用于 x86-64 System V 的编译器,它以 2 个寄存器以外的任何方式传递 2x结构,则该编译器已损坏,而不是您的手写 asm。

(假设在我们到达 struct arg 之前没有很多早期的 args 用完 arg 传递寄存器。)

于 2019-06-06T19:10:08.970 回答
3

ABI 似乎说每个八字节字段都应该作为 INTEGER 传递给函数,因此rdi == fooand rsi == bar

同意,对于可从多个编译单元访问的“全局”函数,参数结构被分解为八字节的片段,第一个完全由 填充foo,第二个完全由 填充bar。这些被归类为 INTEGER,因此分别在 %rdi 和 %rsi 中传递。

同样,在返回时我们应该能够使用raxand rdx,因为我们不需要在rdi.

我不同意你关于 %rdi 的观点,但我同意返回值的成员在 %rax 和 %rdx 中返回。

一个有效的程序集实现,忽略序言和结尾,将是:[...]

同意。

可以想象,一个精神上有缺陷的编译器可以用 NO_CLASS 填充填充结构,并以某种方式使该程序集无效。我想知道它是否写在必须以这种方式处理只有两个八字节字段的结构的任何地方。

生成符合 SysV x86-64 ABI 的代码的编译器将使用已经讨论过的寄存器来传递参数并返回返回值。这样的编译器当然没有义务完全按照您的描述实现函数体,但我没有看到您的担忧。是的,这些细节都写下来了。尽管您提供的特定情况未在您链接的 ABI 规范中明确描述,但上面讨论的所有行为都遵循该规范。这就是重点

生成行为不同的代码(用于全局函数)的编译器不是智力缺陷,而是不合格的

我的问题的更大背景是我正在编写一个简单的 C11 任务切换器以供我自己启迪。我主要基于 boost.context ,这正是 boost 传递两字段结构的方式。我想知道它是否在所有情况下都是犹太洁食,或者 boost 是否有点作弊。

我需要进行比我准备花费更多的分析来确定 Boost 在您指向的代码中正在做什么。请注意,这不是您在example_function. 但可以合理地假设 Boost 至少尝试根据 ABI 实现其函数调用。

于 2019-06-06T19:11:13.203 回答