Сборка странное использование стека

У меня есть несколько вопросов о том, как стек используется в сборке. Насколько я знаю, регистр% 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)? Почему это не используется? Есть ли какая-то особая причина этого?

0 ответов

Другие вопросы по тегам