为什么gcc将8字节格式的char类型传递给函数汇编
Why does gcc pass char type in 8 byte format to function assembly
为了学习汇编,我正在查看 GCC 为一些简单的 c 程序使用 -S 命令生成的汇编。我有一个 add 函数,它接受一些 int 和一些 char 并将它们加在一起。我只是想知道为什么将 char 参数作为 8 个字节 (pushq) 压入堆栈?为什么不只推送一个字节?
.file "test.c"
.text
.globl add
.type add, @function
add:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl %edx, -12(%rbp)
movl %ecx, -16(%rbp)
movl %r8d, -20(%rbp)
movl %r9d, -24(%rbp)
movl 16(%rbp), %ecx
movl 24(%rbp), %edx
movl 32(%rbp), %eax
movb %cl, -28(%rbp)
movb %dl, -32(%rbp)
movb %al, -36(%rbp)
movl -4(%rbp), %edx
movl -8(%rbp), %eax
addl %eax, %edx
movl -12(%rbp), %eax
addl %eax, %edx
movl -16(%rbp), %eax
addl %eax, %edx
movl -20(%rbp), %eax
addl %eax, %edx
movl -24(%rbp), %eax
addl %eax, %edx
movsbl -28(%rbp), %eax
addl %eax, %edx
movsbl -32(%rbp), %eax
addl %eax, %edx
movsbl -36(%rbp), %eax
addl %edx, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size add, .-add
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq
pushq
pushq
movl , %r9d
movl , %r8d
movl , %ecx
movl , %edx
movl , %esi
movl , %edi
call add
addq , %rsp
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Ubuntu 4.9.2-10ubuntu13) 4.9.2"
.section .note.GNU-stack,"",@progbits
#include <stdio.h>
int add(int a, int b, int c, int d, int e, int f, char g, char h, char i)
{
return a + b + c + d + e + f + g + h + i;
}
int main()
{
return add(1, 2, 3, 4, 5, 6, 7, 8, 9);
}
将值压入堆栈时,压入必须始终基于系统的字长。如果你是像我这样的老计时器,那就是 16 位(尽管我确实有一些 12 位字大小的系统!),但它确实取决于系统。
既然您在谈论 X86_64,那么您将在谈论 64 位字。我的理解是,字长通常与寻址系统 RAM 上的任何值所需的最小字节数有关。由于您有 64 位内存 space,因此需要 64 位(或 8 字节,基于原始 16 位字大小的 "quad word")。
之所以如此,是因为 x86-64 SystemV ABI 需要它。
请参阅 https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-r252.pdf for a copy of the current version of the spec. See also the x86 tag wiki 以获取指向 ABI 的链接(以及更多好东西。)
参见 abi PDF 的第 17 页:
Classification The size of each argument gets rounded up to eightbytes. (footnote: Therefore the stack will always be eightbyte aligned).
进一步(第 16 页:堆栈框架):
The end of the input argument area shall be aligned on a 16 (32, if
__m256
is passed on stack) byte boundary. In other words, the value
(%rsp + 8
) is always a multiple of 16 (32) when control is
transferred to the function entry point.
如果他们将其设计为不同的整数类型在堆栈上具有不同的宽度,但 8 字节类型仍然始终是 8 字节对齐的,那么关于填充的位置将会有复杂的规则,(因此被调用函数根据当前和先前参数的类型找到它的参数)。这意味着像 printf 这样的可变参数函数将需要一个不打包 args 的不同调用约定。
8 位推送根本不可编码。只有 16 位(带有 0x66
前缀)或 64 位(无前缀,或 REX.W=1
)可用。 Intel 手册对此有点混乱,在文本中暗示 push r32
可以在 64 位模式下编码(可能 REX.W=0),但那是 not 案例:见
How many bytes does the push instruction pushes onto the stack when I don't specify the operand size?.
为了学习汇编,我正在查看 GCC 为一些简单的 c 程序使用 -S 命令生成的汇编。我有一个 add 函数,它接受一些 int 和一些 char 并将它们加在一起。我只是想知道为什么将 char 参数作为 8 个字节 (pushq) 压入堆栈?为什么不只推送一个字节?
.file "test.c"
.text
.globl add
.type add, @function
add:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl %edx, -12(%rbp)
movl %ecx, -16(%rbp)
movl %r8d, -20(%rbp)
movl %r9d, -24(%rbp)
movl 16(%rbp), %ecx
movl 24(%rbp), %edx
movl 32(%rbp), %eax
movb %cl, -28(%rbp)
movb %dl, -32(%rbp)
movb %al, -36(%rbp)
movl -4(%rbp), %edx
movl -8(%rbp), %eax
addl %eax, %edx
movl -12(%rbp), %eax
addl %eax, %edx
movl -16(%rbp), %eax
addl %eax, %edx
movl -20(%rbp), %eax
addl %eax, %edx
movl -24(%rbp), %eax
addl %eax, %edx
movsbl -28(%rbp), %eax
addl %eax, %edx
movsbl -32(%rbp), %eax
addl %eax, %edx
movsbl -36(%rbp), %eax
addl %edx, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size add, .-add
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
pushq
pushq
pushq
movl , %r9d
movl , %r8d
movl , %ecx
movl , %edx
movl , %esi
movl , %edi
call add
addq , %rsp
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Ubuntu 4.9.2-10ubuntu13) 4.9.2"
.section .note.GNU-stack,"",@progbits
#include <stdio.h>
int add(int a, int b, int c, int d, int e, int f, char g, char h, char i)
{
return a + b + c + d + e + f + g + h + i;
}
int main()
{
return add(1, 2, 3, 4, 5, 6, 7, 8, 9);
}
将值压入堆栈时,压入必须始终基于系统的字长。如果你是像我这样的老计时器,那就是 16 位(尽管我确实有一些 12 位字大小的系统!),但它确实取决于系统。
既然您在谈论 X86_64,那么您将在谈论 64 位字。我的理解是,字长通常与寻址系统 RAM 上的任何值所需的最小字节数有关。由于您有 64 位内存 space,因此需要 64 位(或 8 字节,基于原始 16 位字大小的 "quad word")。
之所以如此,是因为 x86-64 SystemV ABI 需要它。
请参阅 https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-r252.pdf for a copy of the current version of the spec. See also the x86 tag wiki 以获取指向 ABI 的链接(以及更多好东西。)
参见 abi PDF 的第 17 页:
Classification The size of each argument gets rounded up to eightbytes. (footnote: Therefore the stack will always be eightbyte aligned).
进一步(第 16 页:堆栈框架):
The end of the input argument area shall be aligned on a 16 (32, if
__m256
is passed on stack) byte boundary. In other words, the value (%rsp + 8
) is always a multiple of 16 (32) when control is transferred to the function entry point.
如果他们将其设计为不同的整数类型在堆栈上具有不同的宽度,但 8 字节类型仍然始终是 8 字节对齐的,那么关于填充的位置将会有复杂的规则,(因此被调用函数根据当前和先前参数的类型找到它的参数)。这意味着像 printf 这样的可变参数函数将需要一个不打包 args 的不同调用约定。
8 位推送根本不可编码。只有 16 位(带有 0x66
前缀)或 64 位(无前缀,或 REX.W=1
)可用。 Intel 手册对此有点混乱,在文本中暗示 push r32
可以在 64 位模式下编码(可能 REX.W=0),但那是 not 案例:见
How many bytes does the push instruction pushes onto the stack when I don't specify the operand size?.