NASM я должен выдвинуть аргумент функции после вызова функции? - PullRequest
2 голосов
/ 24 марта 2020

Допустим, у меня есть функция назма, подобная этой:

inc:
    mov rax,[rsp + 8]
    add [rax],BYTE 1
    ret

И я вызываю эту функцию следующим образом:

push some_var
call inc

Я хочу передать аргумент функции через стек, поэтому я пу sh some_var, а затем вызываю свою функцию. В функции мой элемент занимает второе место в стеке, поэтому я беру его следующим образом: mov rax,[rsp+8]

Мой вопрос: после вызова функции мне как-то вытолкнуть мой аргумент из стека? Если да, могу ли я как-то удалить его из стека, я имею в виду выскочить, но не зарегистрировать? (Потому что мне больше не нужен этот аргумент.)

ОБНОВЛЕНИЕ: Я обнаружил, что могу просто add rsp,8, и именно так я могу удалить элемент из стека. Но это хорошая практика? Удалить аргумент из стека после вызова функции?

Ответы [ 2 ]

3 голосов
/ 24 марта 2020

В этом ответе я перечислил несколько способов удаления вещей из стека: Могу ли я POP-значение из стека, но не добавить его в сборку NASM? , чтобы подвести итог:

  • add rsp, x

  • lea rsp, [rsp + x]

  • mov rsp, rbp (также часть leave)

  • lea rsp, [rbp - x]

  • отображение в неиспользуемых в противном случае регистров

Помимо этого, Если удалить параметры из стека в вызывающем абоненте, определяется тем, предписывает ли ваше соглашение о вызовах очистка вызывающего абонента или наоборот, очистка вызываемого абонента. Очистка Callee выполняется путем указания количества байтов, удаляемых из стека, как непосредственного операнда в инструкции retn. Например:

    ...
    ; caller code
    push rax
    push rdi
    call testfunction
    ...


    ; function code
testfunction:
    push rbp
    mov rbp, rsp
    mov rcx, qword [rbp + 16]
    ...
    mov rsp, rbp
    pop rbp
    retn 16
3 голосов
/ 24 марта 2020

Рекомендуется передавать аргументы в регистрах, как в стандартных соглашениях о вызовах x86-64, используемых компиляторами. Например, x86-64 System V передает первые 6 аргументов целого числа / указателя в регистры, поэтому ваша функция будет
add byte [rdi], 1 / ret и не нуждается в очистке.
Вызывающему абоненту потребуется только mov edi, some_var или lea rdi, [rel some_var].

(Основы вызова функций пользовательского пространства описаны в Каковы соглашения о вызовах для системных вызовов UNIX & Linux на i386 и x86-64 , хотя в названии упоминаются системные вызовы. Полная информация в https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI. Также удобно посмотреть, что делают компиляторы для простых C функций: см. Как удалить «шум» из GCC / clang вывод ассемблера? )

Если вам нужно передать стековый аргумент, его вставка в фиктивный регистр, например pop rcx, на самом деле может быть более эффективной. чем add rsp, 8, по тем же причинам, почему компиляторы иногда используют пустышку push, чтобы зарезервировать один слот стека qword / повторно выровнять стек на 16: Почему эта функция pu sh RAX используется в стеке как Первая операция? Но если у вас более 1 стак k arg для очистки вызывающей стороны, используйте
add rsp, 8 * n, где n - количество слотов стека.

Также можно вызвать вызываемого абонента для очистки стека с помощью ret 8. Но это исключает возможность того, что вызывающий абонент оставит выделенное пространство стека и сохранит в нем mov, например, при подготовке к другому call.

...