首页 文章

为什么gcc将8字节格式的char类型传递给函数汇编

提问于
浏览
5

为了学习汇编,我正在查看GCC使用-S命令为一些简单的c程序生成的汇编 . 我有一个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   $9
    pushq   $8
    pushq   $7
    movl    $6, %r9d
    movl    $5, %r8d
    movl    $4, %ecx
    movl    $3, %edx
    movl    $2, %esi
    movl    $1, %edi
    call    add
    addq    $24, %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);
}

2 回答

  • 3

    将值推入堆栈时,推送必须始终基于系统的字大小 . 如果你是一个像我这样的旧计时器,这是16位(虽然我确实有一些12位字大小的系统!),但它确实是系统依赖的 .

    既然你在谈论X86_64,你将谈论64位字 . 我的理解是字大小通常连接到解决系统RAM上任何值所需的最小字节数 . 由于您具有64位存储空间,因此需要64位(或8字节,基于原始16位字大小的“四字”) .

  • 9

    就是这样,因为x86-64 SystemV ABI需要它 .

    请参阅https://github.com/hjl-tools/x86-psABI/wiki/x86-64-psABI-r252.pdf以获取当前版本规范的副本 . 另请参阅x86 tag wiki以获取ABI的链接(以及更多好东西 . )

    参见abi PDF的第17页:

    分类每个参数的大小四舍五入到八字节 . (脚注:因此堆栈将始终是八字节对齐) .

    进一步(第16页: Stack Frame ):

    输入参数区域的末尾应在16(32,如果在堆栈上传递__m256)字节边界上对齐 . 换句话说,当控制转移到函数入口点时,值(%rsp 8)始终是16(32)的倍数 .

    如果他们设计了它,那么不同的整数类型在堆栈上有不同的宽度,但是8字节类型仍然总是8字节对齐,关于填充的位置会有复杂的规则(因此被调用的函数找到它的位置) args)取决于当前和先前args的类型 . 这意味着像printf这样的可变函数需要一个不包含args的不同调用约定 .


    8位推送根本不可编码 . 只有16位(带有 0x66 前缀)或64位(无前缀或 REX.W=1 )可用 . 英特尔手册对此有点疑惑,暗示 push r32 可在64位模式下编码(可能是REX.W = 0),但情况并非如此:请参阅How many bytes does the push instruction pushes onto the stack when I don't specify the operand size? .

相关问题