memory-managementx86heap-memorymasmirvine32

Dynamic Heap Allocation Confusion


The following is a code provided in the Assembly Kip Irvine book. I want to know what this code is doing. I understand that the getProcessHeap returns a 32 bit integer handle to the program's existing heap area in EAX. If the function were to suceed, it will return a handle to the heap in EAX. If it fails, the return value in EAX is NULL

The HeapAlloc allocates a block of memory from a heap. If it succeeds, the return value in EAX contains the address of memory block. If it fails, the returned value in EAX is NULL.

How is character allocation being used in CALLOC?

How is integer allocation being used in IALLOC?

Hoe is long integer allocation being used in LALLOC?

In MALLOC, how is size in bytes being allocated from the heap? Thanks

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

Solution

  • Although your description of these functions are essentially correct, it is worth pointing out that the GetProcessHeap, HeapAlloc, and HeapFree functions are actually Win32 API functions, meaning that they are provided as part of the operating system for applications to call. Irvine's library has just provided prototypes for these functions to make them easier to call. As such, the semantics for these functions can be obtained directly from the horse's mouth by reading Microsoft's MSDN documentation (links above).

    Like the documentation explains, it is a common pattern for an application that needs to allocate a moderate sized amount of memory to just obtain that memory from the process's default heap. This saves the need to create and overhead of managing a separate, private heap, just to make an allocation. The HeapAlloc and HeapFree (and similarly-named) functions are the ones you should be using in modern Windows programming, and not the obsolete GlobalAlloc or LocalAlloc functions that you sometimes still see used or referenced in Windows programming materials that haven't been updated for this century.

    Now, in the code you have, since everything ultimately goes back to MALLOC, let's start there. It sets up a stack frame, calls GetProcessHeap, calls HeapAlloc, and then tears down the stack frame. You should immediately see a bug. Remember how in your description of the GetProcessHeap and HeapAlloc functions, you were careful to describe what happens if they fail? Well, you were right; these functions can fail, and correctly written code should be checking for failures and handling them. This code does not.

    In MALLOC, how is size in bytes being allocated from the heap?

    The implementation of MALLOC is pretty simple: all it really does is obtain a handle to the process heap and then use HeapAlloc to allocate memory from that heap. So if you want to know how it works, go back to the documentation for HeapAlloc. From this, we see that the first parameter is the handle to the heap (returned from GetProcessHeap in eax), the second parameter is a bitwise combination of flags that control the allocation (in this case, 8, or HEAP_ZERO_MEMORY), and the third parameter is the number of bytes to allocate (in this case, [ebp + 8]).

    [ebp + 8] reads the first (and presumably only) parameter that was passed to the MALLOC function on the stack. ([ebp + 4] is the pointer to the calling function (where MALLOC will ret), and [ebp + 0] is the original value of ebp, saved upon entry to the MALLOC function.)

    In my opinion, this is another (minor) bug with the MALLOC function: it is insufficiently documented! How are we supposed to know that it takes a parameter, much less what the size/type and meaning of that parameter are, without diving into its implementation? A function's basic purpose and interface should be documented right there in the code, using a comment.

    So, the answer to your question is, MALLOC allocates as many bytes as you ask it to allocate.

    How is character allocation being used in CALLOC?

    This is a simple macro wrapped around the MALLOC function. Its purpose is basically to determine how many bytes to allocate, based on the number of characters you want to allocate space for, and then pass that value as the parameter to MALLOC.

    The CALLOC macro takes a single parameter, size, which is the number of characters you want to allocate space for. (By the way, I think that size is a poor choice of name for this parameter, as it isn't very descriptive.)

    It then multiplies the caller-specified size by the actual number of bytes required by a character, which is determined at assembly time by the expression sizeof BYTE. This will give the number of bytes that actually need to be allocated. (Now, since sizeof BYTE is just 1, this is pretty silly and inefficient code! I guess it was written to be "portable", but come on—it's assembly language!)

    Finally, it pushes the result (the number of bytes to allocate) onto the stack and calls MALLOC to do the allocation.

    And now that you understand how CALLOC works, you should understand how all of these *ALLOC macros work, since they're all the same. IALLOC multiplies its parameter by the size of a short integer (sizeof WORD) to arrive at the actual number of bytes that need to be allocated, while LALLOC multiplies its parameter by the size of a long integer (sizeof DWORD).

    (Note that, although the multiplications in these other macros are necessary, they are also inefficient. sizeof WORD == 2, so you could just do a left-shift by 1. Or, better yet, an addition of the value to itself. sizeof DWORD == 4, so that's a left-shift by 2. Adds and shifts are much faster to execute than multiplications.)