push/pop eax дает мне недопустимую инструкцию во время компиляции

Я только начинаю программировать на fasm / x86, и я начал с 64-битных образцов, так как моей целью для моего проекта, в котором мне понадобится сборка, является 64-битный xeon.

Я начал с PE64DEMO и модифицировал его, чтобы добавить цикл, однако он терпит неудачу после первой итерации, поскольку (из того, что я мог собрать в Интернете) функции API Windows могут изменять регистры, а не восстанавливать их. Я также читал, что должен сохранять то, что мне нужно, чтобы я сам выталкивал и выталкивал из стека, я прокомментировал push и pop, если я раскомментирую их, я получаю ошибку времени компиляции с сообщением «Ошибка: недопустимая инструкция pop eax».

Весь код файла ниже:

; Example of 64-bit PE program

format PE64 GUI
include 'win64a.inc'
entry start


section '.text' code readable executable

  start:
    sub     rsp,8*5         ; reserve stack for API use and make stack dqword aligned



    ; Assembly code representing expr1 here (for instance mov [count], 0)
    mov eax,10
    .for_loop_check:
    ; Assembly code representing expr2 that jumps to .exit_for_loop when false (for instance cmp [count], TOP_VALUE / jae .exit_for_loop when expr2 is "count < TOP_VALUE"). Note that if expr2 is absent then so is the jump.

    cmp eax,0
    jz .exit_for_loop

    ;push eax

    ; body code here
    mov     r9d,0
    lea     r8,[_caption]
    lea     rdx,[_message]
    mov     rcx,0
    call    [MessageBoxA]

    ;pop eax

    ; Assembly code representing expr3 here (for instance inc [count])
    dec eax
    jmp .for_loop_check

    .exit_for_loop:

    mov     ecx,eax
    call    [ExitProcess]

section '.data' data readable writeable

  _caption db 'Win64 assembly program',0
  _message db 'Hello World!',0

section '.idata' import data readable writeable

   library kernel32,'KERNEL32.DLL',\
      user32,'USER32.DLL'

  include 'api\kernel32.inc'
  include 'api\user32.inc'

person Ronan Thibaudau    schedule 07.06.2016    source источник
comment
Возможный дубликат Push and Pop на AMD64   -  person Ari0nhh    schedule 07.06.2016
comment
@ Ari0nhh Я бы не сказал, что это дубликат (это полезно для кого-то, кто ищет fasm и незаконные инструкции с помощью pop или push, гугление ничего не дало). Однако похоже, что это может быть то же решение, я проверю его и вернусь сюда.   -  person Ronan Thibaudau    schedule 07.06.2016
comment
В 64-битной версии вы действительно можете использовать только 64-битные регистры, поэтому это должны быть push rax и pop rax, а не eax.   -  person Rudy Velthuis    schedule 07.06.2016
comment
@RudyVelthuis Я только что закончил тестирование на основе этого, оно все еще не работает, но решает проблему в моем вопросе (fasm больше не дает сбой во время компиляции). Если вы опубликуете это как ответ, я приму это. Однако, если у вас есть идеи, что также может произойти с моей новой проблемой, чтобы избежать нового вопроса, это было бы здорово. Я заменил eax на rax, все работает, если я оставлю push / pop прокомментированным (за исключением того, что цикл будет продолжаться вечно), но как только я их раскомментирую, я получаю нарушение прав доступа внутри MessageBox, я не делаю правильную вещь, нажимая зарегистрироваться и открыть его, как только победа вернется?   -  person Ronan Thibaudau    schedule 07.06.2016
comment
Различные платформы, такие как Windows 64 и Linux 64 бит, имеют ABI, который сообщает вам, какие регистры для чего используются при вызове функции, и какие регистры должны быть сохранены, а какие нет. RAX или EAX часто используются для хранения возвращаемого функцией значения.   -  person Rudy Velthuis    schedule 07.06.2016
comment
Спасибо за ключевое слово ABI, оно помогло мне найти все, что мне нужно, в msdn!   -  person Ronan Thibaudau    schedule 07.06.2016
comment
Если вы нашли ABI, то, возможно, поняли, что вы можете использовать энергонезависимый регистр (тот, который должен быть сохранен) функциями. RBX вместо RAX для счетчика циклов будет работать. Вы можете спросить, почему PUSH/POP не работает. Есть требование перед вызовом на 64-битной Windows стек выровнять по 16-байтной границе. Без PUSH/POP стек выравнивается по 16 байтам при вызове окна сообщений и т. д. При входе в start он смещается на 8 (адрес возврата в стеке). Вы вычитаете 40 из стека в начале. 48 делится на 16 без остатка. Если вы добавите PUSH, стек снова сместится на 8   -  person Michael Petch    schedule 07.06.2016
comment
Мораль истории: убедитесь, что при вызове функции стек выровнен по 16-байтовой границе в точке непосредственно перед вызовом.   -  person Michael Petch    schedule 07.06.2016
comment
@RonanThibaudau: см. также вики тегов x86 для получения ссылок на документы и руководства.   -  person Peter Cordes    schedule 07.06.2016
comment
@MichaelPetch Спасибо, это очень помогло, вы хотите превратить это в ответ, чтобы я мог его принять, или я должен это сделать?   -  person Ronan Thibaudau    schedule 08.06.2016


