Правильное управление стека в ассемблере X.86 X64 - PullRequest
0 голосов
/ 01 июня 2018

Итак, прочитав краткое руководство по архитектуре x64, я написал несколько ассемблеров.

https://software.intel.com/en-us/articles/introduction-to-x64-assembly

Функция ассемблера вызывается из C. В свою очередь, ассемблер вызывает функции C.

Я не уверен в том, как работает механика стека, поскольку мне кажется, что она несколько раз портит стек.

Следующий код демонстрирует:

PUBLIC Lbra_R_A ; Op 16 - Long Branch Always
Lbra_R_A PROC
    sub rsp, 28h
    push rbx ; must preserve rbx
    ; Calc destination branch address by Adding the two Bytes at [PC+1] (high byte) and [PC+2] (low byte) with PC+2 reg
    ; Get first byte high byte
    movzx rcx, word ptr [pc_s]
    mov rbx, rcx ; save pc_s into temp
    inc bx ; inc temp pc_s
    call MemRead8_s ; returns byte in ax (al)
    push ax ; save high byte
    ; Get second byte low byte @ pc_s
    mov rcx, rbx
    inc bx ; inc temp pc_s
    call MemRead8_s ; returns byte in ax (al) - this call destroys saved high byte???
    ; combine low and high bytes to make 16 bit 2 complements offset
    pop dx ; get saved high byte - wrong value
    mov ah, dl ; move the high byte to high position ; ax now contains 16 bit offset
    add bx, ax ; bx now contains pc_s(+2) + offset
    mov word ptr [pc_s], bx
    pop rbx ; must restore rbx - wrong value???
    add rsp, 28h
    ret
Lbra_R_A ENDP

Я установил стекс sub rsp, 28h, но я не уверен, почему, и я понятия не имею, что мне разрешено делать в этой области 28h !!!Это для меня или это зарезервировано.Однако без этого мой код даже не запускается !!!

Далее я сохраняю регистр rbx, потому что он считается энергонезависимым.Однако в конце, когда я восстанавливаю rbx, это не то же самое, что я сохранил ???

Средний код Я сохраняю / нажимаю регистр топора перед вызовом функции C под названием MemRead8_s (предоставлена ​​мной).Однако в тот момент, когда я вызываю эту функцию, значение ax, хранящееся в стеке, перезаписывается, поэтому, когда я пытаюсь восстановить его несколько инструкций позже, это неправильно !!!Значение rsp до и после этого вызова, кажется, одинаково, так что сделал вызов этой функции со стеком?

Может кто-нибудь пролить свет на то, что является правильным протоколом настройки стека, и, возможно, объяснить, почему мой стек сохраняетпортятся?

1 Ответ

0 голосов
/ 01 июня 2018

Ваша функция владеет пространством стека ниже начального значения RSP (при вводе функции) и выше текущего значения RSP.

В Windows вашей функции также принадлежит 32 байта выше адреса возврата (теньпространство).Обязательно зарезервируйте это место перед вызовом другой функции, если только это не частная вспомогательная функция, которая не использует теневое пространство.

В Linux и других не Windows, ABI System V x86-64 говорит, что ваша функциявладеет 128 байтами ниже текущего RSP (красная зона).Очевидно, что push или вызовы функций будут происходить в этом пространстве, так что это в основном полезно в конечных функциях, чтобы избежать добавления / добавления RSP.Но это безопасно от засорения обработчиками сигналов.


Так что sub rsp, 28h выделяет 0x28 байт стекового пространства (и выравнивает стек по 16 байтам , потому что это было 16-Выровненный байт до того, как call в вызывающей стороне выдвинул адрес возврата. (Вы нарушаете это выравнивание с помощью 16-битного (2-байтового) нажатия, это плохо). ret в основном pop rip, поэтому у вас естьчтобы восстановить стек до его первоначального значения, прежде чем вы сможете запустить ret.

Не использовать 16-битный push / pop; сохранять / восстанавливать полный 64-битный регистр. MemRead8_s допускается приниматьчто RSP был выровнен на 16 байт перед вызовом, который выдвинул для него адрес возврата, вам повезло, что он не полагается на выравнивание стека.)

...