【问题标题】:Allocating memory using malloc() in 32-bit and 64-bit assembly language在 32 位和 64 位汇编语言中使用 malloc() 分配内存
【发布时间】:2017-04-27 11:55:04
【问题描述】:

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

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

【问题讨论】:

  • 不能将 64 位整数直接写入内存。您将不得不分两半进行,例如mov dword ptr [eax], 1; mov dword ptr [eax+4], 0.
  • 粘贴代码而不是这个截图。
  • 您的 64 位版本是否应该是 x86-64,因为您使用的是 qword ptrinteger MOV instruction?如果是这样,那么 64 位调用约定就不同了……见stackoverflow.com/tags/x86/info

标签: windows assembly x86 malloc x86-64


【解决方案1】:

代码的第一个 sn-p 运行良好。正如 Jester 所建议的那样,您正在将一个 64 位值分成两个单独的(32 位)半部分。这是您必须在 32 位架构上执行此操作的方式。您没有可用的 64 位寄存器,也不能一次写入 64 位内存块。不过你好像已经知道了,我就不赘述了。

在第二个 sn-p 代码中,您尝试针对 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, 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 位调用约定有不同的要求。

(由于您的屏幕截图中提到了“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

比较这三个sn-ps的代码,确保你了解它们的区别以及为什么需要按原样编写它们!

【讨论】:

  • 在第二个 sn-p 代码中,您尝试针对 64 位架构 ...或者他只是想在 32 位中存储一个 64 位整数模式。另请注意,mov qword ptr [rax], 1 仅适用于 32 位符号扩展立即数。
猜你喜欢
  • 2017-03-20
  • 1970-01-01
  • 2015-12-07
  • 1970-01-01
  • 2011-01-31
  • 1970-01-01
  • 2019-04-15
  • 2010-10-09
  • 1970-01-01
相关资源
最近更新 更多