Распределение стека, почему дополнительное место? - PullRequest
4 голосов
/ 25 марта 2012

Я немного поигрался, чтобы лучше понять соглашения о вызовах и способ обработки стека, но я не могу понять, почему main выделяет три дополнительных двойных слова при настройке стека (на <main+0>). Он не выровнен ни к 8, ни к 16 байтам, поэтому, насколько я знаю, это не то, почему. Насколько я понимаю, main требует 12 байтов для двух параметров для функции и возвращаемого значения.

Чего мне не хватает?

Программа написана на языке C и скомпилирована с помощью gcc -ggdb для архитектуры x86.

Редактировать: я удалил флаг -O0 из gcc, и это не имело никакого значения для вывода.

(gdb) disas main
Dump of assembler code for function main:
    0x080483d1 <+0>:    sub    esp,0x18
    0x080483d4 <+3>:    mov    DWORD PTR [esp+0x4],0x7
    0x080483dc <+11>:   mov    DWORD PTR [esp],0x3
    0x080483e3 <+18>:   call   0x80483b4 <func>
    0x080483e8 <+23>:   mov    DWORD PTR [esp+0x14],eax
    0x080483ec <+27>:   add    esp,0x18
    0x080483ef <+30>:   ret    
End of assembler dump.

Редактировать: Конечно, я должен был опубликовать код C:

int func(int a, int b) {
    int c = 9;
    return a + b + c;
}

void main() {
    int x;
    x = func(3, 7);
}

Платформа - Arch Linux i686.

Ответы [ 2 ]

5 голосов
/ 25 марта 2012

Параметры функции (включая, но не ограничиваясь main) уже находятся в стеке при входе в функцию. Пространство, которое вы выделяете внутри функции, предназначено для локальных переменных. Для функций с простыми типами возврата, такими как int, возвращаемое значение обычно будет в регистре (eax, с типичным 32-разрядным компилятором на x86).

Если, например, main было что-то вроде этого:

int main(int argc, char **argv) { 
   char a[35];

   return 0;
}

... мы ожидаем, что в стеке будет выделено не менее 35 байт, когда мы введем main, чтобы освободить место для a. Предполагая 32-битную реализацию, она обычно округляется до следующего кратного 4 (в данном случае 36) для поддержания 32-битного выравнивания стека. Мы не ожидаем увидеть пространство, выделенное для возвращаемого значения. argc и argv будут в стеке, но они уже будут в стеке до ввода main, поэтому main не нужно будет ничего делать, чтобы выделить для них место.

В случае выше, после выделения места для a, a обычно начинается в [esp-36], argv будет в [esp-44] и argc будет в [esp-48] (или эти два может быть перевернут - в зависимости от того, были ли аргументы сдвинуты слева направо или справа налево). Если вам интересно, почему я пропустил [esp-40], это будет обратный адрес.

Редактировать: вот схема стека при входе в функцию и после настройки фрейма стека:

enter image description here

Редактировать 2: Исходя из вашего обновленного вопроса, то, что у вас есть, является немного окольным, но не особенно трудным для понимания. После ввода в main он выделяет пространство не только для переменных, локальных для main, но и для параметров, передаваемых в функцию, которую вы вызываете из main.

Это составляет, по крайней мере, часть выделенного дополнительного пространства (хотя не обязательно все).

2 голосов
/ 28 марта 2012

Это выравнивание. Я почему-то предположил, что esp будет выровнен с самого начала, что явно не так.

gcc выравнивает кадры стека по 16 байтов по умолчанию, что и произошло.

...