Различные способы выделения памяти для стека в gcc

Как 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. Это странно, потому что это значительно усложняет поиск онлайн-руководств.

См. также:  CS50 PSSet 2: Замена

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

Понравилась статья? Поделиться с друзьями:
IT Шеф
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: