Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does gcc pass char type in 8 byte format to function assembly

To learn assembly I am viewing the assembly generated by GCC using the -S command for some simple c programs. I have an add function which accepts some ints and some char and adds them together. I am just wondering why the char parameters are pushed onto the stack as 8 bytes (pushq)? Why not just push a single byte?

    .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);
}
like image 783
chasep255 Avatar asked Jul 19 '15 04:07

chasep255


2 Answers

It's like that because the x86-64 SystemV ABI requires it.

See 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 for links to ABIs (and much more good stuff.)

See page 17 of the abi PDF:

Classification The size of each argument gets rounded up to eightbytes. (footnote: Therefore the stack will always be eightbyte aligned).

Further (pg 16: Stack Frame):

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.

If they'd designed it so different integer types had different widths on the stack, but 8-byte types were still always 8-byte aligned, there would be complicated rules about where the padding goes, (and thus where the called function finds its args) depending on the types of current and previous args. And it would mean variadic functions like printf would need a different calling convention that didn't pack the args.


8-bit pushes are not encodable at all. Only 16-bit (with a 0x66 prefix), or 64-bit (no prefix, or REX.W=1) are available. Intel manual is a bit confusing on this, implying in the text that push r32 is encodeable in 64-bit mode (maybe with REX.W=0), but that is not the case: See How many bytes does the push instruction pushes onto the stack when I don't specify the operand size?.

like image 66
Peter Cordes Avatar answered Nov 15 '22 04:11

Peter Cordes


When pushing values onto the stack, the push must always be based on the word size of the system. If you're an old timer like me, that's 16 bits (though I do have some 12 bit word size systems!), but it really is system dependent.

Since you're talking about X86_64, you will be talking about 64 bit words. My understanding is that the word size is typically connected to the minimum number of bytes required to address any value on the RAM of the system. Since you have a 64 bit memory space, a 64 bit (or 8 bytes, a "quad word" based on the original 16 bit word size) is required.

like image 24
David Hoelzer Avatar answered Nov 15 '22 03:11

David Hoelzer