-1

I have to do a 64 bits stack. To make myself comfortable with malloc I managed to write two integers(32 bits) into memory and read from there:

32bits

But, when i try to do this with 64 bits:

64bits

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Crisan
  • 1
  • 1
  • 5
  • 4
    You can't write 64 bit integers into memory directly. You will have to do it in two halves, e.g. `mov dword ptr [eax], 1; mov dword ptr [eax+4], 0`. – Jester Dec 12 '16 at 15:14
  • 9
    Paste code instead of this screenshots. – DimaSan Dec 12 '16 at 15:23
  • 2
    Is your 64-bit version supposed to be x86-64, since you're using `qword ptr` with an [integer MOV instruction](http://www.felixcloutier.com/x86/MOV.html)? If so, the 64-bit calling convention is different... See http://stackoverflow.com/tags/x86/info – Peter Cordes Dec 12 '16 at 19:58

1 Answers1

1

The first snippet of code works perfectly fine. As Jester suggested, you are writing a 64-bit value in two separate (32-bit) halves. This is the way you have to do it on a 32-bit architecture. You don't have 64-bit registers available, and you can't write 64-bit chunks of memory at once. But you already seemed to know that, so I won't belabor it.

In the second snippet of code, you tried to target a 64-bit architecture (x86-64). Now, you no longer have to write 64-bit values in two 32-bit halves, since 64-bit architectures natively support 64-bit integers. You have 64-bit wide registers available, and you can write a 64-bit chunk to memory directly. Take advantage of that to simplify (and speed up) the code.

The 64-bit registers are Rxx instead of Exx. When you use QWORD PTR, you will want to use Rxx; when you use DWORD PTR, you will want to use Exx. Both are legal in 64-bit code, but only 32-bit DWORDs are legal in 32-bit code.

A couple of other things to note:

  1. Although it is perfectly valid to clear a register using MOV xxx, 0, it is smaller and faster to use XOR eax, eax, so this is generally what you should write. It is a very old trick, something that any assembly-language programmer should know, and if you ever try to read other people's assembly programs, you'll need to be familiar with this idiom. (But actually, in the code you're writing, you don't need to do this at all. For the reason why, see point #2.)

  2. In 64-bit mode, all instructions implicitly zero the upper 32 bits when writing the lower 32 bits, so you can simply write XOR eax, eax instead of XOR rax, rax. This is, again, smaller and faster.

  3. The calling convention for 64-bit programs is different than the one used in 32-bit programs. The exact specification of the calling convention is going to vary, depending on which operating system you're using. As Peter Cordes commented, there is information on this in the x86 tag wiki. Both Windows and Linux x64 calling conventions pass at least the first 4 integer parameters in registers (rather than on the stack like the x86-32 calling convention), but which registers are actually used is different. Also, the 64-bit calling conventions have different requirements than do the 32-bit calling conventions for how you must set up the stack before calling functions.

(Since your screenshot says something about "MASM", I'll assume that you're using Windows in the sample code below.)

; 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

If you wanted to do this in 32-bit halves, even on a 64-bit architecture, you certainly could. That would look like the following:

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

Note that these last two MOV instructions are identical to what you wrote in the 32-bit version of the code. That makes sense, because you're doing exactly the same thing.

The reason the code you originally wrote didn't work is because EAX doesn't contain a QWORD PTR, it contains a DWORD PTR. Hence, the assembler generated the "invalid instruction operands" error, because there was a mismatch. This is the same reason that you don't offset by 8, because a DWORD PTR is only 4 bytes. A QWORD PTR is indeed 8 bytes, but you don't have one of those in EAX.

Or, if you wanted to write 16 bytes:

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

Compare these three snippets of code, and make sure you understand the differences and why they need to be written as they are!

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • _In the second snippet of code, you tried to target a 64-bit architecture_ ... or maybe he just wanted to store a 64 bit integer in 32 bit mode. Also note that `mov qword ptr [rax], 1` only works for 32 bit sign extended immediates. – Jester Dec 13 '16 at 21:35