结构与参数中数据对齐方式的差异? [英] Difference in data alignment in struct vs parameter?

查看:116
本文介绍了结构与参数中数据对齐方式的差异?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给出以下代码:

typedef struct tagRECT {
  int left;
  int top;
  int right;
  int bottom;
} RECT;

extern int Func(RECT *a, int b, char *c, int d, char e, long f, int g, int h, int i, int j);

int main() {

}

void gui() {
    RECT x = {4, 5, 6, 7};
    Func(&x, 1, 0, 3, 4, 5, 6, 7, 8, 9);
}

这大概是在Linux上由程序集生成的gcc x86_64(我使用了编译器资源管理器).

This is the assembly generated gcc x86_64 presumably on linux (I used compiler explorer).

main:
  mov eax, 0
  ret
gui:
  push rbp
  mov rbp, rsp
  sub rsp, 16
  ; RECT x assignment
  mov DWORD PTR [rbp-16], 4
  mov DWORD PTR [rbp-12], 5
  mov DWORD PTR [rbp-8], 6
  mov DWORD PTR [rbp-4], 7

  ; parameters
  lea rax, [rbp-16]
  push 9
  push 8
  push 7
  push 6
  mov r9d, 5
  mov r8d, 4
  mov ecx, 3
  mov edx, 0
  mov esi, 1
  mov rdi, rax
  call Func
  add rsp, 32
  nop
  leave
  ret

可以看出,结构中的int对齐了4个字节.但是函数的最后4个参数,所有int都被push d到堆栈中,这意味着它们被对齐了8个字节.为什么会有这种不一致?

It can be seen that the int in the struct are aligned by 4 bytes. But the last 4 parameters to the function, all int are pushd to the stack which means they were aligned by 8 bytes. Why this inconsistency?

推荐答案

在x86-64调用约定(如您使用的x86-64 System V调用约定)中,堆栈插槽为8个字节,因为32位push/pop是不可能,并使其更容易保持16字节对齐.请参阅是UNIX&的调用约定在i386和x86-64上的Linux系统调用(它还涵盖了函数调用约定以及系统调用约定.

stack slots are 8 bytes in x86-64 calling conventions like the x86-64 System V calling convention you're using, because 32-bit push/pop is impossible, and to make it easier to keep it 16-byte aligned. See What are the calling conventions for UNIX & Linux system calls on i386 and x86-64 (it also covers function-calling conventions, as well as system-calling conventions. Where is the x86-64 System V ABI documented?.

mov可以正常工作,因此将4个字节作为堆栈args的最小单位是一种有效的设计. (与x86-16(其中SP相对寻址模式是不可能的)不同). 但是除非您引入填充规则,否则您可能会错位8字节的args.因此,给每个arg至少8字节的对齐可能是动机的一部分. (尽管有填充规则来保证__m128 args具有16字节的对齐方式,而__m256具有32字节的对齐方式,等等.而且大概也适用于诸如struct { alignas(64) char b[256]; };的过度对齐的结构.

mov works just fine, though, so it would have been a valid design to make 4 bytes the minimum unit for stack args. (Unlike x86-16 where SP-relative addressing modes were impossible). But unless you introduce padding rules, then you could have misaligned 8-byte args. So giving every arg at least 8-byte alignment was probably part of the motivation. (Although there are padding rules to guarantee that __m128 args have 16-byte alignment, and __m256 have 32-byte, etc. And presumably also for over-aligned structs, like struct { alignas(64) char b[256]; };.

对于没有原型的函数,只有4字节的插槽会更容易中断,并且可能会使可变参数的函数更复杂,但是x86-64 System V已经按堆栈上的值传递了较大的对象,因此堆栈arg可能占用多个对象8字节的堆栈插槽".

Only 4-byte slots would break more easily for functions without prototypes, and maybe make variadic functions more complex, but x86-64 System V already passes larger objects by value on the stack, so a stack arg may take more than one 8-byte "stack slot".

(与Windows x64通过隐藏引用传递的方式不同,因此每个arg恰好是一个堆栈槽.它甚至保留32字节的影子空间,因此可变参数函数可以将其寄存器args溢出到影子空间中并创建所有args.)

(Unlike Windows x64 which passes by hidden reference so every arg is exactly one stack slot. It even reserves 32 bytes of shadow space so a variadic function can spill its register args into the shadow space and create a full array of all the args.)

这篇关于结构与参数中数据对齐方式的差异?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