动态堆分配混淆

3
以下是《Assembly Kip Irvine》书中提供的代码。我想知道这段代码在做什么。我知道getProcessHeap会返回一个32位整数句柄,该句柄指向程序现有堆区域中的EAX。如果函数成功,则会在EAX中返回堆的句柄。如果失败,则EAX中的返回值为NULL。
HeapAlloc从堆中分配一个内存块。如果它成功,则EAX中的返回值包含内存块的地址。如果失败,则EAX中的返回值为NULL。
CALLOC如何使用字符分配?
IALLOC如何使用整数分配?
LALLOC如何使用长整数分配?
在MALLOC中,如何从堆中分配以字节为单位的大小?谢谢
INCLUDE Irvine32.inc 

HANDLE          TEXTEQU  <WORD> 

GetProcessHeap  PROTO 



HeapAlloc       PROTO, 
                hHeap : HANDLE, 
                dwflags: DWORD, 
                dwbytes: DWORD

HeapFree        PROTO, 
                hHeap : HANDLE,
                dwflags: DWORD,
                lpmem : DWORD 

.data 

hHeap HANDLE ? 

.code 

CALLOC          MACRO size  
                mov   eax, sizeof BYTE 
                imul  eax, size

                push  eax 
                call  MALLOC 
                ENDM 

IALLOC          MACRO size 
                mov   eax, sizeof WORD 
                imul  eax, size 

                push  eax 
                call  MALLOC 
                ENDM 

LALLOC          MACRO   size 
                mov     eax, sizeof DWORD 
                imul    eax, size 

                push    eax 
                call MALLOC 
                ENDM

MALLOC          PROC 
                push        ebp
                mov         ebp, esp 

                invoke      GetProcessHeap 
                invoke      HeapAlloc, eax, 8, [ebp + 8]

                pop     ebp 
                ret     4

MALLOC          ENDP 

MEMFREE         PROC 
                push    ebp 
                mov     ebp, esp 

                invoke  GetProcessHeap 
                invoke  HeapFree, eax, 0, [ebp + 8]

                pop     ebp 
                ret     4 
MEMFREE         ENDP
1个回答

1
尽管您对这些函数的描述基本正确,但值得指出的是GetProcessHeapHeapAllocHeapFree函数实际上是Win32 API函数,这意味着它们作为操作系统的一部分提供给应用程序调用。Irvine库只提供了这些函数的原型,以使它们更易于调用。因此,可以通过阅读Microsoft的MSDN文档(上面的链接)直接获取这些函数的语义。

正如文档所解释的那样,当应用程序需要分配适量的内存时,从进程的默认堆中获取该内存是一种常见模式。这样可以避免创建和管理单独的私有堆的需要和开销,仅仅为了进行分配。在现代Windows编程中,您应该使用HeapAllocHeapFree(以及类似命名的)函数,而不是已过时的GlobalAllocLocalAlloc函数,这些函数有时仍然在未更新至本世纪的Windows编程材料中被使用或引用。

现在,在您的代码中,由于一切最终都会回到MALLOC,所以我们从这里开始。它设置了一个堆栈帧,调用GetProcessHeap,调用HeapAlloc,然后拆除堆栈帧。您应该立即看到一个错误。还记得您在描述GetProcessHeapHeapAlloc函数时如何小心地描述它们失败时会发生什么吗?好吧,您是正确的;这些函数可能会失败,编写正确的代码应该检查故障并处理它们。这段代码没有这样做。

在MALLOC中,如何从堆中分配字节大小?

实现 MALLOC 的方法非常简单:它只是获取进程堆的句柄,然后使用 HeapAlloc 从该堆中分配内存。因此,如果您想知道它是如何工作的,请返回 HeapAlloc 的文档。从这里,我们可以看到第一个参数是堆的句柄(在 eax 中由 GetProcessHeap 返回),第二个参数是控制分配的位标志的组合(在本例中为 8 或 HEAP_ZERO_MEMORY),第三个参数是要分配的字节数(在本例中为 [ebp + 8])。

[ebp + 8] 读取了在堆栈上传递给 MALLOC 函数的第一个(也可能是唯一的)参数。([ebp + 4] 是调用函数的指针(即 MALLOC 返回的位置),而 [ebp + 0] 是进入 MALLOC 函数时保存的 ebp 的原始值。)

在我看来,这是 MALLOC 函数的另一个(较小的)错误:它的文档不足!如果没有深入到实现中,我们怎么知道它需要一个参数,更不用说该参数的大小/类型和含义了。一个函数的基本目的和接口应该使用注释在代码中进行文档化。

所以,你的问题的答案是,MALLOC 分配与您要求分配的字节数相同的字节数。

CALLOC 中字符分配是如何使用的?

这是一个简单的宏,封装了 MALLOC 函数。其目的基本上是根据您要为其分配空间的字符数确定要分配多少字节,然后将该值作为参数传递给 MALLOC

CALLOC 宏接受单个参数 size,它是您要为其分配空间的字符数。 (顺便说一句,我认为 size 是此参数的名称选择不好,因为它没有很好地描述。)

然后,它将调用方指定的 size 乘以汇编时间由表达式 sizeof BYTE 确定的字符所需的实际字节数。 这将给出实际需要分配的字节数。 (现在,由于 sizeof BYTE 只是 1,这是相当愚蠢和低效的代码! 我想它是为了“可移植性”而编写的,但拜托——这是汇编语言!)

最后,它将结果(要分配的字节数)推送到堆栈上,并调用 MALLOC 进行分配。

现在你已经了解了CALLOC的工作原理,那么你应该了解所有这些*ALLOC宏的工作原理,因为它们都是相同的。 IALLOC将其参数乘以短整数的大小(sizeof WORD)以得到需要分配的实际字节数,而LALLOC将其参数乘以长整数的大小(sizeof DWORD)。
(请注意,尽管这些其他宏中的乘法是必需的,但它们也是低效的。 sizeof WORD == 2,所以您可以通过左移1位或更好地将值加到自身来完成。 sizeof DWORD == 4,因此这是左移2位。添加和移位比乘法执行速度要快得多。)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接