wine/dlls/ntdll/heap.c
Alexandre Julliard 410e6b7b39 Changed the init code to make libwine load only ntdll and transfer
control to it, and then have ntdll load kernel32 using the normal
loader mechanisms.
Get rid of BUILTIN32_LoadExeModule.
2003-10-10 00:12:17 +00:00

1621 lines
52 KiB
C

/*
* Win32 heap functions
*
* Copyright 1996 Alexandre Julliard
* Copyright 1998 Ulrich Weigand
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <assert.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_VALGRIND_MEMCHECK_H
#include <valgrind/memcheck.h>
#endif
#define NONAMELESSUNION
#define NONAMELESSSTRUCT
#include "ntstatus.h"
#include "windef.h"
#include "winbase.h"
#include "winreg.h"
#include "winternl.h"
#include "wine/winbase16.h"
#include "winerror.h"
#include "winnt.h"
#include "thread.h"
#include "wine/debug.h"
#include "winternl.h"
#include "wine/server_protocol.h"
WINE_DEFAULT_DEBUG_CHANNEL(heap);
/* Note: the heap data structures are based on what Pietrek describes in his
* book 'Windows 95 System Programming Secrets'. The layout is not exactly
* the same, but could be easily adapted if it turns out some programs
* require it.
*/
typedef struct tagARENA_INUSE
{
DWORD size; /* Block size; must be the first field */
DWORD magic; /* Magic number */
} ARENA_INUSE;
typedef struct tagARENA_FREE
{
DWORD size; /* Block size; must be the first field */
DWORD magic; /* Magic number */
struct tagARENA_FREE *next; /* Next free arena */
struct tagARENA_FREE *prev; /* Prev free arena */
} ARENA_FREE;
#define ARENA_FLAG_FREE 0x00000001 /* flags OR'ed with arena size */
#define ARENA_FLAG_PREV_FREE 0x00000002
#define ARENA_SIZE_MASK (~3)
#define ARENA_INUSE_MAGIC 0x44455355 /* Value for arena 'magic' field */
#define ARENA_FREE_MAGIC 0x45455246 /* Value for arena 'magic' field */
#define ARENA_INUSE_FILLER 0x55
#define ARENA_FREE_FILLER 0xaa
#define ALIGNMENT 8 /* everything is aligned on 8 byte boundaries */
#define ROUND_SIZE(size) (((size) + ALIGNMENT - 1) & ~(ALIGNMENT-1))
#define QUIET 1 /* Suppress messages */
#define NOISY 0 /* Report all errors */
#define HEAP_NB_FREE_LISTS 4 /* Number of free lists */
/* Max size of the blocks on the free lists */
static const DWORD HEAP_freeListSizes[HEAP_NB_FREE_LISTS] =
{
0x20, 0x80, 0x200, ~0UL
};
typedef struct
{
DWORD size;
ARENA_FREE arena;
} FREE_LIST_ENTRY;
struct tagHEAP;
typedef struct tagSUBHEAP
{
DWORD size; /* Size of the whole sub-heap */
DWORD commitSize; /* Committed size of the sub-heap */
DWORD headerSize; /* Size of the heap header */
struct tagSUBHEAP *next; /* Next sub-heap */
struct tagHEAP *heap; /* Main heap structure */
DWORD magic; /* Magic number */
} SUBHEAP;
#define SUBHEAP_MAGIC ((DWORD)('S' | ('U'<<8) | ('B'<<16) | ('H'<<24)))
typedef struct tagHEAP
{
SUBHEAP subheap; /* First sub-heap */
struct tagHEAP *next; /* Next heap for this process */
CRITICAL_SECTION critSection; /* Critical section for serialization */
FREE_LIST_ENTRY freeList[HEAP_NB_FREE_LISTS]; /* Free lists */
DWORD flags; /* Heap flags */
DWORD magic; /* Magic number */
} HEAP;
#define HEAP_MAGIC ((DWORD)('H' | ('E'<<8) | ('A'<<16) | ('P'<<24)))
#define HEAP_DEF_SIZE 0x110000 /* Default heap size = 1Mb + 64Kb */
#define HEAP_MIN_BLOCK_SIZE (8+sizeof(ARENA_FREE)) /* Min. heap block size */
#define COMMIT_MASK 0xffff /* bitmask for commit/decommit granularity */
static HANDLE processHeap; /* main process heap */
static HEAP *firstHeap; /* head of secondary heaps list */
static BOOL HEAP_IsRealArena( HEAP *heapPtr, DWORD flags, LPCVOID block, BOOL quiet );
/* SetLastError for ntdll */
inline static void set_status( NTSTATUS status )
{
NtCurrentTeb()->last_error = RtlNtStatusToDosError( status );
}
/* mark a block of memory as free for debugging purposes */
static inline void mark_block_free( void *ptr, size_t size )
{
if (TRACE_ON(heap)) memset( ptr, ARENA_FREE_FILLER, size );
#ifdef VALGRIND_MAKE_NOACCESS
VALGRIND_DISCARD( VALGRIND_MAKE_NOACCESS( ptr, size ));
#endif
}
/* mark a block of memory as initialized for debugging purposes */
static inline void mark_block_initialized( void *ptr, size_t size )
{
#ifdef VALGRIND_MAKE_READABLE
VALGRIND_DISCARD( VALGRIND_MAKE_READABLE( ptr, size ));
#endif
}
/* mark a block of memory as uninitialized for debugging purposes */
static inline void mark_block_uninitialized( void *ptr, size_t size )
{
#ifdef VALGRIND_MAKE_WRITABLE
VALGRIND_DISCARD( VALGRIND_MAKE_WRITABLE( ptr, size ));
#endif
if (TRACE_ON(heap))
{
memset( ptr, ARENA_INUSE_FILLER, size );
#ifdef VALGRIND_MAKE_WRITABLE
/* make it uninitialized to valgrind again */
VALGRIND_DISCARD( VALGRIND_MAKE_WRITABLE( ptr, size ));
#endif
}
}
/* clear contents of a block of memory */
static inline void clear_block( void *ptr, size_t size )
{
mark_block_initialized( ptr, size );
memset( ptr, 0, size );
}
/***********************************************************************
* HEAP_Dump
*/
void HEAP_Dump( HEAP *heap )
{
int i;
SUBHEAP *subheap;
char *ptr;
DPRINTF( "Heap: %08lx\n", (DWORD)heap );
DPRINTF( "Next: %08lx Sub-heaps: %08lx",
(DWORD)heap->next, (DWORD)&heap->subheap );
subheap = &heap->subheap;
while (subheap->next)
{
DPRINTF( " -> %08lx", (DWORD)subheap->next );
subheap = subheap->next;
}
DPRINTF( "\nFree lists:\n Block Stat Size Id\n" );
for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
DPRINTF( "%08lx free %08lx prev=%08lx next=%08lx\n",
(DWORD)&heap->freeList[i].arena, heap->freeList[i].size,
(DWORD)heap->freeList[i].arena.prev,
(DWORD)heap->freeList[i].arena.next );
subheap = &heap->subheap;
while (subheap)
{
DWORD freeSize = 0, usedSize = 0, arenaSize = subheap->headerSize;
DPRINTF( "\n\nSub-heap %08lx: size=%08lx committed=%08lx\n",
(DWORD)subheap, subheap->size, subheap->commitSize );
DPRINTF( "\n Block Stat Size Id\n" );
ptr = (char*)subheap + subheap->headerSize;
while (ptr < (char *)subheap + subheap->size)
{
if (*(DWORD *)ptr & ARENA_FLAG_FREE)
{
ARENA_FREE *pArena = (ARENA_FREE *)ptr;
DPRINTF( "%08lx free %08lx prev=%08lx next=%08lx\n",
(DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
(DWORD)pArena->prev, (DWORD)pArena->next);
ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
arenaSize += sizeof(ARENA_FREE);
freeSize += pArena->size & ARENA_SIZE_MASK;
}
else if (*(DWORD *)ptr & ARENA_FLAG_PREV_FREE)
{
ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
DPRINTF( "%08lx Used %08lx back=%08lx\n",
(DWORD)pArena, pArena->size & ARENA_SIZE_MASK, *((DWORD *)pArena - 1) );
ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
arenaSize += sizeof(ARENA_INUSE);
usedSize += pArena->size & ARENA_SIZE_MASK;
}
else
{
ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
DPRINTF( "%08lx used %08lx\n",
(DWORD)pArena, pArena->size & ARENA_SIZE_MASK );
ptr += sizeof(*pArena) + (pArena->size & ARENA_SIZE_MASK);
arenaSize += sizeof(ARENA_INUSE);
usedSize += pArena->size & ARENA_SIZE_MASK;
}
}
DPRINTF( "\nTotal: Size=%08lx Committed=%08lx Free=%08lx Used=%08lx Arenas=%08lx (%ld%%)\n\n",
subheap->size, subheap->commitSize, freeSize, usedSize,
arenaSize, (arenaSize * 100) / subheap->size );
subheap = subheap->next;
}
}
/***********************************************************************
* HEAP_GetPtr
* RETURNS
* Pointer to the heap
* NULL: Failure
*/
static HEAP *HEAP_GetPtr(
HANDLE heap /* [in] Handle to the heap */
) {
HEAP *heapPtr = (HEAP *)heap;
if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
{
ERR("Invalid heap %p!\n", heap );
return NULL;
}
if (TRACE_ON(heap) && !HEAP_IsRealArena( heapPtr, 0, NULL, NOISY ))
{
HEAP_Dump( heapPtr );
assert( FALSE );
return NULL;
}
return heapPtr;
}
/***********************************************************************
* HEAP_InsertFreeBlock
*
* Insert a free block into the free list.
*/
static inline void HEAP_InsertFreeBlock( HEAP *heap, ARENA_FREE *pArena, BOOL last )
{
FREE_LIST_ENTRY *pEntry = heap->freeList;
while (pEntry->size < pArena->size) pEntry++;
if (last)
{
/* insert at end of free list, i.e. before the next free list entry */
pEntry++;
if (pEntry == &heap->freeList[HEAP_NB_FREE_LISTS]) pEntry = heap->freeList;
pArena->prev = pEntry->arena.prev;
pArena->prev->next = pArena;
pArena->next = &pEntry->arena;
pEntry->arena.prev = pArena;
}
else
{
/* insert at head of free list */
pArena->next = pEntry->arena.next;
pArena->next->prev = pArena;
pArena->prev = &pEntry->arena;
pEntry->arena.next = pArena;
}
pArena->size |= ARENA_FLAG_FREE;
}
/***********************************************************************
* HEAP_FindSubHeap
* Find the sub-heap containing a given address.
*
* RETURNS
* Pointer: Success
* NULL: Failure
*/
static SUBHEAP *HEAP_FindSubHeap(
HEAP *heap, /* [in] Heap pointer */
LPCVOID ptr /* [in] Address */
) {
SUBHEAP *sub = &heap->subheap;
while (sub)
{
if (((char *)ptr >= (char *)sub) &&
((char *)ptr < (char *)sub + sub->size)) return sub;
sub = sub->next;
}
return NULL;
}
/***********************************************************************
* HEAP_Commit
*
* Make sure the heap storage is committed up to (not including) ptr.
*/
static inline BOOL HEAP_Commit( SUBHEAP *subheap, void *ptr )
{
DWORD size = (DWORD)((char *)ptr - (char *)subheap);
size = (size + COMMIT_MASK) & ~COMMIT_MASK;
if (size > subheap->size) size = subheap->size;
if (size <= subheap->commitSize) return TRUE;
size -= subheap->commitSize;
if (NtAllocateVirtualMemory( GetCurrentProcess(), &ptr, (char *)subheap + subheap->commitSize,
&size, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
{
WARN("Could not commit %08lx bytes at %08lx for heap %08lx\n",
size, (DWORD)((char *)subheap + subheap->commitSize),
(DWORD)subheap->heap );
return FALSE;
}
subheap->commitSize += size;
return TRUE;
}
/***********************************************************************
* HEAP_Decommit
*
* If possible, decommit the heap storage from (including) 'ptr'.
*/
static inline BOOL HEAP_Decommit( SUBHEAP *subheap, void *ptr )
{
void *addr;
ULONG decommit_size;
DWORD size = (DWORD)((char *)ptr - (char *)subheap);
/* round to next block and add one full block */
size = ((size + COMMIT_MASK) & ~COMMIT_MASK) + COMMIT_MASK + 1;
if (size >= subheap->commitSize) return TRUE;
decommit_size = subheap->commitSize - size;
addr = (char *)subheap + size;
if (NtFreeVirtualMemory( GetCurrentProcess(), &addr, &decommit_size, MEM_DECOMMIT ))
{
WARN("Could not decommit %08lx bytes at %08lx for heap %p\n",
decommit_size, (DWORD)((char *)subheap + size), subheap->heap );
return FALSE;
}
subheap->commitSize -= decommit_size;
return TRUE;
}
/***********************************************************************
* HEAP_CreateFreeBlock
*
* Create a free block at a specified address. 'size' is the size of the
* whole block, including the new arena.
*/
static void HEAP_CreateFreeBlock( SUBHEAP *subheap, void *ptr, DWORD size )
{
ARENA_FREE *pFree;
char *pEnd;
BOOL last;
/* Create a free arena */
mark_block_uninitialized( ptr, sizeof( ARENA_FREE ) );
pFree = (ARENA_FREE *)ptr;
pFree->magic = ARENA_FREE_MAGIC;
/* If debugging, erase the freed block content */
pEnd = (char *)ptr + size;
if (pEnd > (char *)subheap + subheap->commitSize) pEnd = (char *)subheap + subheap->commitSize;
if (pEnd > (char *)(pFree + 1)) mark_block_free( pFree + 1, pEnd - (char *)(pFree + 1) );
/* Check if next block is free also */
if (((char *)ptr + size < (char *)subheap + subheap->size) &&
(*(DWORD *)((char *)ptr + size) & ARENA_FLAG_FREE))
{
/* Remove the next arena from the free list */
ARENA_FREE *pNext = (ARENA_FREE *)((char *)ptr + size);
pNext->next->prev = pNext->prev;
pNext->prev->next = pNext->next;
size += (pNext->size & ARENA_SIZE_MASK) + sizeof(*pNext);
mark_block_free( pNext, sizeof(ARENA_FREE) );
}
/* Set the next block PREV_FREE flag and pointer */
last = ((char *)ptr + size >= (char *)subheap + subheap->size);
if (!last)
{
DWORD *pNext = (DWORD *)((char *)ptr + size);
*pNext |= ARENA_FLAG_PREV_FREE;
mark_block_initialized( pNext - 1, sizeof( ARENA_FREE * ) );
*(ARENA_FREE **)(pNext - 1) = pFree;
}
/* Last, insert the new block into the free list */
pFree->size = size - sizeof(*pFree);
HEAP_InsertFreeBlock( subheap->heap, pFree, last );
}
/***********************************************************************
* HEAP_MakeInUseBlockFree
*
* Turn an in-use block into a free block. Can also decommit the end of
* the heap, and possibly even free the sub-heap altogether.
*/
static void HEAP_MakeInUseBlockFree( SUBHEAP *subheap, ARENA_INUSE *pArena )
{
ARENA_FREE *pFree;
DWORD size = (pArena->size & ARENA_SIZE_MASK) + sizeof(*pArena);
/* Check if we can merge with previous block */
if (pArena->size & ARENA_FLAG_PREV_FREE)
{
pFree = *((ARENA_FREE **)pArena - 1);
size += (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
/* Remove it from the free list */
pFree->next->prev = pFree->prev;
pFree->prev->next = pFree->next;
}
else pFree = (ARENA_FREE *)pArena;
/* Create a free block */
HEAP_CreateFreeBlock( subheap, pFree, size );
size = (pFree->size & ARENA_SIZE_MASK) + sizeof(ARENA_FREE);
if ((char *)pFree + size < (char *)subheap + subheap->size)
return; /* Not the last block, so nothing more to do */
/* Free the whole sub-heap if it's empty and not the original one */
if (((char *)pFree == (char *)subheap + subheap->headerSize) &&
(subheap != &subheap->heap->subheap))
{
ULONG size = 0;
SUBHEAP *pPrev = &subheap->heap->subheap;
/* Remove the free block from the list */
pFree->next->prev = pFree->prev;
pFree->prev->next = pFree->next;
/* Remove the subheap from the list */
while (pPrev && (pPrev->next != subheap)) pPrev = pPrev->next;
if (pPrev) pPrev->next = subheap->next;
/* Free the memory */
subheap->magic = 0;
NtFreeVirtualMemory( GetCurrentProcess(), (void **)&subheap, &size, MEM_RELEASE );
return;
}
/* Decommit the end of the heap */
if (!(subheap->heap->flags & HEAP_SHARED)) HEAP_Decommit( subheap, pFree + 1 );
}
/***********************************************************************
* HEAP_ShrinkBlock
*
* Shrink an in-use block.
*/
static void HEAP_ShrinkBlock(SUBHEAP *subheap, ARENA_INUSE *pArena, DWORD size)
{
if ((pArena->size & ARENA_SIZE_MASK) >= size + HEAP_MIN_BLOCK_SIZE)
{
HEAP_CreateFreeBlock( subheap, (char *)(pArena + 1) + size,
(pArena->size & ARENA_SIZE_MASK) - size );
/* assign size plus previous arena flags */
pArena->size = size | (pArena->size & ~ARENA_SIZE_MASK);
}
else
{
/* Turn off PREV_FREE flag in next block */
char *pNext = (char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK);
if (pNext < (char *)subheap + subheap->size)
*(DWORD *)pNext &= ~ARENA_FLAG_PREV_FREE;
}
}
/***********************************************************************
* HEAP_InitSubHeap
*/
static BOOL HEAP_InitSubHeap( HEAP *heap, LPVOID address, DWORD flags,
DWORD commitSize, DWORD totalSize )
{
SUBHEAP *subheap;
FREE_LIST_ENTRY *pEntry;
int i;
/* Commit memory */
if (flags & HEAP_SHARED)
commitSize = totalSize; /* always commit everything in a shared heap */
if (NtAllocateVirtualMemory( GetCurrentProcess(), &address, address,
&commitSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
{
WARN("Could not commit %08lx bytes for sub-heap %p\n", commitSize, address );
return FALSE;
}
/* Fill the sub-heap structure */
subheap = (SUBHEAP *)address;
subheap->heap = heap;
subheap->size = totalSize;
subheap->commitSize = commitSize;
subheap->magic = SUBHEAP_MAGIC;
if ( subheap != (SUBHEAP *)heap )
{
/* If this is a secondary subheap, insert it into list */
subheap->headerSize = ROUND_SIZE( sizeof(SUBHEAP) );
subheap->next = heap->subheap.next;
heap->subheap.next = subheap;
}
else
{
/* If this is a primary subheap, initialize main heap */
subheap->headerSize = ROUND_SIZE( sizeof(HEAP) );
subheap->next = NULL;
heap->next = NULL;
heap->flags = flags;
heap->magic = HEAP_MAGIC;
/* Build the free lists */
for (i = 0, pEntry = heap->freeList; i < HEAP_NB_FREE_LISTS; i++, pEntry++)
{
pEntry->size = HEAP_freeListSizes[i];
pEntry->arena.size = 0 | ARENA_FLAG_FREE;
pEntry->arena.next = i < HEAP_NB_FREE_LISTS-1 ?
&heap->freeList[i+1].arena : &heap->freeList[0].arena;
pEntry->arena.prev = i ? &heap->freeList[i-1].arena :
&heap->freeList[HEAP_NB_FREE_LISTS-1].arena;
pEntry->arena.magic = ARENA_FREE_MAGIC;
}
/* Initialize critical section */
RtlInitializeCriticalSection( &heap->critSection );
if (flags & HEAP_SHARED)
{
/* let's assume that only one thread at a time will try to do this */
HANDLE sem = heap->critSection.LockSemaphore;
if (!sem) NtCreateSemaphore( &sem, SEMAPHORE_ALL_ACCESS, NULL, 0, 1 );
NtDuplicateObject( GetCurrentProcess(), sem, GetCurrentProcess(), &sem, 0, 0,
DUP_HANDLE_MAKE_GLOBAL | DUP_HANDLE_SAME_ACCESS | DUP_HANDLE_CLOSE_SOURCE );
heap->critSection.LockSemaphore = sem;
}
}
/* Create the first free block */
HEAP_CreateFreeBlock( subheap, (LPBYTE)subheap + subheap->headerSize,
subheap->size - subheap->headerSize );
return TRUE;
}
/***********************************************************************
* HEAP_CreateSubHeap
*
* Create a sub-heap of the given size.
* If heap == NULL, creates a main heap.
*/
static SUBHEAP *HEAP_CreateSubHeap( HEAP *heap, void *base, DWORD flags,
DWORD commitSize, DWORD totalSize )
{
LPVOID address = base;
/* round-up sizes on a 64K boundary */
totalSize = (totalSize + 0xffff) & 0xffff0000;
commitSize = (commitSize + 0xffff) & 0xffff0000;
if (!commitSize) commitSize = 0x10000;
if (totalSize < commitSize) totalSize = commitSize;
if (!address)
{
/* allocate the memory block */
if (NtAllocateVirtualMemory( GetCurrentProcess(), &address, NULL, &totalSize,
MEM_RESERVE, PAGE_EXECUTE_READWRITE ))
{
WARN("Could not allocate %08lx bytes\n", totalSize );
return NULL;
}
}
/* Initialize subheap */
if (!HEAP_InitSubHeap( heap ? heap : (HEAP *)address,
address, flags, commitSize, totalSize ))
{
ULONG size = 0;
if (!base) NtFreeVirtualMemory( GetCurrentProcess(), &address, &size, MEM_RELEASE );
return NULL;
}
return (SUBHEAP *)address;
}
/***********************************************************************
* HEAP_FindFreeBlock
*
* Find a free block at least as large as the requested size, and make sure
* the requested size is committed.
*/
static ARENA_FREE *HEAP_FindFreeBlock( HEAP *heap, DWORD size,
SUBHEAP **ppSubHeap )
{
SUBHEAP *subheap;
ARENA_FREE *pArena;
FREE_LIST_ENTRY *pEntry = heap->freeList;
/* Find a suitable free list, and in it find a block large enough */
while (pEntry->size < size) pEntry++;
pArena = pEntry->arena.next;
while (pArena != &heap->freeList[0].arena)
{
DWORD arena_size = (pArena->size & ARENA_SIZE_MASK) +
sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
if (arena_size >= size)
{
subheap = HEAP_FindSubHeap( heap, pArena );
if (!HEAP_Commit( subheap, (char *)pArena + sizeof(ARENA_INUSE)
+ size + HEAP_MIN_BLOCK_SIZE))
return NULL;
*ppSubHeap = subheap;
return pArena;
}
pArena = pArena->next;
}
/* If no block was found, attempt to grow the heap */
if (!(heap->flags & HEAP_GROWABLE))
{
WARN("Not enough space in heap %08lx for %08lx bytes\n",
(DWORD)heap, size );
return NULL;
}
/* make sure that we have a big enough size *committed* to fit another
* last free arena in !
* So just one heap struct, one first free arena which will eventually
* get inuse, and HEAP_MIN_BLOCK_SIZE for the second free arena that
* might get assigned all remaining free space in HEAP_ShrinkBlock() */
size += ROUND_SIZE(sizeof(SUBHEAP)) + sizeof(ARENA_INUSE) + HEAP_MIN_BLOCK_SIZE;
if (!(subheap = HEAP_CreateSubHeap( heap, NULL, heap->flags, size,
max( HEAP_DEF_SIZE, size ) )))
return NULL;
TRACE("created new sub-heap %08lx of %08lx bytes for heap %08lx\n",
(DWORD)subheap, size, (DWORD)heap );
*ppSubHeap = subheap;
return (ARENA_FREE *)(subheap + 1);
}
/***********************************************************************
* HEAP_IsValidArenaPtr
*
* Check that the pointer is inside the range possible for arenas.
*/
static BOOL HEAP_IsValidArenaPtr( HEAP *heap, void *ptr )
{
int i;
SUBHEAP *subheap = HEAP_FindSubHeap( heap, ptr );
if (!subheap) return FALSE;
if ((char *)ptr >= (char *)subheap + subheap->headerSize) return TRUE;
if (subheap != &heap->subheap) return FALSE;
for (i = 0; i < HEAP_NB_FREE_LISTS; i++)
if (ptr == (void *)&heap->freeList[i].arena) return TRUE;
return FALSE;
}
/***********************************************************************
* HEAP_ValidateFreeArena
*/
static BOOL HEAP_ValidateFreeArena( SUBHEAP *subheap, ARENA_FREE *pArena )
{
char *heapEnd = (char *)subheap + subheap->size;
/* Check for unaligned pointers */
if ( (long)pArena % ALIGNMENT != 0 )
{
ERR( "Heap %08lx: unaligned arena pointer %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena );
return FALSE;
}
/* Check magic number */
if (pArena->magic != ARENA_FREE_MAGIC)
{
ERR("Heap %08lx: invalid free arena magic for %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena );
return FALSE;
}
/* Check size flags */
if (!(pArena->size & ARENA_FLAG_FREE) ||
(pArena->size & ARENA_FLAG_PREV_FREE))
{
ERR("Heap %08lx: bad flags %lx for free arena %08lx\n",
(DWORD)subheap->heap, pArena->size & ~ARENA_SIZE_MASK, (DWORD)pArena );
}
/* Check arena size */
if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
{
ERR("Heap %08lx: bad size %08lx for free arena %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena->size & ARENA_SIZE_MASK, (DWORD)pArena );
return FALSE;
}
/* Check that next pointer is valid */
if (!HEAP_IsValidArenaPtr( subheap->heap, pArena->next ))
{
ERR("Heap %08lx: bad next ptr %08lx for arena %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena->next, (DWORD)pArena );
return FALSE;
}
/* Check that next arena is free */
if (!(pArena->next->size & ARENA_FLAG_FREE) ||
(pArena->next->magic != ARENA_FREE_MAGIC))
{
ERR("Heap %08lx: next arena %08lx invalid for %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena->next, (DWORD)pArena );
return FALSE;
}
/* Check that prev pointer is valid */
if (!HEAP_IsValidArenaPtr( subheap->heap, pArena->prev ))
{
ERR("Heap %08lx: bad prev ptr %08lx for arena %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena->prev, (DWORD)pArena );
return FALSE;
}
/* Check that prev arena is free */
if (!(pArena->prev->size & ARENA_FLAG_FREE) ||
(pArena->prev->magic != ARENA_FREE_MAGIC))
{
/* this often means that the prev arena got overwritten
* by a memory write before that prev arena */
ERR("Heap %08lx: prev arena %08lx invalid for %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena->prev, (DWORD)pArena );
return FALSE;
}
/* Check that next block has PREV_FREE flag */
if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd)
{
if (!(*(DWORD *)((char *)(pArena + 1) +
(pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
{
ERR("Heap %08lx: free arena %08lx next block has no PREV_FREE flag\n",
(DWORD)subheap->heap, (DWORD)pArena );
return FALSE;
}
/* Check next block back pointer */
if (*((ARENA_FREE **)((char *)(pArena + 1) +
(pArena->size & ARENA_SIZE_MASK)) - 1) != pArena)
{
ERR("Heap %08lx: arena %08lx has wrong back ptr %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena,
*((DWORD *)((char *)(pArena+1)+ (pArena->size & ARENA_SIZE_MASK)) - 1));
return FALSE;
}
}
return TRUE;
}
/***********************************************************************
* HEAP_ValidateInUseArena
*/
static BOOL HEAP_ValidateInUseArena( SUBHEAP *subheap, ARENA_INUSE *pArena, BOOL quiet )
{
char *heapEnd = (char *)subheap + subheap->size;
/* Check for unaligned pointers */
if ( (long)pArena % ALIGNMENT != 0 )
{
if ( quiet == NOISY )
{
ERR( "Heap %08lx: unaligned arena pointer %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena );
if ( TRACE_ON(heap) )
HEAP_Dump( subheap->heap );
}
else if ( WARN_ON(heap) )
{
WARN( "Heap %08lx: unaligned arena pointer %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena );
if ( TRACE_ON(heap) )
HEAP_Dump( subheap->heap );
}
return FALSE;
}
/* Check magic number */
if (pArena->magic != ARENA_INUSE_MAGIC)
{
if (quiet == NOISY) {
ERR("Heap %08lx: invalid in-use arena magic for %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena );
if (TRACE_ON(heap))
HEAP_Dump( subheap->heap );
} else if (WARN_ON(heap)) {
WARN("Heap %08lx: invalid in-use arena magic for %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena );
if (TRACE_ON(heap))
HEAP_Dump( subheap->heap );
}
return FALSE;
}
/* Check size flags */
if (pArena->size & ARENA_FLAG_FREE)
{
ERR("Heap %08lx: bad flags %lx for in-use arena %08lx\n",
(DWORD)subheap->heap, pArena->size & ~ARENA_SIZE_MASK, (DWORD)pArena );
return FALSE;
}
/* Check arena size */
if ((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) > heapEnd)
{
ERR("Heap %08lx: bad size %08lx for in-use arena %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena->size & ARENA_SIZE_MASK, (DWORD)pArena );
return FALSE;
}
/* Check next arena PREV_FREE flag */
if (((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK) < heapEnd) &&
(*(DWORD *)((char *)(pArena + 1) + (pArena->size & ARENA_SIZE_MASK)) & ARENA_FLAG_PREV_FREE))
{
ERR("Heap %08lx: in-use arena %08lx next block has PREV_FREE flag\n",
(DWORD)subheap->heap, (DWORD)pArena );
return FALSE;
}
/* Check prev free arena */
if (pArena->size & ARENA_FLAG_PREV_FREE)
{
ARENA_FREE *pPrev = *((ARENA_FREE **)pArena - 1);
/* Check prev pointer */
if (!HEAP_IsValidArenaPtr( subheap->heap, pPrev ))
{
ERR("Heap %08lx: bad back ptr %08lx for arena %08lx\n",
(DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
return FALSE;
}
/* Check that prev arena is free */
if (!(pPrev->size & ARENA_FLAG_FREE) ||
(pPrev->magic != ARENA_FREE_MAGIC))
{
ERR("Heap %08lx: prev arena %08lx invalid for in-use %08lx\n",
(DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
return FALSE;
}
/* Check that prev arena is really the previous block */
if ((char *)(pPrev + 1) + (pPrev->size & ARENA_SIZE_MASK) != (char *)pArena)
{
ERR("Heap %08lx: prev arena %08lx is not prev for in-use %08lx\n",
(DWORD)subheap->heap, (DWORD)pPrev, (DWORD)pArena );
return FALSE;
}
}
return TRUE;
}
/***********************************************************************
* HEAP_IsRealArena [Internal]
* Validates a block is a valid arena.
*
* RETURNS
* TRUE: Success
* FALSE: Failure
*/
static BOOL HEAP_IsRealArena( HEAP *heapPtr, /* [in] ptr to the heap */
DWORD flags, /* [in] Bit flags that control access during operation */
LPCVOID block, /* [in] Optional pointer to memory block to validate */
BOOL quiet ) /* [in] Flag - if true, HEAP_ValidateInUseArena
* does not complain */
{
SUBHEAP *subheap;
BOOL ret = TRUE;
if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
{
ERR("Invalid heap %p!\n", heapPtr );
return FALSE;
}
flags &= HEAP_NO_SERIALIZE;
flags |= heapPtr->flags;
/* calling HeapLock may result in infinite recursion, so do the critsect directly */
if (!(flags & HEAP_NO_SERIALIZE))
RtlEnterCriticalSection( &heapPtr->critSection );
if (block)
{
/* Only check this single memory block */
if (!(subheap = HEAP_FindSubHeap( heapPtr, block )) ||
((char *)block < (char *)subheap + subheap->headerSize
+ sizeof(ARENA_INUSE)))
{
if (quiet == NOISY)
ERR("Heap %p: block %p is not inside heap\n", heapPtr, block );
else if (WARN_ON(heap))
WARN("Heap %p: block %p is not inside heap\n", heapPtr, block );
ret = FALSE;
} else
ret = HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)block - 1, quiet );
if (!(flags & HEAP_NO_SERIALIZE))
RtlLeaveCriticalSection( &heapPtr->critSection );
return ret;
}
subheap = &heapPtr->subheap;
while (subheap && ret)
{
char *ptr = (char *)subheap + subheap->headerSize;
while (ptr < (char *)subheap + subheap->size)
{
if (*(DWORD *)ptr & ARENA_FLAG_FREE)
{
if (!HEAP_ValidateFreeArena( subheap, (ARENA_FREE *)ptr )) {
ret = FALSE;
break;
}
ptr += sizeof(ARENA_FREE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
}
else
{
if (!HEAP_ValidateInUseArena( subheap, (ARENA_INUSE *)ptr, NOISY )) {
ret = FALSE;
break;
}
ptr += sizeof(ARENA_INUSE) + (*(DWORD *)ptr & ARENA_SIZE_MASK);
}
}
subheap = subheap->next;
}
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
return ret;
}
/***********************************************************************
* RtlCreateHeap (NTDLL.@)
*
* Create a new Heap.
*
* PARAMS
* flags [I] HEAP_ flags from "winnt.h"
* addr [I] Desired base address
* totalSize [I] Total size of the heap, or 0 for a growable heap
* commitSize [I] Amount of heap space to commit
* unknown [I] Not yet understood
* definition [I] Heap definition
*
* RETURNS
* Success: A HANDLE to the newly created heap.
* Failure: a NULL HANDLE.
*/
HANDLE WINAPI RtlCreateHeap( ULONG flags, PVOID addr, ULONG totalSize, ULONG commitSize,
PVOID unknown, PRTL_HEAP_DEFINITION definition )
{
SUBHEAP *subheap;
/* Allocate the heap block */
if (!totalSize)
{
totalSize = HEAP_DEF_SIZE;
flags |= HEAP_GROWABLE;
}
if (!(subheap = HEAP_CreateSubHeap( NULL, addr, flags, commitSize, totalSize ))) return 0;
/* link it into the per-process heap list */
if (processHeap)
{
HEAP *heapPtr = subheap->heap;
RtlLockHeap( processHeap );
heapPtr->next = firstHeap;
firstHeap = heapPtr;
RtlUnlockHeap( processHeap );
}
else processHeap = subheap->heap; /* assume the first heap we create is the process main heap */
return (HANDLE)subheap;
}
/***********************************************************************
* RtlDestroyHeap (NTDLL.@)
*
* Destroy a Heap created with RtlCreateHeap().
*
* PARAMS
* heap [I] Heap to destroy.
*
* RETURNS
* Success: A NULL HANDLE, if heap is NULL or it was destroyed
* Failure: The Heap handle, if heap is the process heap.
*/
HANDLE WINAPI RtlDestroyHeap( HANDLE heap )
{
HEAP *heapPtr = HEAP_GetPtr( heap );
SUBHEAP *subheap;
TRACE("%p\n", heap );
if (!heapPtr) return heap;
if (heap == processHeap) return heap; /* cannot delete the main process heap */
else /* remove it from the per-process list */
{
HEAP **pptr;
RtlLockHeap( processHeap );
pptr = &firstHeap;
while (*pptr && *pptr != heapPtr) pptr = &(*pptr)->next;
if (*pptr) *pptr = (*pptr)->next;
RtlUnlockHeap( processHeap );
}
RtlDeleteCriticalSection( &heapPtr->critSection );
subheap = &heapPtr->subheap;
while (subheap)
{
SUBHEAP *next = subheap->next;
ULONG size = 0;
void *addr = subheap;
NtFreeVirtualMemory( GetCurrentProcess(), &addr, &size, MEM_RELEASE );
subheap = next;
}
return 0;
}
/***********************************************************************
* RtlAllocateHeap (NTDLL.@)
*
* Allocate a memory block from a Heap.
*
* PARAMS
* heap [I] Heap to allocate block from
* flags [I] HEAP_ flags from "winnt.h"
* size [I] Size of the memory block to allocate
*
* RETURNS
* Success: A pointer to the newly allocated block
* Failure: NULL.
*
* NOTES
* This call does not SetLastError().
*/
PVOID WINAPI RtlAllocateHeap( HANDLE heap, ULONG flags, ULONG size )
{
ARENA_FREE *pArena;
ARENA_INUSE *pInUse;
SUBHEAP *subheap;
HEAP *heapPtr = HEAP_GetPtr( heap );
/* Validate the parameters */
if (!heapPtr) return NULL;
flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY;
flags |= heapPtr->flags;
size = ROUND_SIZE(size);
if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
/* Locate a suitable free block */
if (!(pArena = HEAP_FindFreeBlock( heapPtr, size, &subheap )))
{
TRACE("(%p,%08lx,%08lx): returning NULL\n",
heap, flags, size );
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
return NULL;
}
/* Remove the arena from the free list */
pArena->next->prev = pArena->prev;
pArena->prev->next = pArena->next;
/* Build the in-use arena */
pInUse = (ARENA_INUSE *)pArena;
/* in-use arena is smaller than free arena,
* so we have to add the difference to the size */
pInUse->size = (pInUse->size & ~ARENA_FLAG_FREE) + sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
pInUse->magic = ARENA_INUSE_MAGIC;
/* Shrink the block */
HEAP_ShrinkBlock( subheap, pInUse, size );
if (flags & HEAP_ZERO_MEMORY)
clear_block( pInUse + 1, pInUse->size & ARENA_SIZE_MASK );
else
mark_block_uninitialized( pInUse + 1, pInUse->size & ARENA_SIZE_MASK );
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
TRACE("(%p,%08lx,%08lx): returning %08lx\n",
heap, flags, size, (DWORD)(pInUse + 1) );
return (LPVOID)(pInUse + 1);
}
/***********************************************************************
* RtlFreeHeap (NTDLL.@)
*
* Free a memory block allocated with RtlAllocateHeap().
*
* PARAMS
* heap [I] Heap that block was allocated from
* flags [I] HEAP_ flags from "winnt.h"
* ptr [I] Block to free
*
* RETURNS
* Success: TRUE, if ptr is NULL or was freed successfully.
* Failure: FALSE.
*/
BOOLEAN WINAPI RtlFreeHeap( HANDLE heap, ULONG flags, PVOID ptr )
{
ARENA_INUSE *pInUse;
SUBHEAP *subheap;
HEAP *heapPtr = HEAP_GetPtr( heap );
/* Validate the parameters */
if (!ptr) return TRUE; /* freeing a NULL ptr isn't an error in Win2k */
if (!heapPtr)
{
set_status( STATUS_INVALID_HANDLE );
return FALSE;
}
flags &= HEAP_NO_SERIALIZE;
flags |= heapPtr->flags;
if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
{
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
set_status( STATUS_INVALID_PARAMETER );
TRACE("(%p,%08lx,%08lx): returning FALSE\n",
heap, flags, (DWORD)ptr );
return FALSE;
}
/* Turn the block into a free block */
pInUse = (ARENA_INUSE *)ptr - 1;
subheap = HEAP_FindSubHeap( heapPtr, pInUse );
HEAP_MakeInUseBlockFree( subheap, pInUse );
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
TRACE("(%p,%08lx,%08lx): returning TRUE\n",
heap, flags, (DWORD)ptr );
return TRUE;
}
/***********************************************************************
* RtlReAllocateHeap (NTDLL.@)
*
* Change the size of a memory block allocated with RtlAllocateHeap().
*
* PARAMS
* heap [I] Heap that block was allocated from
* flags [I] HEAP_ flags from "winnt.h"
* ptr [I] Block to resize
* size [I] Size of the memory block to allocate
*
* RETURNS
* Success: A pointer to the resized block (which may be different).
* Failure: NULL.
*/
PVOID WINAPI RtlReAllocateHeap( HANDLE heap, ULONG flags, PVOID ptr, ULONG size )
{
ARENA_INUSE *pArena;
DWORD oldSize;
HEAP *heapPtr;
SUBHEAP *subheap;
if (!ptr) return RtlAllocateHeap( heap, flags, size ); /* FIXME: correct? */
if (!(heapPtr = HEAP_GetPtr( heap )))
{
set_status( STATUS_INVALID_HANDLE );
return FALSE;
}
/* Validate the parameters */
flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY |
HEAP_REALLOC_IN_PLACE_ONLY;
flags |= heapPtr->flags;
size = ROUND_SIZE(size);
if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
{
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
set_status( STATUS_INVALID_PARAMETER );
TRACE("(%p,%08lx,%08lx,%08lx): returning NULL\n",
heap, flags, (DWORD)ptr, size );
return NULL;
}
/* Check if we need to grow the block */
pArena = (ARENA_INUSE *)ptr - 1;
subheap = HEAP_FindSubHeap( heapPtr, pArena );
oldSize = (pArena->size & ARENA_SIZE_MASK);
if (size > oldSize)
{
char *pNext = (char *)(pArena + 1) + oldSize;
if ((pNext < (char *)subheap + subheap->size) &&
(*(DWORD *)pNext & ARENA_FLAG_FREE) &&
(oldSize + (*(DWORD *)pNext & ARENA_SIZE_MASK) + sizeof(ARENA_FREE) >= size))
{
/* The next block is free and large enough */
ARENA_FREE *pFree = (ARENA_FREE *)pNext;
pFree->next->prev = pFree->prev;
pFree->prev->next = pFree->next;
pArena->size += (pFree->size & ARENA_SIZE_MASK) + sizeof(*pFree);
if (!HEAP_Commit( subheap, (char *)pArena + sizeof(ARENA_INUSE)
+ size + HEAP_MIN_BLOCK_SIZE))
{
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
set_status( STATUS_NO_MEMORY );
return NULL;
}
HEAP_ShrinkBlock( subheap, pArena, size );
}
else /* Do it the hard way */
{
ARENA_FREE *pNew;
ARENA_INUSE *pInUse;
SUBHEAP *newsubheap;
if ((flags & HEAP_REALLOC_IN_PLACE_ONLY) ||
!(pNew = HEAP_FindFreeBlock( heapPtr, size, &newsubheap )))
{
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
if (flags & HEAP_GENERATE_EXCEPTIONS) RtlRaiseStatus( STATUS_NO_MEMORY );
set_status( STATUS_NO_MEMORY );
return NULL;
}
/* Build the in-use arena */
pNew->next->prev = pNew->prev;
pNew->prev->next = pNew->next;
pInUse = (ARENA_INUSE *)pNew;
pInUse->size = (pInUse->size & ~ARENA_FLAG_FREE)
+ sizeof(ARENA_FREE) - sizeof(ARENA_INUSE);
pInUse->magic = ARENA_INUSE_MAGIC;
HEAP_ShrinkBlock( newsubheap, pInUse, size );
mark_block_initialized( pInUse + 1, oldSize );
memcpy( pInUse + 1, pArena + 1, oldSize );
/* Free the previous block */
HEAP_MakeInUseBlockFree( subheap, pArena );
subheap = newsubheap;
pArena = pInUse;
}
}
else HEAP_ShrinkBlock( subheap, pArena, size ); /* Shrink the block */
/* Clear the extra bytes if needed */
if (size > oldSize)
{
if (flags & HEAP_ZERO_MEMORY)
clear_block( (char *)(pArena + 1) + oldSize,
(pArena->size & ARENA_SIZE_MASK) - oldSize );
else
mark_block_uninitialized( (char *)(pArena + 1) + oldSize,
(pArena->size & ARENA_SIZE_MASK) - oldSize );
}
/* Return the new arena */
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
TRACE("(%p,%08lx,%08lx,%08lx): returning %08lx\n",
heap, flags, (DWORD)ptr, size, (DWORD)(pArena + 1) );
return (LPVOID)(pArena + 1);
}
/***********************************************************************
* RtlCompactHeap (NTDLL.@)
*
* Compact the free space in a Heap.
*
* PARAMS
* heap [I] Heap that block was allocated from
* flags [I] HEAP_ flags from "winnt.h"
*
* RETURNS
* The number of bytes compacted.
*
* NOTES
* This function is a harmless stub.
*/
ULONG WINAPI RtlCompactHeap( HANDLE heap, ULONG flags )
{
FIXME( "stub\n" );
return 0;
}
/***********************************************************************
* RtlLockHeap (NTDLL.@)
*
* Lock a Heap.
*
* PARAMS
* heap [I] Heap to lock
*
* RETURNS
* Success: TRUE. The Heap is locked.
* Failure: FALSE, if heap is invalid.
*/
BOOLEAN WINAPI RtlLockHeap( HANDLE heap )
{
HEAP *heapPtr = HEAP_GetPtr( heap );
if (!heapPtr) return FALSE;
RtlEnterCriticalSection( &heapPtr->critSection );
return TRUE;
}
/***********************************************************************
* RtlUnlockHeap (NTDLL.@)
*
* Unlock a Heap.
*
* PARAMS
* heap [I] Heap to unlock
*
* RETURNS
* Success: TRUE. The Heap is unlocked.
* Failure: FALSE, if heap is invalid.
*/
BOOLEAN WINAPI RtlUnlockHeap( HANDLE heap )
{
HEAP *heapPtr = HEAP_GetPtr( heap );
if (!heapPtr) return FALSE;
RtlLeaveCriticalSection( &heapPtr->critSection );
return TRUE;
}
/***********************************************************************
* RtlSizeHeap (NTDLL.@)
*
* Get the actual size of a memory block allocated from a Heap.
*
* PARAMS
* heap [I] Heap that block was allocated from
* flags [I] HEAP_ flags from "winnt.h"
* ptr [I] Block to get the size of
*
* RETURNS
* Success: The size of the block.
* Failure: -1, heap or ptr are invalid.
*
* NOTES
* The size may be bigger than what was passed to RtlAllocateHeap().
*/
ULONG WINAPI RtlSizeHeap( HANDLE heap, ULONG flags, PVOID ptr )
{
DWORD ret;
HEAP *heapPtr = HEAP_GetPtr( heap );
if (!heapPtr)
{
set_status( STATUS_INVALID_HANDLE );
return (ULONG)-1;
}
flags &= HEAP_NO_SERIALIZE;
flags |= heapPtr->flags;
if (!(flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
{
set_status( STATUS_INVALID_PARAMETER );
ret = (ULONG)-1;
}
else
{
ARENA_INUSE *pArena = (ARENA_INUSE *)ptr - 1;
ret = pArena->size & ARENA_SIZE_MASK;
}
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
TRACE("(%p,%08lx,%08lx): returning %08lx\n",
heap, flags, (DWORD)ptr, ret );
return ret;
}
/***********************************************************************
* RtlValidateHeap (NTDLL.@)
*
* Determine if a block is a valid alloction from a heap.
*
* PARAMS
* heap [I] Heap that block was allocated from
* flags [I] HEAP_ flags from "winnt.h"
* ptr [I] Block to check
*
* RETURNS
* Success: TRUE. The block was allocated from heap.
* Failure: FALSE, if heap is invalid or ptr was not allocated from it.
*/
BOOLEAN WINAPI RtlValidateHeap( HANDLE heap, ULONG flags, LPCVOID ptr )
{
HEAP *heapPtr = HEAP_GetPtr( heap );
if (!heapPtr) return FALSE;
return HEAP_IsRealArena( heapPtr, flags, ptr, QUIET );
}
/***********************************************************************
* RtlWalkHeap (NTDLL.@)
*
* FIXME
* The PROCESS_HEAP_ENTRY flag values seem different between this
* function and HeapWalk(). To be checked.
*/
NTSTATUS WINAPI RtlWalkHeap( HANDLE heap, PVOID entry_ptr )
{
LPPROCESS_HEAP_ENTRY entry = entry_ptr; /* FIXME */
HEAP *heapPtr = HEAP_GetPtr(heap);
SUBHEAP *sub, *currentheap = NULL;
NTSTATUS ret;
char *ptr;
int region_index = 0;
FIXME( "not fully compatible\n" );
if (!heapPtr || !entry) return STATUS_INVALID_PARAMETER;
if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) RtlEnterCriticalSection( &heapPtr->critSection );
/* set ptr to the next arena to be examined */
if (!entry->lpData) /* first call (init) ? */
{
TRACE("begin walking of heap %p.\n", heap);
currentheap = &heapPtr->subheap;
ptr = (char*)currentheap + currentheap->headerSize;
}
else
{
ptr = entry->lpData;
sub = &heapPtr->subheap;
while (sub)
{
if (((char *)ptr >= (char *)sub) &&
((char *)ptr < (char *)sub + sub->size))
{
currentheap = sub;
break;
}
sub = sub->next;
region_index++;
}
if (currentheap == NULL)
{
ERR("no matching subheap found, shouldn't happen !\n");
ret = STATUS_NO_MORE_ENTRIES;
goto HW_end;
}
ptr += entry->cbData; /* point to next arena */
if (ptr > (char *)currentheap + currentheap->size - 1)
{ /* proceed with next subheap */
if (!(currentheap = currentheap->next))
{ /* successfully finished */
TRACE("end reached.\n");
ret = STATUS_NO_MORE_ENTRIES;
goto HW_end;
}
ptr = (char*)currentheap + currentheap->headerSize;
}
}
entry->wFlags = 0;
if (*(DWORD *)ptr & ARENA_FLAG_FREE)
{
ARENA_FREE *pArena = (ARENA_FREE *)ptr;
/*TRACE("free, magic: %04x\n", pArena->magic);*/
entry->lpData = pArena + 1;
entry->cbData = pArena->size & ARENA_SIZE_MASK;
entry->cbOverhead = sizeof(ARENA_FREE);
entry->wFlags = PROCESS_HEAP_UNCOMMITTED_RANGE;
}
else
{
ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
/*TRACE("busy, magic: %04x\n", pArena->magic);*/
entry->lpData = pArena + 1;
entry->cbData = pArena->size & ARENA_SIZE_MASK;
entry->cbOverhead = sizeof(ARENA_INUSE);
entry->wFlags = PROCESS_HEAP_ENTRY_BUSY;
/* FIXME: can't handle PROCESS_HEAP_ENTRY_MOVEABLE
and PROCESS_HEAP_ENTRY_DDESHARE yet */
}
entry->iRegionIndex = region_index;
/* first element of heap ? */
if (ptr == (char *)(currentheap + currentheap->headerSize))
{
entry->wFlags |= PROCESS_HEAP_REGION;
entry->u.Region.dwCommittedSize = currentheap->commitSize;
entry->u.Region.dwUnCommittedSize =
currentheap->size - currentheap->commitSize;
entry->u.Region.lpFirstBlock = /* first valid block */
currentheap + currentheap->headerSize;
entry->u.Region.lpLastBlock = /* first invalid block */
currentheap + currentheap->size;
}
ret = STATUS_SUCCESS;
HW_end:
if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
return ret;
}
/***********************************************************************
* RtlGetProcessHeaps (NTDLL.@)
*
* Get the Heaps belonging to the current process.
*
* PARAMS
* count [I] size of heaps
* heaps [O] Destination array for heap HANDLE's
*
* RETURNS
* Success: The number of Heaps allocated by the process.
* Failure: 0.
*/
ULONG WINAPI RtlGetProcessHeaps( ULONG count, HANDLE *heaps )
{
DWORD total;
HEAP *ptr;
if (!processHeap) return 0; /* should never happen */
total = 1; /* main heap */
RtlLockHeap( processHeap );
for (ptr = firstHeap; ptr; ptr = ptr->next) total++;
if (total <= count)
{
*heaps++ = (HANDLE)processHeap;
for (ptr = firstHeap; ptr; ptr = ptr->next) *heaps++ = (HANDLE)ptr;
}
RtlUnlockHeap( processHeap );
return total;
}