Понимание стековых и базовых указателей в рекурсивных вызовах функций - PullRequest
0 голосов
/ 26 января 2020

Мы на машине x86-64. Рассмотрим

int foo(int a) {
    if( a > 0)
            foo(a-1);

        return 0;
}

в сборке, вот:

foo:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $16, %rsp
        movl    %edi, -4(%rbp)
        cmpl    $0, -4(%rbp)
        jle     .L2
        movl    -4(%rbp), %eax
        subl    $1, %eax
        movl    %eax, %edi
        call    foo
.L2:
        movl    $0, %eax
        leave
        ret

Теперь я пытаюсь выяснить, что именно здесь происходит. Я получаю общую сборку, вопрос относится к строке 2,3 и 4 фрагмента кода сборки.

Мы запускаем нашу программу. Мы получаем виртуальную память. Затем происходит следующее, когда функция foo() вызывается в первый раз:

pushq %rbp Мы помещаем sh 8-байтов в стек, тогда как стек начинается с% rbp.

movq %rsp, %rbp Мы позволяем% rsp указывать на% rbp, то есть на начало стека. Вот почему мы сначала выдвинули эти 8 байтов - нам нужно хранилище для $ rsp. Правильно?

subq $16, %rsp Мы перемещаем% rsp вниз на 16 байт.

Теперь я не думаю, что понял это правильно. В чем конкретно разница между pushq и просто перемещением %rsp напрямую?

Когда тогда в основном выполняются условие if и рекурсия. Рекурсия приводит к повторению вышеупомянутых трех строк. Теперь, не приводит ли это к последовательному перемещению указателя базы и стека на 16 байт? Как мы узнаем, с чего мы начинали?

...