2016-12-12 10 views
-1

Я должен сделать стек из 64 бит. Для того, чтобы сделать себя комфортно с таНос мне удалось записать два целых числа (32 бита) в память и читать оттуда:Выделение памяти с помощью malloc() в 32-разрядном и 64-битном языке ассемблера

32bits

Но, когда я пытаюсь сделать это с 64 битами:

64bits

+3

Вы не можете напрямую записать 64-битные целые числа в память. Вам нужно будет сделать это в двух половинах, например. 'mov dword ptr [eax], 1; mov dword ptr [eax + 4], 0'. – Jester

+7

Вставьте код вместо этих скриншотов. – DimaSan

+1

Предполагается, что ваша 64-разрядная версия должна быть x86-64, так как вы используете 'qword ptr' с [целой инструкцией MOV] (http://www.felixcloutier.com/x86/MOV.html)? Если это так, соглашение о 64-битном вызове отличается от другого ... См. Http://stackoverflow.com/tags/x86/info –

ответ

0

Первый фрагмент кода работает отлично. Как предложил Джестер, вы пишете 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-битном коде.

Несколько других вещей, чтобы отметить:

  1. Хотя это вполне допустимо, чтобы очистить реестр с помощью MOV xxx, 0, it is smaller and faster to use XOR eax, eax, так это, как правило, что вы должны писать. Это очень старый трюк, который должен знать любой программист на ассемблере, и если вы когда-нибудь попробуете читать программы сборки других людей, вам нужно быть знакомым с этой идиомой. (Но на самом деле, в коде, который вы пишете, вам не нужно это делать вообще. По этой причине см. Пункт № 2.)

  2. В 64-разрядном режиме all instructions implicitly zero the upper 32 bits when writing the lower 32 bits, так что вы можете просто напишите XOR eax, eax вместо XOR rax, rax. Это снова и снова и снова.

  3. Вызывающее соглашение для 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 

сравнить эти три фрагмента кода, и убедитесь, что вы понимаете различия и почему они должны быть написаны, как они!

+0

. Во втором фрагменте кода вы пытались настроить 64-разрядную архитектуру ... или, возможно, он просто хотел сохранить 64-битное целое число в 32-битном режиме. Также обратите внимание, что 'mov qword ptr [rax], 1' работает только для 32-битного знака, расширенного. – Jester