В настоящее время я играю, изучая кадры стека, пытаясь понять, как это работает. После прочтения нескольких статей, которые всегда объясняли, что общая структура будет следующей:
локальные переменные ‹--- SP
младший адрес
старый BP
‹--- BP
ret addr args старший адрес
У меня есть пример программы, которая вызывает функцию с тремя аргументами и имеет два буфера в качестве локальных переменных:
#include <stdio.h>
void function(int a, int b, int c);
int main()
{
function(1, 2, 3);
return 0;
}
void function(int a, int b, int c)
{
char buffer1[5];
char buffer2[10];
}
Я взглянул на ассемблерный код программы и был удивлен, не найдя того, что я ожидаю при вызове функции. Я ожидал чего-то вроде:
# The arguments are pushed onto the stack:
push 3
push 2
push 1
call function # Pushes ret address onto stack and changes IP to function
...
# In function:
# Push old base pointer onto stack and set current base pointer to point to it
push rbp
mov rbp, rsp
# Reserve space for stack frame etc....
Чтобы структура фрейма при выполнении функции была примерно такой:
buffers <--- SP low address
old BP <--- BP
ret Addr
1
2
3 high address
Но вместо этого происходит следующее:
Вызов функции:
mov edx, 3
mov esi, 2
mov edi, 1
call function
Зачем использовать регистры здесь, когда мы можем просто запихнуть в стек??? И в самой функции, которую мы вызываем:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
sub rsp, 48
mov DWORD PTR [rbp-36], edi
mov DWORD PTR [rbp-40], esi
mov DWORD PTR [rbp-44], edx
mov rax, QWORD PTR fs:40
mov QWORD PTR [rbp-8], rax
xor eax, eax
mov rax, QWORD PTR [rbp-8]
xor rax, QWORD PTR fs:40
je .L3
call __stack_chk_fail
Насколько я понимаю, для кадра стека зарезервировано 48 байт, верно? И далее, используя регистры из вызова функции, аргументы функции копируются в конец стека. Таким образом, это будет выглядеть примерно так:
3 <--- SP
2
1
??
??
old BP <--- BP
return Address
??
Я предполагаю, что буферы находятся где-то между аргументами и старым BP
. Но я действительно не уверен, где именно ... поскольку они оба всего 15 байтов всего и 48 байтов, где зарезервировано ... не будет ли там куча неиспользуемого пространства? Может ли кто-нибудь помочь мне описать, что здесь происходит? Это что-то, что зависит от процессора? Я использую Intel i7.
Привет, Брик
won't there be a bunch of unused space in there?
важно отметить, что выделение стека всегда будет округляться до кратного размера слова целевой архитектуры. наряду с этим ABI может также навязывать требования к выравниванию (например, требование 16 байтов на x64), что также может увеличить размер. - person Necrolis   schedule 03.04.2013