Ответы (1)


Было несколько проблем с моим кодом.

Начнем с того, что, как прокомментировал Руди Велтуис, мой код был незаконным и по праву отклонен fasm, push eax и pop eax никогда не действительны в X64, поскольку eax является 32-битным регистром, а X64 не позволяет помещать 32-битные значения в стек. Переключение на 64-битный регистр решило эту проблему.

;change
push eax
pop eax
;to
push rax;
pop rax;

При этом теперь он компилируется и позволяет мне сохранять мой регистр во время вызова, но код теперь segfaults внутри Windows API. Как упомянул Майкл Петч, это связано с тем, что, как только я помещаю данные в стек, я нарушаю выравнивание, которое было выполнено ранее, а Windows 64 требует, чтобы стек был выровнен по 16 байтам, однако вместо исправления выравнивания решение заключается в простом использовании энергонезависимого регистра среди те, которые описаны в Windows X64 ABI, и отказаться от использования стека для этой операции.

Окончательный код с изменениями:

format PE64 GUI
include 'win64a.inc'
entry start

section '.text' code readable executable

  start:
        sub     rsp,8*5         ; reserve stack for API use and make stack dqword aligned

        ;use rbx as counter as it is preserved across windows API calls
        mov rbx,10

        .for_loop_check:

        cmp rbx,0
        je .exit_for_loop

        mov     r9d,0
        lea     r8,[_caption]
        lea     rdx,[_message]
        mov     rcx,0
        call    [MessageBoxA]

        ;no more pushing and poping to the stack required, rbx is guaranteed to be unmodified by the windows API

        dec rbx
        jmp .for_loop_check

        .exit_for_loop:

        mov     ecx,eax
        call    [ExitProcess]

section '.data' data readable writeable

  _caption db 'Win64 assembly program',0
  _message db 'Hello World!',0

section '.idata' import data readable writeable

  library kernel32,'KERNEL32.DLL',\
          user32,'USER32.DLL'

  include 'api\kernel32.inc'
  include 'api\user32.inc'
person Ronan Thibaudau    schedule 08.06.2016
comment
Обратите внимание, что если бы вы писали функцию, которая возвращала значение, вместо вызова ExitProcess вам нужно было бы сохранить/восстановить rbx, а не просто стирать rbx вызывающего объекта. Поскольку вам это не нужно, лучше не тратить инструкции на это, но эта инструкция заслуживает комментария о том, почему это безопасно в данном случае. - person Peter Cordes; 08.06.2016