Что такое регистр %fs:‹num› в ассемблере gnu?

Этот простой код c:

файл 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));
}

который выводит 24 как размер структуры (5 * 4 + 4), так что это правильно. Газовый код выглядит следующим образом:

    .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:
 

Теперь у меня есть несколько вопросов об этом выводе:

  1. почему есть subq $32, %rsp, когда размер структуры равен 24? Почему из стека не вычитается просто 24, а для чего нужно еще 8 bytes? выравнивание?

  2. что такое movq %fs:40, %rax # MEM[(<address-space-1> long unsigned int *)40B], tmp85? Что такое регистр %fs? Что означает 40, смещение? Что предлагает комментарий, сгенерированный компилятором? Нет типа данных long unsigned int * ???

это заявления:

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

Я не совсем понимаю. Из моего определения структуры, я думаю

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

Это правильное выравнивание struct foo в стеке? Как иначе выровнять?


person milanHrabos    schedule 06.01.2021    source источник
comment
fs и gs — сегментные регистры. %fs: — это переопределение сегмента по сравнению с обычным сегментным регистром, связанным с инструкцией.   -  person Weather Vane    schedule 06.01.2021
comment
В этом случае он используется как канарейка стека.   -  person Noah    schedule 06.01.2021
comment
№ 1: Да, для выравнивания см. stackoverflow.com/questions/49391001/   -  person Nate Eldredge    schedule 06.01.2021
comment
# 2: Как говорит Ной, канарейка стека, значение которой поступает из локального хранилища потока, см. stackoverflow.com/questions/10325713/. Для получения общей информации о регистрах fs/gs см. stackoverflow.com/questions/10810203/   -  person Nate Eldredge    schedule 06.01.2021
comment
3: Да, это верно. Это 64-битные инструкции mov (обратите внимание на q в movq), поэтому они записывают нули в общей сложности 24 байта памяти, что составляет 6 x 4-байтовых int в вашей структуре.   -  person Nate Eldredge    schedule 06.01.2021
comment
@NateEldredge, но структура № 3 точно соответствует этим адресам? Если да, то что находится в -4(%rbp), почему в стеке должен быть неиспользуемый адрес?   -  person milanHrabos    schedule 06.01.2021
comment
О нет, вы пропустили двойное слово между count и ar[0]. Так что на самом деле count находится в -32(%rbp), ar[0] в -28(%rbp) и так далее. Массив int требует выравнивания только по 4 байтам, а не по 8, и это согласуется с общим размером, равным 24. Таким образом, двойные слова в -8(%rbp) и -4(%rbp) остаются неиспользованными. Что ж, если стек будет выровнен по 16 байтам (см. № 1), где-то останется 8 неиспользуемых байтов.   -  person Nate Eldredge    schedule 06.01.2021
comment
gcc -fstack-protector-strong сохраняет константу канарейки стека в локальном хранилище потока. (Поэтому его фактический адрес рандомизирован отдельно от кода, я думаю, и, возможно, по другим причинам). Вот почему он обращается к нему относительно базы сегментов FS, подобно тому, как обычно работает локальное хранилище потока.   -  person Peter Cordes    schedule 06.01.2021