; a very basic implementation of malloc/realloc ; for porting fasmg to systems that do not have such API natively struct MemoryHeader size dd ? ; total size of this block, the lowest bit set for blocks in use preceding_size dd ? ; total size of the block that precedes this one in memory (zero if this is an initial block of address range) ends struct FreeMemory header MemoryHeader right dd ? ; next free block in cyclic list left dd ? ; previous free block in cyclic list ends assert defined valloc ; in: ecx = requested minimum size ; out: eax - allocated block, ecx = allocated size, zero if failed ; preserves: ebx, esi, edi ; note: ; valloc is called to request raw memory from the OS; ; it is encouraged to allocate more memory than requested (even entire available memory), ; the obtained memory is kept indefinitely in the pool for malloc ; and should be released by OS automatically when the process ends; ; if the OS does not do it automatically, additional list of the memory areas ; may need to be maintained to release them before exit malloc: malloc_fixed: malloc_growable: ; in: ecx = requested size ; out: eax - allocated block, ecx = allocated size, on error jumps to out_of_memory (does not return) ; preserves: ebx, esi, edi add ecx,sizeof.MemoryHeader-1 jc out_of_memory and ecx,(-1) shl 2 add ecx,1 shl 2 jc out_of_memory cmp ecx,sizeof.FreeMemory jae malloc_size_ok mov ecx,sizeof.FreeMemory malloc_size_ok: mov eax,[malloc_freelist] test eax,eax jz malloc_new find_fit: cmp ecx,[eax+MemoryHeader.size] jbe malloc_use_block mov eax,[eax+FreeMemory.left] cmp eax,[malloc_freelist] jne find_fit malloc_new: push ecx add ecx,sizeof.MemoryHeader jc out_of_memory call valloc test ecx,ecx jz out_of_memory xor edx,edx mov [eax+MemoryHeader.preceding_size],edx pop edx push eax sub ecx,edx cmp ecx,sizeof.FreeMemory+sizeof.MemoryHeader jb no_space_for_free_block mov [eax+MemoryHeader.size],edx add eax,edx mov [eax+MemoryHeader.preceding_size],edx mov edx,ecx sub edx,sizeof.MemoryHeader dec edx and edx,(-1) shl 2 add edx,1 shl 2 mov [eax+MemoryHeader.size],edx mov ecx,[malloc_freelist] jecxz freelist_empty mov [eax+FreeMemory.left],ecx mov edx,eax xchg edx,[ecx+FreeMemory.right] mov [eax+FreeMemory.right],edx mov [edx+FreeMemory.left],eax mov edx,[eax+MemoryHeader.size] jmp free_block_ready no_space_for_free_block: sub ecx,sizeof.MemoryHeader add edx,ecx mov [eax+MemoryHeader.size],edx jmp append_limiter freelist_empty: mov [eax+FreeMemory.left],eax mov [eax+FreeMemory.right],eax free_block_ready: mov [malloc_freelist],eax append_limiter: add eax,edx mov [eax+MemoryHeader.preceding_size],edx mov [eax+MemoryHeader.size],sizeof.MemoryHeader or 1 ; cannot be freed pop eax finish_malloc: mov ecx,[eax+MemoryHeader.size] or [eax+MemoryHeader.size],1 add eax,sizeof.MemoryHeader sub ecx,sizeof.MemoryHeader retn malloc_use_block: mov edx,[eax+MemoryHeader.size] sub edx,ecx cmp edx,sizeof.FreeMemory jb use_whole_block mov [eax+MemoryHeader.size],ecx mov [eax+ecx+MemoryHeader.preceding_size],ecx add ecx,eax mov [malloc_freelist],ecx mov [ecx+MemoryHeader.size],edx mov [ecx+edx+MemoryHeader.preceding_size],edx mov edx,[eax+FreeMemory.right] cmp edx,eax je update_free_singleton mov [ecx+FreeMemory.right],edx mov [edx+FreeMemory.left],ecx mov edx,[eax+FreeMemory.left] mov [ecx+FreeMemory.left],edx mov [edx+FreeMemory.right],ecx jmp finish_malloc update_free_singleton: mov [ecx+FreeMemory.left],ecx mov [ecx+FreeMemory.right],ecx jmp finish_malloc use_whole_block: mov edx,[eax+FreeMemory.right] cmp edx,eax je depleted_freelist mov ecx,[eax+FreeMemory.left] mov [ecx+FreeMemory.right],edx mov [edx+FreeMemory.left],ecx mov [malloc_freelist],edx jmp finish_malloc depleted_freelist: and [malloc_freelist],0 jmp finish_malloc mfree: ; in: eax - memory block ; out: cf set on error ; preserves: ebx, esi, edi ; note: eax may have value 0 or -1, it should be treated as invalid input then test eax,eax jz mfree_error cmp eax,-1 je mfree_error sub eax,sizeof.MemoryHeader mov ecx,[eax+MemoryHeader.size] btr ecx,0 jnc mfree_error cmp ecx,sizeof.FreeMemory jb mfree_error cmp [eax+ecx+MemoryHeader.preceding_size],ecx jne mfree_error mov [eax+MemoryHeader.size],ecx mov edx,eax sub edx,[eax+MemoryHeader.preceding_size] cmp edx,eax je no_preceding_block test [edx+MemoryHeader.size],1 jz coalesce_with_preceding_block no_preceding_block: test [eax+ecx+MemoryHeader.size],1 jz coalesce_with_following_block mov ecx,[malloc_freelist] jecxz mfree_init_freelist mov edx,[ecx+FreeMemory.right] mov [eax+FreeMemory.left],ecx mov [edx+FreeMemory.left],eax mov [eax+FreeMemory.right],edx mov [ecx+FreeMemory.right],eax mfree_ok: mov [malloc_freelist],eax clc retn mfree_init_freelist: mov [eax+FreeMemory.left],eax mov [eax+FreeMemory.right],eax jmp mfree_ok mfree_error: stc retn coalesce_with_preceding_block: add ecx,[edx+MemoryHeader.size] test [edx+ecx+MemoryHeader.size],1 jz coalesce_on_both_ends mov [edx+MemoryHeader.size],ecx mov [edx+ecx+MemoryHeader.preceding_size],ecx clc retn coalesce_on_both_ends: lea eax,[edx+ecx] add ecx,[eax+MemoryHeader.size] mov [edx+MemoryHeader.size],ecx mov [edx+ecx+MemoryHeader.preceding_size],ecx mov [malloc_freelist],edx mov ecx,[eax+FreeMemory.left] mov edx,[eax+FreeMemory.right] mov [edx+FreeMemory.left],ecx mov [ecx+FreeMemory.right],edx clc retn coalesce_with_following_block: push ebx lea ebx,[eax+ecx] add ecx,[ebx+MemoryHeader.size] mov [eax+MemoryHeader.size],ecx mov [eax+ecx+MemoryHeader.preceding_size],ecx mov ecx,[ebx+FreeMemory.left] mov edx,[ebx+FreeMemory.right] mov [ecx+FreeMemory.right],eax mov [edx+FreeMemory.left],eax mov ecx,[ebx+FreeMemory.left] mov edx,[ebx+FreeMemory.right] mov [eax+FreeMemory.left],ecx mov [eax+FreeMemory.right],edx pop ebx jmp mfree_ok realloc: ; in: eax - memory block, ecx = requested size ; out: eax - resized block, ecx = allocated size, on error jumps to out_of_memory (does not return) ; preserves: ebx, esi, edi add ecx,sizeof.MemoryHeader-1 jc out_of_memory and ecx,(-1) shl 2 add ecx,1 shl 2 jc out_of_memory sub eax,sizeof.MemoryHeader mov edx,[eax+MemoryHeader.size] and edx,not 1 cmp ecx,edx jbe realloc_retain test [eax+edx+MemoryHeader.size],1 jnz realloc_and_copy add edx,[eax+edx+MemoryHeader.size] cmp ecx,edx ja realloc_and_copy sub edx,ecx cmp edx,sizeof.FreeMemory jb append_whole_block push esi edi push ecx edx lea edi,[eax+ecx] xchg ecx,[eax+MemoryHeader.size] and ecx,not 1 lea esi,[eax+ecx] mov ecx,[esi+FreeMemory.left] mov edx,[esi+FreeMemory.right] mov [edx+FreeMemory.left],edi mov [ecx+FreeMemory.right],edi mov ecx,[esi+FreeMemory.left] mov edx,[esi+FreeMemory.right] mov [edi+FreeMemory.left],ecx mov [edi+FreeMemory.right],edx mov [malloc_freelist],edi pop edx ecx mov [edi+MemoryHeader.size],edx mov [edi+edx+MemoryHeader.preceding_size],edx mov [edi+MemoryHeader.preceding_size],ecx pop edi esi jmp finish_malloc append_whole_block: add edx,ecx mov [eax+edx+MemoryHeader.preceding_size],edx xchg edx,[eax+MemoryHeader.size] and edx,not 1 add edx,eax mov ecx,[edx+FreeMemory.left] cmp ecx,edx je depleted_freelist mov edx,[edx+FreeMemory.right] mov [ecx+FreeMemory.right],edx mov [edx+FreeMemory.left],ecx mov [malloc_freelist],ecx jmp finish_malloc realloc_retain: and [eax+MemoryHeader.size],not 1 jmp finish_malloc realloc_and_copy: push esi edi lea esi,[eax+sizeof.MemoryHeader] call malloc_growable push eax ecx mov edi,eax mov eax,esi mov ecx,[esi-sizeof.MemoryHeader+MemoryHeader.size] shr ecx,2 rep movsd call mfree pop ecx eax pop edi esi retn if used mcheck mcheck: pushf push eax ebx ecx edx mov eax,[malloc_freelist] test eax,eax jz integrity_verified verify_freelist: mov ebx,eax verify_preceding_blocks: mov ecx,[ebx+MemoryHeader.preceding_size] jecxz preceding_blocks_ok sub ebx,ecx mov edx,[ebx+MemoryHeader.size] and edx,not 1 cmp ecx,edx je verify_preceding_blocks jmp internal_error preceding_blocks_ok: mov ebx,eax verify_following_blocks: mov ecx,[ebx+MemoryHeader.size] and ecx,not 1 cmp ecx,sizeof.MemoryHeader je following_blocks_ok add ebx,ecx cmp ecx,[ebx+MemoryHeader.preceding_size] je verify_following_blocks jmp internal_error following_blocks_ok: mov edx,[eax+FreeMemory.right] cmp eax,[edx+FreeMemory.left] je verify_next jmp internal_error verify_next: mov eax,edx cmp eax,[malloc_freelist] jne verify_freelist integrity_verified: pop edx ecx ebx eax popf retn end if