C 编译器是否保证两个八字节字段结构将在 SysV x64 上作为 INTEGER 传递?
Do C compilers guarantee two eightbyte field structs will be passed as INTEGER on SysV x64?
特别是在 the SysV x86-64 ABI
的上下文中
如果我有一个只有两个字段的结构,比如:
typedef struct {
void *foo;
void *bar;
} foobar_t;
然后我将它传递给定义如下的函数:
foobar_t example_function(foobar_t example_param);
ABI 似乎说每个八字节字段都应作为 INTEGER
传递给函数,因此 rdi == foo
和 rsi == bar
。类似地,返回时我们应该能够使用 rax
和 rdx
,因为我们不需要 rdi
中的内存指针。如果 example_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 和 this is exactly how boost passes two-field structs around。我想知道它是否在所有情况下都符合犹太洁食标准,或者 boost 是否有点作弊。
编译器同意结构布局以及它们如何作为函数参数按值传递是 ABI 的 key 部分。否则他们无法调用彼此的功能。
Hand-written asm 与 compiler-generated asm 没有区别;它不必来自同一编译器的同一版本即可正确互操作。这就是为什么稳定和正确的 ABI 如此重要的原因。
与 hand-written asm 的兼容性与与很久以前编译的机器代码的兼容性非常相似,并且多年来一直位于二进制共享库中。如果当时是正确的,那么现在也是正确的。除非源代码中的结构已更改 新编译的代码可以调用现有指令并被现有指令调用。
如果编译器不符合标准as-written,它就坏了。
或者更准确地说,如果它与 gcc 不匹配,它就坏了。如果标准措辞没有描述 gcc/clang/ICC 做什么,那么标准文档就被破坏了。
如果您的 x86-64 System V 编译器以 2 个寄存器之外的任何方式传递 2x void*
结构,则该编译器已损坏,不会 你的 hand-written asm.
(假设在我们到达 struct arg 之前没有很多早期的 args 用完 arg-passing 寄存器。)
The ABI seems to say that each eightbyte field should be passed as
INTEGER to the function, therefore rdi == foo
and rsi == bar
.
同意,对于可从多个编译单元访问的 "global" 函数,参数结构被分成八字节的片段,第一个完全由 foo
,第二个完全由 bar
填充。它们被归类为 INTEGER,因此分别传入 %rdi 和 %rsi。
Similarly, when returning we should be able to use rax
and rdx
, since we don't need a memory pointer in rdi
.
我不同意你关于 %rdi 的观点,但我同意 return 值的成员在 %rax 和 %rdx 中 returned。
A valid assembly implementation, ignoring prologue and epilogue, would be: [...]
同意。
Conceivably, a mentally-deficient compiler could fill the struct with NO_CLASS padding and make that assembly invalid somehow. I'm wondering if it's written down anywhere that a struct with only two eightbyte fields must be handled this way.
生成符合 SysV x86-64 ABI 代码的编译器将使用已经讨论过的寄存器来传递参数和 returning return 值。这样的编译器当然没有义务完全按照您的描述实现函数体,但我没有看到您的担忧。是的,这些细节都写下来了。尽管您提供的具体案例未在您链接的 ABI 规范中明确描述,但上面讨论的所有行为都遵循该规范。这就是它的点。
生成行为不同的代码(针对全局函数)的编译器不是 mentally-deficient,它是 non-conforming.
The larger context to my question is that I'm writing a simple C11
task switcher for my own edification. I'm basing it largely on
boost.context and this is exactly how boost passes two-field structs
around. I want to know if it's kosher under all circumstances or if
boost is cheating a little.
要确定 Boost 在您指向的代码中究竟做了什么,我需要进行比我准备花费更多的分析。请注意,不是您在 example_function
中显示的内容。但有理由假设 Boost 至少在尝试根据 ABI 实现其函数调用。
特别是在 the SysV x86-64 ABI
的上下文中如果我有一个只有两个字段的结构,比如:
typedef struct {
void *foo;
void *bar;
} foobar_t;
然后我将它传递给定义如下的函数:
foobar_t example_function(foobar_t example_param);
ABI 似乎说每个八字节字段都应作为 INTEGER
传递给函数,因此 rdi == foo
和 rsi == bar
。类似地,返回时我们应该能够使用 rax
和 rdx
,因为我们不需要 rdi
中的内存指针。如果 example_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 和 this is exactly how boost passes two-field structs around。我想知道它是否在所有情况下都符合犹太洁食标准,或者 boost 是否有点作弊。
编译器同意结构布局以及它们如何作为函数参数按值传递是 ABI 的 key 部分。否则他们无法调用彼此的功能。
Hand-written asm 与 compiler-generated asm 没有区别;它不必来自同一编译器的同一版本即可正确互操作。这就是为什么稳定和正确的 ABI 如此重要的原因。
与 hand-written asm 的兼容性与与很久以前编译的机器代码的兼容性非常相似,并且多年来一直位于二进制共享库中。如果当时是正确的,那么现在也是正确的。除非源代码中的结构已更改 新编译的代码可以调用现有指令并被现有指令调用。
如果编译器不符合标准as-written,它就坏了。
或者更准确地说,如果它与 gcc 不匹配,它就坏了。如果标准措辞没有描述 gcc/clang/ICC 做什么,那么标准文档就被破坏了。
如果您的 x86-64 System V 编译器以 2 个寄存器之外的任何方式传递 2x void*
结构,则该编译器已损坏,不会 你的 hand-written asm.
(假设在我们到达 struct arg 之前没有很多早期的 args 用完 arg-passing 寄存器。)
The ABI seems to say that each eightbyte field should be passed as INTEGER to the function, therefore
rdi == foo
andrsi == bar
.
同意,对于可从多个编译单元访问的 "global" 函数,参数结构被分成八字节的片段,第一个完全由 foo
,第二个完全由 bar
填充。它们被归类为 INTEGER,因此分别传入 %rdi 和 %rsi。
Similarly, when returning we should be able to use
rax
andrdx
, since we don't need a memory pointer inrdi
.
我不同意你关于 %rdi 的观点,但我同意 return 值的成员在 %rax 和 %rdx 中 returned。
A valid assembly implementation, ignoring prologue and epilogue, would be: [...]
同意。
Conceivably, a mentally-deficient compiler could fill the struct with NO_CLASS padding and make that assembly invalid somehow. I'm wondering if it's written down anywhere that a struct with only two eightbyte fields must be handled this way.
生成符合 SysV x86-64 ABI 代码的编译器将使用已经讨论过的寄存器来传递参数和 returning return 值。这样的编译器当然没有义务完全按照您的描述实现函数体,但我没有看到您的担忧。是的,这些细节都写下来了。尽管您提供的具体案例未在您链接的 ABI 规范中明确描述,但上面讨论的所有行为都遵循该规范。这就是它的点。
生成行为不同的代码(针对全局函数)的编译器不是 mentally-deficient,它是 non-conforming.
The larger context to my question is that I'm writing a simple C11 task switcher for my own edification. I'm basing it largely on boost.context and this is exactly how boost passes two-field structs around. I want to know if it's kosher under all circumstances or if boost is cheating a little.
要确定 Boost 在您指向的代码中究竟做了什么,我需要进行比我准备花费更多的分析。请注意,不是您在 example_function
中显示的内容。但有理由假设 Boost 至少在尝试根据 ABI 实现其函数调用。