1

This simple c code:

file bar.c:

#include <stdio.h>

#define BSIZE 5

typedef struct
{
    int count;
    int ar[BSIZE];
} foo;

int main()
{
    foo f = {.count = 0};
     printf("%ld\n",sizeof(foo));
}

which output 24 as size of the struct (5*4 + 4), so it is correct. The gas code is as follows:

    .text
    .section    .rodata
.LC0:
    .string "%ld\n"
    .text
    .globl  main
    .type   main, @function
main:
    endbr64 
    pushq   %rbp    #
    movq    %rsp, %rbp  #,
    subq    $32, %rsp   #,
# bar.c:12: {
    movq    %fs:40, %rax    # MEM[(<address-space-1> long unsigned int *)40B], tmp85
    movq    %rax, -8(%rbp)  # tmp85, D.2347
    xorl    %eax, %eax  # tmp85
# bar.c:13:     foo f = {.count = 0};
    movq    $0, -32(%rbp)   #, f
    movq    $0, -24(%rbp)   #, f
    movq    $0, -16(%rbp)   #, f
# bar.c:14:      printf("%ld\n",sizeof(foo));
    movl    $24, %esi   #,
    leaq    .LC0(%rip), %rdi    #,
    movl    $0, %eax    #,
    call    printf@PLT  #
    movl    $0, %eax    #, _5
# bar.c:15: }
    movq    -8(%rbp), %rdx  # D.2347, tmp86
    subq    %fs:40, %rdx    # MEM[(<address-space-1> long unsigned int *)40B], tmp86
    je  .L3 #,
    call    __stack_chk_fail@PLT    #
.L3:
    leave   
    ret 
    .size   main, .-main
    .ident  "GCC: (Ubuntu 10.2.0-13ubuntu1) 10.2.0"
    .section    .note.GNU-stack,"",@progbits
    .section    .note.gnu.property,"a"
    .align 8
    .long    1f - 0f
    .long    4f - 1f
    .long    5
0:
    .string  "GNU"
1:
    .align 8
    .long    0xc0000002
    .long    3f - 2f
2:
    .long    0x3
3:
    .align 8
4:
 

Now I have multiple question about this output:

  1. why is there subq $32, %rsp when the size of struct is 24? Why is not substracted just 24 from the stack, but it needs another 8 bytes for what? alignment?

  2. what is movq %fs:40, %rax # MEM[(<address-space-1> long unsigned int *)40B], tmp85 ? What is register %fs? What does the 40 mean, offset ? What the comment generated from compiler suggest? There is no datatype long unsigned int * ???

this statements:

# bar.c:13:     foo f = {.count = 0};
   movq  $0, -32(%rbp)  #, f
   movq  $0, -24(%rbp)  #, f
   movq  $0, -16(%rbp)  #, f

I do not fully understand. From my struct definition, I guess

 -32(%rbp) == count
 -24(%rbp) == ar[0]
 -20(%rbp) == ar[1]
 -16(%rbp) == ar[2]
 -12(%rbp) == ar[3]
 -8(%rbp) == ar[4]

Is this correct alignment of struct foo in stack? How otherwise is it align?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
milanHrabos
  • 2,010
  • 3
  • 11
  • 45
  • 1
    `fs` and `gs` are segment registers. `%fs:` is a segment override over the normal segment register associated with an instruction. – Weather Vane Jan 06 '21 at 00:38
  • 2
    In this case its being used as a stack canary. – Noah Jan 06 '21 at 00:42
  • #1: Yes, for alignment, see https://stackoverflow.com/questions/49391001/why-does-the-x86-64-amd64-system-v-abi-mandate-a-16-byte-stack-alignment – Nate Eldredge Jan 06 '21 at 00:46
  • #2: As Noah says, a stack canary whose value comes from thread-local storage, see https://stackoverflow.com/questions/10325713/why-does-this-memory-address-fs0x28-fs0x28-have-a-random-value. For general info on the fs/gs registers, see https://stackoverflow.com/questions/10810203/what-is-the-fs-gs-register-intended-for – Nate Eldredge Jan 06 '21 at 00:52
  • 1
    3: Yes, that's right. These are 64-bit `mov` instructions (note the `q` in `movq`), so they write zeros over a total of 24 bytes of memory, which accounts for the 6 x 4-byte `int`s in your struct. – Nate Eldredge Jan 06 '21 at 00:54
  • @NateEldredge but is #3 the struct **exactly** matching those addresses ? If so what is at `-4(%rbp)`, why would there be unused address at stack? – milanHrabos Jan 06 '21 at 01:10
  • 2
    Oh, no, you skipped a dword between `count` and `ar[0]`. So in fact `count` is at `-32(%rbp)`, `ar[0]` is at `-28(%rbp)`, and so on. The `int` array only needs 4-byte alignment, not 8, and that's consistent with the total size being 24. So that leaves the dwords at `-8(%rbp)` and `-4(%rbp)` unused. Well, if the stack is going to be aligned to 16 bytes (see #1), there's going to be 8 bytes unused somewhere. – Nate Eldredge Jan 06 '21 at 01:22
  • `gcc -fstack-protector-strong` keeps the stack canary constant in thread-local storage. (So its actual address is randomized separately from code, I guess, and perhaps other reasons). That's why it's accessing it relative to the FS segment base, like how thread-local storage works normally. – Peter Cordes Jan 06 '21 at 06:25

0 Answers0