c - 结构在汇编中是如何组织的?
问题描述
我试图弄清楚,编译器如何在每个结构成员之间填充空间。在这个例子中:
struct s{
int a,b,c;
};
struct s get(int a){
struct s foo = {.a=a,.b=a+1,.c=a+2};
return foo;
}
编译为cc -S a.c
:
.file "a.c"
.text
.globl get
.type get, @function
get:
.LFB0:
pushq %rbp
movq %rsp, %rbp
movl %edi, -36(%rbp)
movl -36(%rbp), %eax
movl %eax, -24(%rbp)
movl -36(%rbp), %eax
addl $1, %eax
movl %eax, -20(%rbp)
movl -36(%rbp), %eax
addl $2, %eax
movl %eax, -16(%rbp)
movq -24(%rbp), %rax
movq %rax, -12(%rbp)
movl -16(%rbp), %eax
movl %eax, -4(%rbp)
movq -12(%rbp), %rax
movl -4(%rbp), %ecx
movq %rcx, %rdx
popq %rbp
ret
.LFE0:
.size get, .-get
.ident "GCC: (Debian 8.3.0-6) 8.3.0"
.section .note.GNU-stack,"",@progbits
不使用优化。问题是为什么将它们-36(%rbp)
用作第一个成员“参考”,当它们按顺序排列时
.a == -24(%rbp)
.b == -20(%rbp)
.c == -16(%rbp)
无需为-36(%rbp)
此处使用的编译器腾出空间。是故意的(作为房间或编译器使用-36(%rbp)
作为第一个成员的“参考”)?
另外,最后,
movq -24(%rbp), %rax #take first member
movq %rax, -12(%rbp) #place it randomly
movl -16(%rbp), %eax #take third member
movl %eax, -4(%rbp) #place it randomly
没有意义,它与初始结构不连续,结构的第一个和第三个成员在函数get
分配的空间中随机复制。
结构的约定是什么?
解决方案
您观察到的代码是三个不同事物的混杂: a 的实际布局、struct s
如何从函数返回结构的 ABI 规范,以及许多编译器在其默认模式下插入的反优化(相当于-O0
)以确保简单调试器可以在任何断点处停止时查找和更改变量的值(有关更多信息,请参阅Why does clang generate inefficient asm with -O0 (for this simple floating point sum)?)。
get
您可以通过写入参数来消除这些因素中的第二个struct s *
,而不是按值返回结构,并通过编译gcc -O2 -S
而不是 just来消除第三个因素gcc -S
。(也试试-Og
and -O1
; 应用的复杂优化-O2
也可能令人困惑。)例如:
$ cat test.c
struct s {
int a,b,c;
};
void get(int a, struct s *s)
{
s->a = a;
s->b = a+1;
s->c = a+2;
}
$ gcc -O2 -S test.c
$ cat test.s
.file "test.c"
.text
.p2align 4
.globl get
.type get, @function
get:
.LFB0:
.cfi_startproc
leal 1(%rdi), %eax
movl %edi, (%rsi)
addl $2, %edi
movl %eax, 4(%rsi)
movl %edi, 8(%rsi)
ret
.cfi_endproc
.LFE0:
.size get, .-get
.ident "GCC: (Debian 9.3.0-13) 9.3.0"
.section .note.GNU-stack,"",@progbits
从这个汇编语言中,应该更清楚的a
是在偏移量 0 内struct s
,b
在偏移量 4 和c
偏移量 8。
结构布局由每个 CPU 架构的“psABI”(特定于处理器的应用程序二进制接口)指定。您可以在https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI阅读 x86 的 psABI 规范。这些还解释了如何从函数返回结构。知道堆栈帧的布局仅部分由 psABI 指定也很重要。实际上,程序集转储中的一些“随机”偏移量是由编译器任意选择的。
推荐阅读
- python-3.x - doctest 中的行太长
- azure - 使用用户模拟调用 Azure AD Microsoft API 的 Web API
- javascript - Azure Function Azure Synapse - 为什么我收到空白输出
- r - 根据 R 中行的内容重新组织数据框元素
- kotlin - 如何在运行时使 ktor 重新加载 JS 更改
- plsql - Oracle express edition 10g 运行时触发问题
- javascript - 注意能够根据角度/打字稿中对象的键以升序/降序对数据进行排序
- c# - Bot Framework Teams 消息扩展搜索
- python - 如何在按下按钮时更改按钮颜色并在按下其他按钮时更改为原始颜色。按钮是使用 python 中的类创建的
- java - 试图让一个动画对象在Java中跳转