使用32位和64位汇编语言的malloc()分配内存

时间:2016-12-12 15:12:03

标签: assembly x86 malloc x86-64

我必须做64位堆栈。为了让我对malloc感到满意,我设法将两个整数(32位)写入内存并从那里读取:

32bits

但是,当我尝试用64位执行此操作时:

64bits

1 个答案:

答案 0 :(得分:0)

第一段代码完美无缺。正如Jester建议的那样,您正在以两个独立的(32位)半部分写入64位值。这是您在32位架构上执行此操作的方式。您没有可用的64位寄存器,并且您无法一次写入64位内存块。但你似乎已经知道了,所以我不会讨论它。

在第二段代码中,您尝试使用64位体系结构(x86-64)。 现在,您不再需要在两个32位半部分中写入64位值,因为64位体系结构本身支持64位整数。您可以使用64位宽的寄存器,并且可以直接将64位块写入存储器。利用它来简化(并加速)代码。

64位寄存器为Rxx而不是Exx。使用QWORD PTR时,您需要使用Rxx;当您使用DWORD PTR时,您需要使用Exx。两者在64位代码中都是合法的,但在32位代码中只有32位DWORD是合法的。

还有其他几点需要注意:

  1. 虽然使用MOV xxx, 0it 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位程序中使用的约定不同。调用约定的确切规范将根据您使用的操作系统而有所不同。正如Peter Cordes评论的那样,x86 tag wiki中有相关信息。 两者 Windows和Linux x64调用约定至少传递寄存器中的前4个整数参数(而不是像x86-32调用约定那样在堆栈上),但是哪些寄存器是实际使用的是不同的。此外,64位调用约定与32位调用约定的要求不同,因为在调用函数之前必须如何设置堆栈。

  4. (由于您的屏幕截图说明了“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个字节。 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
    

    比较这三段代码,并确保您了解它们之间的差异和原因!