Первый фрагмент кода работает отлично. Как предложил Джестер, вы пишете 64-битное значение в двух отдельных (32-разрядных) половинах. Так вы должны это сделать в 32-битной архитектуре. У вас нет 64-битных регистров, и вы не можете сразу записать 64-битные куски памяти. Но вы уже, казалось, знали это, поэтому я не буду его преследовать.
Во втором фрагменте кода вы пытались настроить таргетинг на 64-битную архитектуру (x86-64). Теперь вам больше не нужно записывать 64-битные значения в две 32-разрядные половинки, так как 64-разрядные архитектуры поддерживают 64-разрядные целые числа. У вас есть 64-разрядные регистры, и вы можете напрямую записать 64-битный фрагмент в память. Воспользуйтесь этим, чтобы упростить (и ускорить) код.
64-разрядные регистры - Rxx
вместо Exx
. Когда вы используете QWORD PTR
, вы захотите использовать Rxx
; когда вы используете DWORD PTR
, вы захотите использовать Exx
. Оба являются законными в 64-битном коде, но только 32-разрядные DWORD являются законными в 32-битном коде.
Несколько других вещей, чтобы отметить:
Хотя это вполне допустимо, чтобы очистить реестр с помощью MOV xxx, 0
, it is smaller and faster to use XOR eax, eax
, так это, как правило, что вы должны писать. Это очень старый трюк, который должен знать любой программист на ассемблере, и если вы когда-нибудь попробуете читать программы сборки других людей, вам нужно быть знакомым с этой идиомой. (Но на самом деле, в коде, который вы пишете, вам не нужно это делать вообще. По этой причине см. Пункт № 2.)
В 64-разрядном режиме all instructions implicitly zero the upper 32 bits when writing the lower 32 bits, так что вы можете просто напишите XOR eax, eax
вместо XOR rax, rax
. Это снова и снова и снова.
Вызывающее соглашение для 64-разрядных программ отличается от того, которое используется в 32-разрядных программах. Точная спецификация соглашения о вызове будет меняться в зависимости от используемой операционной системы. Как прокомментировал Питер Кордес, информация об этом содержится в документе x86 tag wiki. Оба Соглашения о вызовах Windows и Linux x64 передают по крайней мере первые 4 целочисленных параметра в регистрах (а не в стеке, как правило вызова x86-32), но , которые фактически используются в регистрах, различны. Кроме того, соглашения с 64-битными вызовами имеют разные требования, чем 32-битные соглашения о вызовах для того, как вы должны настроить стек перед вызовом функций.
(Поскольку ваш скриншот говорит что-то о «MASM», я предполагаю, что вы используете Windows в примере кода ниже.)
; Set up the stack, as required by the Windows x64 calling convention.
; (Note that we use the 64-bit form of the instruction, with the RSP register,
; to support stack pointers larger than 32 bits.)
sub rsp, 40
; Dynamically allocate 8 bytes of memory by calling malloc().
; (Note that the x64 calling convention passes the parameter in a register, rather
; than via the stack. On Windows, the first parameter is passed in RCX.)
; (Also note that we use the 32-bit form of the instruction here, storing the
; value into ECX, which is safe because it implicitly zeros the upper 32 bits.)
mov ecx, 8
call malloc
; Write a single 64-bit value into memory.
; (The pointer to the memory block allocated by malloc() is returned in RAX.)
mov qword ptr [rax], 1
; ... do whatever
; Clean up the stack space that we allocated at the top of the function.
add rsp, 40
Если вы хотите сделать это в 32-битных половин, даже на 64-битной архитектуре, вы, конечно, мог. Это будет выглядеть следующим образом:
sub rsp, 40 ; set up stack
mov ecx, 8 ; request 8 bytes
call malloc ; allocate memory
mov dword ptr [eax], 1 ; write "1" into low 32 bits
mov dword ptr [eax+4], 2 ; write "2" into high 32 bits
; ... do whatever
add rsp, 40 ; clean up stack
Обратите внимание, что эти последние две MOV
инструкции идентичную к тому, что вы написали в 32-разрядной версии кода. Это имеет смысл, потому что вы делаете ровно то же самое.
Причина, по которой код, который вы изначально писали, не работал, потому что EAX
не содержит QWORD PTR
, он содержит DWORD PTR
. Следовательно, ассемблер генерировал ошибку «неверные инструкции», потому что было несоответствие. Это по той же причине, что вы не компенсируете 8, потому что DWORD PTR
составляет всего 4 байта. A QWORD PTR
действительно 8 байтов, но у вас нет одного из них в EAX
.
Или, если вы хотите написать 16 байт:
sub rsp, 40 ; set up stack
mov ecx, 16 ; request 16 bytes
call malloc ; allocate memory
mov qword ptr [rax], 1 ; write "1" into low 64 bits
mov qword ptr [rax+8], 2 ; write "2" into high 64 bits
; ... do whatever
add rsp, 40 ; clean up stack
сравнить эти три фрагмента кода, и убедитесь, что вы понимаете различия и почему они должны быть написаны, как они!
Вы не можете напрямую записать 64-битные целые числа в память. Вам нужно будет сделать это в двух половинах, например. 'mov dword ptr [eax], 1; mov dword ptr [eax + 4], 0'. – Jester
Вставьте код вместо этих скриншотов. – DimaSan
Предполагается, что ваша 64-разрядная версия должна быть x86-64, так как вы используете 'qword ptr' с [целой инструкцией MOV] (http://www.felixcloutier.com/x86/MOV.html)? Если это так, соглашение о 64-битном вызове отличается от другого ... См. Http://stackoverflow.com/tags/x86/info –