Этот простой код 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:
Теперь у меня есть несколько вопросов об этом выводе:
почему есть
subq $32, %rsp
, когда размер структуры равен24
? Почему из стека не вычитается просто24
, а для чего нужно еще8 bytes
? выравнивание?что такое
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
в стеке? Как иначе выровнять?
fs
иgs
— сегментные регистры.%fs:
— это переопределение сегмента по сравнению с обычным сегментным регистром, связанным с инструкцией. - person Weather Vane   schedule 06.01.2021mov
(обратите внимание наq
вmovq
), поэтому они записывают нули в общей сложности 24 байта памяти, что составляет 6 x 4-байтовыхint
в вашей структуре. - person Nate Eldredge   schedule 06.01.2021-4(%rbp)
, почему в стеке должен быть неиспользуемый адрес? - person milanHrabos   schedule 06.01.2021count
и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.2021gcc -fstack-protector-strong
сохраняет константу канарейки стека в локальном хранилище потока. (Поэтому его фактический адрес рандомизирован отдельно от кода, я думаю, и, возможно, по другим причинам). Вот почему он обращается к нему относительно базы сегментов FS, подобно тому, как обычно работает локальное хранилище потока. - person Peter Cordes   schedule 06.01.2021