Сборка странное использование стека
У меня есть несколько вопросов о том, как стек используется в сборке. Насколько я знаю, регистр% rsp используется в качестве указателя стека. Чтобы выделить новую память для стека в коде ассемблера, вы просто вычитаете необходимое количество из% rsp, перемещая его назад. Затем вы можете получить доступ к вновь выделенной памяти, добавив определенные значения в% rsp.
Однако, когда я компилирую код C с GCC в Assembly, иногда я получаю странные результаты.
Когда я делаю некоторые функции, подобные этой:
int fun(int arg1, int arg2)
{
return arg2;
}
Я ожидаю что-то вроде:
fun: pushq %rbp
movq %rsp, %rbp
sub $8, %rsp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %eax
addq $8, %rsp
popq %rbp
ret
Вместо этого я получаю это:
fun:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl 16(%rbp), %eax
popq %rbp
ret
На самом деле он не перемещает указатель стека, он просто использует пространство позади него. Становится еще страннее, когда я передаю 7 аргументов:
int fun(int arg1, int arg2, int arg3, int arg4, int arg5, int arg6, int arg7)
{
return arg7;
}
Теперь код сборки:
fun:
pushq %rbp
movq %rsp, %rbp
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl %edx, -12(%rbp)
movl %ecx, -16(%rbp)
movl %r8d, -20(%rbp)
movl %r9d, -24(%rbp)
movl 16(%rbp), %eax
popq %rbp
ret
После предыдущего примера я ожидал, что код ничего не вычитает из% rsp. Использование меньших адресов совершенно нормально, там ничего нет. Но как насчет 16(% RSP)? Он должен указывать на пространство, уже выделенное в стеке, не будет ли оно что-то перезаписывать?
И последнее, но не менее важное, если я напишу эту простую функцию:
void fun(int arr[], int n)
{
int i = 0;
while(i < n)
{
++arr[i++];
}
}
Код сборки:
fun:
.LFB0:
pushq %rbp
movq %rsp, %rbp
movq %rdi, -24(%rbp)
movl %esi, -28(%rbp)
movl $0, -4(%rbp)
jmp .L2
.L3:
movl -4(%rbp), %eax
leal 1(%rax), %edx
movl %edx, -4(%rbp)
cltq
leaq 0(,%rax,4), %rdx
movq -24(%rbp), %rax
addq %rdx, %rax
movl (%rax), %edx
addl $1, %edx
movl %edx, (%rax)
.L2:
movl -4(%rbp), %eax
cmpl -28(%rbp), %eax
jl .L3
nop
popq %rbp
ret
Как видите, указатель на arr хранится в диапазоне от -24(%rsp) до -16(%rsp). Целое число n хранится в диапазоне от -28(%rsp) до -24(%rsp), а целое число i хранится в диапазоне от -4(%rsp) до (% rsp). Как насчет промежутка между -16(% rsp) и -4(%rsp)? Почему это не используется? Есть ли какая-то особая причина этого?