Как gcc определяет, сколько памяти выделяется для стека и почему он больше не уменьшает% rsp, когда я удаляю printf()
(или любой вызов функции) из основного?
1. Я заметил, когда поигрался с образцом кода: https://godbolt.org/z/fQqkNE, что 6th line
в программе просмотра сборок gcc subq $48, %rsp
удаляется, если я удалю printf()
из своего C
кода на line 22
. Похоже, что когда я не делаю никаких вызовов функций из своей main, тогда %rsp
не уменьшается, но данные по-прежнему распределяются на основе %rbp
и смещений. Думал %rsp
меняется только при росте стека. Моя теория состоит в том, что, поскольку он не будет вызывать никаких других функций, он знает, что ему не нужно хранить стек для других несуществующих функций. Но разве не должно %rsp
расти по мере сохранения данных?
2. Добавляя переменные в свой rect struct
, я также заметил, что он иногда выделяет память с шагом, превышающим размер добавленного типа данных. Какого соглашения следует придерживаться при принятии решения о том, сколько памяти выделить для стека?
3. Есть ли онлайн-инструмент, который будет принимать ассемблерный код в качестве входных данных, а затем рисовать изображение стека и сообщать мне состояние каждого регистра в любой момент выполнения? Godbolt.org — очень хороший инструмент, я бы просто хотел, чтобы у него были эти две дополнительные функции.
Я вставлю приведенный ниже код на случай, если в будущем ссылка на Godbolt перестанет работать:
#include <stdio.h>
#include <stdint.h>
struct rect {
int a;
int b;
int* c;
int d[2];
uint8_t f;
};
int main() {
int arr[2] = {2, 3};
struct rect Rect;
Rect.a = 10;
Rect.b = 20;
Rect.c = arr;
Rect.d[0] = Rect.a;
Rect.d[1] = Rect.b;
Rect.f =255;
printf("%d and %d", Rect.a, Rect.b);
return 0;
}
.LC0:
.string "%d and %d"
main:
pushq %rbp
movq %rsp, %rbp
subq $48, %rsp
movl $2, -8(%rbp)
movl $3, -4(%rbp)
movl $10, -48(%rbp)
movl $20, -44(%rbp)
leaq -8(%rbp), %rax
movq %rax, -40(%rbp)
movl -48(%rbp), %eax
movl %eax, -32(%rbp)
movl -44(%rbp), %eax
movl %eax, -28(%rbp)
movb $-1, -24(%rbp)
movl -44(%rbp), %edx
movl -48(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
leave
ret
P.S .: В книге, за которой я следую, используется синтаксис AT&T для обучения x86. Это странно, потому что это значительно усложняет поиск онлайн-руководств.
x86-64 System V имеет красную зону на 128 байтов ниже RSP, на которую не будут асинхронно наступать обработчики сигналов или что-то еще. GCC может использовать это для локальных пользователей, если он не собирается сам вызывать какие-либо функции (что будет синхронно использовать пространство под RSP). См. Почему компилятор резервирует небольшой объем стека, но не весь размер массива? — person miran80 schedule 08.04.2020
2. GCC любит выравнивать стек по 16 и должен делать это перед вызовом функции. (Таким образом, он знает, что стек был выровнен на 16 перед call
на main
.) Кроме того, вы скомпилировали с отключенной оптимизацией, и даже с оптимизацией можно легко потратить впустую пространство. — person miran80 schedule 08.04.2020
3. Мне не известны какие-либо инструменты визуализации. Однако вы можете скомпилировать с -fverbose-asm
, чтобы GCC аннотировал каждую инструкцию C-именами операндов. Он довольно хорошо работает в режиме отладки безумие (по умолчанию -O0
); с включенной оптимизацией переменные C обычно заменяются изобретенными временными. — person miran80 schedule 08.04.2020