Commit b0f58617 authored by Alexandre Julliard's avatar Alexandre Julliard

Moved heap functions to ntdll.

Got rid of internal heap flags. Reimplemented MapLS to not depend on the segptr heap.
parent 024d39fc
......@@ -500,15 +500,15 @@ debug_channels (comm debugstr dll int resource stress thunk toolhelp win32)
@ stub Heap32ListFirst
@ stub Heap32ListNext
@ stub Heap32Next
@ stdcall HeapAlloc(long long long) HeapAlloc
@ forward HeapAlloc ntdll.RtlAllocateHeap
@ stdcall HeapCompact(long long) HeapCompact
@ stdcall HeapCreate(long long long) HeapCreate
@ stdcall HeapDestroy(long) HeapDestroy
@ stdcall HeapFree(long long ptr) HeapFree
@ forward HeapFree ntdll.RtlFreeHeap
@ stdcall HeapLock(long) HeapLock
@ stdcall HeapReAlloc(long long ptr long) HeapReAlloc
@ forward HeapReAlloc ntdll.RtlReAllocateHeap
@ stub HeapSetFlags
@ stdcall HeapSize(long long ptr) HeapSize
@ forward HeapSize ntdll.RtlSizeHeap
@ stdcall HeapUnlock(long) HeapUnlock
@ stdcall HeapValidate(long long ptr) HeapValidate
@ stdcall HeapWalk(long ptr) HeapWalk
......
......@@ -11,6 +11,7 @@ C_SRCS = \
exception.c \
error.c \
file.c \
heap.c \
large_int.c \
misc.c \
nt.c \
......
......@@ -14,6 +14,7 @@
#include "thread.h"
#include "winbase.h"
#include "winnt.h"
#include "ntddk.h"
#include "wtypes.h"
DECLARE_DEBUG_CHANNEL(tid);
......@@ -50,9 +51,9 @@ static inline struct debug_info *get_info(void)
tmp.out_pos = tmp.output;
}
if (!GetProcessHeap()) return &tmp;
/* setup the temp structure in case HeapAlloc wants to print something */
/* setup the temp structure in case RtlAllocateHeap wants to print something */
NtCurrentTeb()->debug_info = &tmp;
info = HeapAlloc( GetProcessHeap(), 0, sizeof(*info) );
info = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*info) );
info->str_pos = info->strings;
info->out_pos = info->output;
NtCurrentTeb()->debug_info = info;
......
/*
* Win32 heap functions
*
* Copyright 1996 Alexandre Julliard
* Copyright 1998 Ulrich Weigand
*/
#include "config.h"
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "ntddk.h"
#include "wine/winbase16.h"
#include "winbase.h"
#include "winerror.h"
#include "winnt.h"
#include "heap.h"
#include "thread.h"
#include "debugtools.h"
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 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 )
{
#if defined(__i386__) && defined(__GNUC__)
/* in this case SetLastError is an inline function so we can use it */
SetLastError( RtlNtStatusToDosError( status ) );
#else
/* cannot use SetLastError, do it manually */
NtCurrentTeb()->last_error = RtlNtStatusToDosError( status );
#endif
}
/* set the process main heap */
static void set_process_heap( HANDLE heap )
{
HANDLE *pdb = (HANDLE *)NtCurrentTeb()->process;
pdb[0x18 / sizeof(HANDLE)] = heap; /* heap is at offset 0x18 in pdb */
processHeap = heap;
}
/***********************************************************************
* 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].arena.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 %08x!\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 void HEAP_InsertFreeBlock( HEAP *heap, ARENA_FREE *pArena )
{
FREE_LIST_ENTRY *pEntry = heap->freeList;
while (pEntry->size < pArena->size) pEntry++;
pArena->size |= ARENA_FLAG_FREE;
pArena->next = pEntry->arena.next;
pArena->next->prev = pArena;
pArena->prev = &pEntry->arena;
pEntry->arena.next = pArena;
}
/***********************************************************************
* 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;
if (!VirtualAlloc( (char *)subheap + subheap->commitSize,
size - subheap->commitSize, MEM_COMMIT,
PAGE_EXECUTE_READWRITE))
{
WARN("Could not commit %08lx bytes at %08lx for heap %08lx\n",
size - subheap->commitSize,
(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 )
{
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;
if (!VirtualFree( (char *)subheap + size,
subheap->commitSize - size, MEM_DECOMMIT ))
{
WARN("Could not decommit %08lx bytes at %08lx for heap %08lx\n",
subheap->commitSize - size,
(DWORD)((char *)subheap + size),
(DWORD)subheap->heap );
return FALSE;
}
subheap->commitSize = 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;
/* Create a free arena */
pFree = (ARENA_FREE *)ptr;
pFree->magic = ARENA_FREE_MAGIC;
/* If debugging, erase the freed block content */
if (TRACE_ON(heap))
{
char *pEnd = (char *)ptr + size;
if (pEnd > (char *)subheap + subheap->commitSize)
pEnd = (char *)subheap + subheap->commitSize;
if (pEnd > (char *)(pFree + 1))
memset( pFree + 1, ARENA_FREE_FILLER, 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);
if (TRACE_ON(heap))
memset( pNext, ARENA_FREE_FILLER, sizeof(ARENA_FREE) );
}
/* Set the next block PREV_FREE flag and pointer */
if ((char *)ptr + size < (char *)subheap + subheap->size)
{
DWORD *pNext = (DWORD *)((char *)ptr + size);
*pNext |= ARENA_FLAG_PREV_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 );
}
/***********************************************************************
* 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))
{
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;
VirtualFree( subheap, 0, 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 = (SUBHEAP *)address;
FREE_LIST_ENTRY *pEntry;
int i;
/* Commit memory */
if (flags & HEAP_SHARED)
commitSize = totalSize; /* always commit everything in a shared heap */
if (!VirtualAlloc(address, commitSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
{
WARN("Could not commit %08lx bytes for sub-heap %08lx\n",
commitSize, (DWORD)address );
return FALSE;
}
/* Fill the sub-heap structure */
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 = sizeof(SUBHEAP);
subheap->next = heap->subheap.next;
heap->subheap.next = subheap;
}
else
{
/* If this is a primary subheap, initialize main heap */
subheap->headerSize = 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 );
}
/* 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;
if (!address)
{
/* round-up sizes on a 64K boundary */
totalSize = (totalSize + 0xffff) & 0xffff0000;
commitSize = (commitSize + 0xffff) & 0xffff0000;
if (!commitSize) commitSize = 0x10000;
if (totalSize < commitSize) totalSize = commitSize;
/* allocate the memory block */
if (!(address = VirtualAlloc( NULL, totalSize, MEM_RESERVE, PAGE_EXECUTE_READWRITE )))
{
WARN("Could not VirtualAlloc %08lx bytes\n",
totalSize );
return NULL;
}
}
/* Initialize subheap */
if (!HEAP_InitSubHeap( heap ? heap : (HEAP *)address,
address, flags, commitSize, totalSize ))
{
if (!base) VirtualFree( address, 0, 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 += 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;
#if !defined(ALLOW_UNALIGNED_ACCESS)
/* Check for unaligned pointers */
if ( (long)pArena % sizeof(void *) != 0 )
{
ERR( "Heap %08lx: unaligned arena pointer %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena );
return FALSE;
}
#endif
/* 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;
#if !defined(ALLOW_UNALIGNED_ACCESS)
/* Check for unaligned pointers */
if ( (long)pArena % sizeof(void *) != 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;
}
#endif
/* 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 );
}
/* 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.@)
*/
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;
}
/* round up sizes */
totalSize = (totalSize + 0xffff) & 0xffff0000;
commitSize = (commitSize + 0xffff) & 0xffff0000;
if (!commitSize) commitSize = 0x10000;
if (totalSize < commitSize) totalSize = commitSize;
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 /* assume the first heap we create is the process main heap */
{
set_process_heap( (HANDLE)subheap->heap );
}
return (HANDLE)subheap;
}
/***********************************************************************
* RtlDestroyHeap (NTDLL.@)
*/
HANDLE WINAPI RtlDestroyHeap( HANDLE heap )
{
HEAP *heapPtr = HEAP_GetPtr( heap );
SUBHEAP *subheap;
TRACE("%08x\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;
VirtualFree( subheap, 0, MEM_RELEASE );
subheap = next;
}
return 0;
}
/***********************************************************************
* RtlAllocateHeap (NTDLL.@)
*
* NOTE: does not set last error.
*/
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 = (size + 3) & ~3;
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("(%08x,%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)
memset( pInUse + 1, 0, pInUse->size & ARENA_SIZE_MASK );
else if (TRACE_ON(heap))
memset( pInUse + 1, ARENA_INUSE_FILLER, pInUse->size & ARENA_SIZE_MASK );
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
TRACE("(%08x,%08lx,%08lx): returning %08lx\n",
heap, flags, size, (DWORD)(pInUse + 1) );
return (LPVOID)(pInUse + 1);
}
/***********************************************************************
* RtlFreeHeap (NTDLL.@)
*/
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("(%08x,%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("(%08x,%08lx,%08lx): returning TRUE\n",
heap, flags, (DWORD)ptr );
return TRUE;
}
/***********************************************************************
* RtlReAllocateHeap (NTDLL.@)
*/
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 = (size + 3) & ~3;
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("(%08x,%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 );
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)
memset( (char *)(pArena + 1) + oldSize, 0,
(pArena->size & ARENA_SIZE_MASK) - oldSize );
else if (TRACE_ON(heap))
memset( (char *)(pArena + 1) + oldSize, ARENA_INUSE_FILLER,
(pArena->size & ARENA_SIZE_MASK) - oldSize );
}
/* Return the new arena */
if (!(flags & HEAP_NO_SERIALIZE)) RtlLeaveCriticalSection( &heapPtr->critSection );
TRACE("(%08x,%08lx,%08lx,%08lx): returning %08lx\n",
heap, flags, (DWORD)ptr, size, (DWORD)(pArena + 1) );
return (LPVOID)(pArena + 1);
}
/***********************************************************************
* RtlCompactHeap (NTDLL.@)
*/
ULONG WINAPI RtlCompactHeap( HANDLE heap, ULONG flags )
{
FIXME( "stub\n" );
return 0;
}
/***********************************************************************
* RtlLockHeap (NTDLL.@)
*/
BOOLEAN WINAPI RtlLockHeap( HANDLE heap )
{
HEAP *heapPtr = HEAP_GetPtr( heap );
if (!heapPtr) return FALSE;
RtlEnterCriticalSection( &heapPtr->critSection );
return TRUE;
}
/***********************************************************************
* RtlUnlockHeap (NTDLL.@)
*/
BOOLEAN WINAPI RtlUnlockHeap( HANDLE heap )
{
HEAP *heapPtr = HEAP_GetPtr( heap );
if (!heapPtr) return FALSE;
RtlLeaveCriticalSection( &heapPtr->critSection );
return TRUE;
}
/***********************************************************************
* RtlSizeHeap (NTDLL.@)
*/
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("(%08x,%08lx,%08lx): returning %08lx\n",
heap, flags, (DWORD)ptr, ret );
return ret;
}
/***********************************************************************
* RtlValidateHeap (NTDLL.@)
*/
BOOLEAN WINAPI RtlValidateHeap( HANDLE heap, ULONG flags, PCVOID block )
{
HEAP *heapPtr = HEAP_GetPtr( heap );
if (!heapPtr) return FALSE;
return HEAP_IsRealArena( heapPtr, flags, block, 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)) EnterCriticalSection( &heapPtr->critSection );
/* set ptr to the next arena to be examined */
if (!entry->lpData) /* first call (init) ? */
{
TRACE("begin walking of heap 0x%08x.\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)) LeaveCriticalSection( &heapPtr->critSection );
return ret;
}
/***********************************************************************
* RtlGetProcessHeaps (NTDLL.@)
*/
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;
}
......@@ -300,7 +300,7 @@ debug_channels (atom cdrom console debug delayhlp dll dosfs dosmem file fixup
@ stub RtlCheckRegistryKey
@ stub RtlClearAllBits
@ stdcall RtlClearBits(long long long) RtlClearBits
@ stub RtlCompactHeap
@ stdcall RtlCompactHeap(long long) RtlCompactHeap
@ stdcall RtlCompareMemory(ptr ptr long) RtlCompareMemory
@ stub RtlCompareMemoryUlong
@ stdcall RtlCompareString(ptr ptr long) RtlCompareString
......@@ -406,7 +406,7 @@ debug_channels (atom cdrom console debug delayhlp dll dosfs dosmem file fixup
@ stub RtlGetNtGlobalFlags
@ stdcall RtlGetNtProductType(ptr) RtlGetNtProductType
@ stdcall RtlGetOwnerSecurityDescriptor(ptr ptr ptr) RtlGetOwnerSecurityDescriptor
@ stub RtlGetProcessHeaps
@ stdcall RtlGetProcessHeaps(long ptr) RtlGetProcessHeaps
@ stdcall RtlGetSaclSecurityDescriptor(ptr ptr ptr ptr)RtlGetSaclSecurityDescriptor
@ stub RtlGetUserInfoHeap
@ stdcall RtlIdentifierAuthoritySid(ptr) RtlIdentifierAuthoritySid
......@@ -446,7 +446,7 @@ debug_channels (atom cdrom console debug delayhlp dll dosfs dosmem file fixup
@ stdcall RtlLengthSecurityDescriptor(ptr) RtlLengthSecurityDescriptor
@ stdcall RtlLengthSid(ptr) RtlLengthSid
@ stub RtlLocalTimeToSystemTime
@ stub RtlLockHeap
@ stdcall RtlLockHeap(long) RtlLockHeap
@ stub RtlLookupElementGenericTable
@ stdcall RtlMakeSelfRelativeSD(ptr ptr ptr) RtlMakeSelfRelativeSD
@ stub RtlMapGenericMask
......@@ -482,7 +482,7 @@ debug_channels (atom cdrom console debug delayhlp dll dosfs dosmem file fixup
@ stdcall RtlRaiseException(ptr) RtlRaiseException
@ stdcall RtlRaiseStatus(long) RtlRaiseStatus
@ stub RtlRandom
@ stub RtlReAllocateHeap
@ stdcall RtlReAllocateHeap(long long ptr long) RtlReAllocateHeap
@ stub RtlRealPredecessor
@ stub RtlRealSuccessor
@ stdcall RtlReleasePebLock() RtlReleasePebLock
......@@ -508,7 +508,7 @@ debug_channels (atom cdrom console debug delayhlp dll dosfs dosmem file fixup
@ stub RtlSetTimeZoneInformation
@ stub RtlSetUserFlagsHeap
@ stub RtlSetUserValueHeap
@ stdcall RtlSizeHeap(long long long) HeapSize
@ stdcall RtlSizeHeap(long long ptr) RtlSizeHeap
@ stub RtlSplay
@ stub RtlStartRXact
@ stdcall RtlSubAuthorityCountSid(ptr) RtlSubAuthorityCountSid
......@@ -532,7 +532,7 @@ debug_channels (atom cdrom console debug delayhlp dll dosfs dosmem file fixup
@ stdcall RtlUnicodeToMultiByteSize(ptr wstr long) RtlUnicodeToMultiByteSize
@ stdcall RtlUnicodeToOemN(ptr long ptr ptr long) RtlUnicodeToOemN
@ stub RtlUniform
@ stub RtlUnlockHeap
@ stdcall RtlUnlockHeap(long) RtlUnlockHeap
@ stdcall RtlUnwind(ptr ptr ptr long) RtlUnwind
@ stub RtlUpcaseUnicodeChar
@ stdcall RtlUpcaseUnicodeString(ptr ptr long) RtlUpcaseUnicodeString
......@@ -548,9 +548,9 @@ debug_channels (atom cdrom console debug delayhlp dll dosfs dosmem file fixup
@ stub RtlValidAcl
@ stdcall RtlValidSecurityDescriptor(ptr) RtlValidSecurityDescriptor
@ stdcall RtlValidSid(ptr) RtlValidSid
@ stub RtlValidateHeap
@ stdcall RtlValidateHeap(long long ptr) RtlValidateHeap
@ stub RtlValidateProcessHeaps
@ stub RtlWalkHeap
@ stdcall RtlWalkHeap(long ptr) RtlWalkHeap
@ stub RtlWriteRegistryValue
@ stub RtlZeroHeap
@ stdcall RtlZeroMemory(ptr long) RtlZeroMemory
......
......@@ -213,64 +213,6 @@ void WINAPI RtlDumpResource(LPRTL_RWLOCK rwl)
}
/*
* heap functions
*/
/******************************************************************************
* RtlCreateHeap [NTDLL.@]
*/
HANDLE WINAPI RtlCreateHeap(
ULONG Flags,
PVOID BaseAddress,
ULONG SizeToReserve,
ULONG SizeToCommit,
PVOID Unknown,
PRTL_HEAP_DEFINITION Definition)
{
FIXME("(0x%08lx, %p, 0x%08lx, 0x%08lx, %p, %p) semi-stub\n",
Flags, BaseAddress, SizeToReserve, SizeToCommit, Unknown, Definition);
return HeapCreate ( Flags, SizeToCommit, SizeToReserve);
}
/******************************************************************************
* RtlAllocateHeap [NTDLL.@]
*/
PVOID WINAPI RtlAllocateHeap(
HANDLE Heap,
ULONG Flags,
ULONG Size)
{
TRACE("(0x%08x, 0x%08lx, 0x%08lx) semi stub\n",
Heap, Flags, Size);
return HeapAlloc(Heap, Flags, Size);
}
/******************************************************************************
* RtlFreeHeap [NTDLL.@]
*/
BOOLEAN WINAPI RtlFreeHeap(
HANDLE Heap,
ULONG Flags,
PVOID Address)
{
TRACE("(0x%08x, 0x%08lx, %p) semi stub\n",
Heap, Flags, Address);
return HeapFree(Heap, Flags, Address);
}
/******************************************************************************
* RtlDestroyHeap [NTDLL.@]
*/
HANDLE WINAPI RtlDestroyHeap(
HANDLE Heap)
{
TRACE("(0x%08x) semi stub\n", Heap);
if (!HeapDestroy(Heap)) return Heap;
return 0;
}
/*
* misc functions
*/
......
......@@ -143,25 +143,32 @@ ULONG WINAPI IMalloc16_fnRelease(IMalloc16* iface) {
SEGPTR WINAPI IMalloc16_fnAlloc(IMalloc16* iface,DWORD cb) {
ICOM_THIS(IMalloc16Impl,iface);
TRACE("(%p)->Alloc(%ld)\n",This,cb);
return MapLS( HeapAlloc( GetProcessHeap(), HEAP_WINE_SEGPTR, cb ) );
return MapLS( HeapAlloc( GetProcessHeap(), 0, cb ) );
}
/******************************************************************************
* IMalloc16_Realloc [COMPOBJ.504]
*/
SEGPTR WINAPI IMalloc16_fnRealloc(IMalloc16* iface,SEGPTR pv,DWORD cb) {
ICOM_THIS(IMalloc16Impl,iface);
TRACE("(%p)->Realloc(%08lx,%ld)\n",This,pv,cb);
return MapLS( HeapReAlloc( GetProcessHeap(), HEAP_WINE_SEGPTR, MapSL(pv), cb ) );
SEGPTR WINAPI IMalloc16_fnRealloc(IMalloc16* iface,SEGPTR pv,DWORD cb)
{
SEGPTR ret;
ICOM_THIS(IMalloc16Impl,iface);
TRACE("(%p)->Realloc(%08lx,%ld)\n",This,pv,cb);
ret = MapLS( HeapReAlloc( GetProcessHeap(), 0, MapSL(pv), cb ) );
UnMapLS(pv);
return ret;
}
/******************************************************************************
* IMalloc16_Free [COMPOBJ.505]
*/
VOID WINAPI IMalloc16_fnFree(IMalloc16* iface,SEGPTR pv) {
ICOM_THIS(IMalloc16Impl,iface);
TRACE("(%p)->Free(%08lx)\n",This,pv);
HeapFree( GetProcessHeap(), HEAP_WINE_SEGPTR, MapSL(pv) );
VOID WINAPI IMalloc16_fnFree(IMalloc16* iface,SEGPTR pv)
{
void *ptr = MapSL(pv);
ICOM_THIS(IMalloc16Impl,iface);
TRACE("(%p)->Free(%08lx)\n",This,pv);
UnMapLS(pv);
HeapFree( GetProcessHeap(), 0, ptr );
}
/******************************************************************************
......@@ -171,7 +178,7 @@ DWORD WINAPI IMalloc16_fnGetSize(const IMalloc16* iface,SEGPTR pv)
{
ICOM_CTHIS(IMalloc16Impl,iface);
TRACE("(%p)->GetSize(%08lx)\n",This,pv);
return HeapSize( GetProcessHeap(), HEAP_WINE_SEGPTR, MapSL(pv) );
return HeapSize( GetProcessHeap(), 0, MapSL(pv) );
}
/******************************************************************************
......
......@@ -95,7 +95,7 @@ HFONT WIN16DRV_FONT_SelectObject( DC * dc, HFONT hfont)
&physDev->lf, 0, 0);
if( physDev->FontInfo &&
HeapSize( GetProcessHeap(), HEAP_WINE_SEGPTR, physDev->FontInfo ) < nSize )
HeapSize( GetProcessHeap(), 0, physDev->FontInfo ) < nSize )
{
SEGPTR_FREE( physDev->FontInfo );
physDev->FontInfo = NULL;
......
......@@ -14,18 +14,16 @@
#include "wine/unicode.h"
#include "wine/windef16.h" /* for SEGPTR */
extern SEGPTR HEAP_GetSegptr( HANDLE heap, DWORD flags, LPCVOID ptr );
/* SEGPTR helper macros */
#define SEGPTR_ALLOC(size) \
(HeapAlloc( GetProcessHeap(), HEAP_WINE_SEGPTR, (size) ))
(HeapAlloc( GetProcessHeap(), 0, (size) ))
#define SEGPTR_NEW(type) \
((type *)HeapAlloc( GetProcessHeap(), HEAP_WINE_SEGPTR, sizeof(type) ))
((type *)HeapAlloc( GetProcessHeap(), 0, sizeof(type) ))
#define SEGPTR_STRDUP_WtoA(str) \
(HIWORD(str) ? HEAP_strdupWtoA( GetProcessHeap(), HEAP_WINE_SEGPTR, (str) ) : (LPSTR)(str))
(HIWORD(str) ? HEAP_strdupWtoA( GetProcessHeap(), 0, (str) ) : (LPSTR)(str))
#define SEGPTR_FREE(ptr) \
(HIWORD(ptr) ? HeapFree( GetProcessHeap(), HEAP_WINE_SEGPTR, (ptr) ) : 0)
(HIWORD(ptr) ? HeapFree( GetProcessHeap(), 0, (ptr) ) : 0)
#define SEGPTR_GET(ptr) MapLS(ptr)
inline static LPSTR SEGPTR_STRDUP( LPCSTR str )
......@@ -33,7 +31,7 @@ inline static LPSTR SEGPTR_STRDUP( LPCSTR str )
if (HIWORD(str))
{
INT len = strlen(str) + 1;
LPSTR p = HeapAlloc( GetProcessHeap(), HEAP_WINE_SEGPTR, len );
LPSTR p = HeapAlloc( GetProcessHeap(), 0, len );
if (p) memcpy( p, str, len );
return p;
}
......
......@@ -723,6 +723,7 @@ BOOLEAN WINAPI RtlUnlockHeap(HANDLE);
ULONG WINAPI RtlSizeHeap(HANDLE,ULONG,PVOID);
BOOLEAN WINAPI RtlValidateHeap(HANDLE,ULONG,PCVOID);
ULONG WINAPI RtlGetProcessHeaps(ULONG,HANDLE*);
NTSTATUS WINAPI RtlWalkHeap(HANDLE,PVOID);
/* exception */
......
......@@ -985,20 +985,33 @@ BOOL WINAPI GetVersionExW(OSVERSIONINFOW*);
/*int WinMain(HINSTANCE, HINSTANCE prev, char *cmd, int show);*/
LONG WINAPI RtlEnterCriticalSection( CRITICAL_SECTION *crit );
LONG WINAPI RtlLeaveCriticalSection( CRITICAL_SECTION *crit );
LONG WINAPI RtlDeleteCriticalSection( CRITICAL_SECTION *crit );
BOOL WINAPI RtlTryEnterCriticalSection( CRITICAL_SECTION *crit );
/* FIXME: need to use defines because we don't have proper imports yet */
/* FIXME: need to use defines because we don't have proper imports everywhere yet */
#ifndef have_proper_imports
LONG WINAPI RtlEnterCriticalSection( CRITICAL_SECTION *crit );
LONG WINAPI RtlLeaveCriticalSection( CRITICAL_SECTION *crit );
LONG WINAPI RtlDeleteCriticalSection( CRITICAL_SECTION *crit );
BOOL WINAPI RtlTryEnterCriticalSection( CRITICAL_SECTION *crit );
PVOID WINAPI RtlAllocateHeap(HANDLE,ULONG,ULONG);
BOOLEAN WINAPI RtlFreeHeap(HANDLE,ULONG,PVOID);
PVOID WINAPI RtlReAllocateHeap(HANDLE,ULONG,PVOID,ULONG);
ULONG WINAPI RtlSizeHeap(HANDLE,ULONG,PVOID);
#define HeapAlloc(heap,flags,size) RtlAllocateHeap(heap,flags,size)
#define HeapFree(heap,flags,ptr) RtlFreeHeap(heap,flags,ptr)
#define HeapReAlloc(heap,flags,ptr,size) RtlReAllocateHeap(heap,flags,ptr,size)
#define HeapSize(heap,flags,ptr) RtlSizeHeap(heap,flags,ptr)
#define EnterCriticalSection(crit) RtlEnterCriticalSection(crit)
#define LeaveCriticalSection(crit) RtlLeaveCriticalSection(crit)
#define DeleteCriticalSection(crit) RtlDeleteCriticalSection(crit)
#define TryEnterCriticalSection(crit) RtlTryEnterCriticalSection(crit)
#if 0
void WINAPI DeleteCriticalSection(CRITICAL_SECTION *lpCrit);
void WINAPI EnterCriticalSection(CRITICAL_SECTION *lpCrit);
BOOL WINAPI TryEnterCriticalSection(CRITICAL_SECTION *lpCrit);
void WINAPI LeaveCriticalSection(CRITICAL_SECTION *lpCrit);
#else
LPVOID WINAPI HeapAlloc(HANDLE,DWORD,DWORD);
BOOL WINAPI HeapFree(HANDLE,DWORD,LPVOID);
LPVOID WINAPI HeapReAlloc(HANDLE,DWORD,LPVOID,DWORD);
DWORD WINAPI HeapSize(HANDLE,DWORD,LPVOID);
void WINAPI DeleteCriticalSection(CRITICAL_SECTION *lpCrit);
void WINAPI EnterCriticalSection(CRITICAL_SECTION *lpCrit);
BOOL WINAPI TryEnterCriticalSection(CRITICAL_SECTION *lpCrit);
void WINAPI LeaveCriticalSection(CRITICAL_SECTION *lpCrit);
#endif
void WINAPI InitializeCriticalSection(CRITICAL_SECTION *lpCrit);
......@@ -1237,14 +1250,10 @@ BOOL WINAPI GetUserNameA(LPSTR,LPDWORD);
BOOL WINAPI GetUserNameW(LPWSTR,LPDWORD);
#define GetUserName WINELIB_NAME_AW(GetUserName)
VOID WINAPI GlobalMemoryStatus(LPMEMORYSTATUS);
LPVOID WINAPI HeapAlloc(HANDLE,DWORD,DWORD);
DWORD WINAPI HeapCompact(HANDLE,DWORD);
HANDLE WINAPI HeapCreate(DWORD,DWORD,DWORD);
BOOL WINAPI HeapDestroy(HANDLE);
BOOL WINAPI HeapFree(HANDLE,DWORD,LPVOID);
BOOL WINAPI HeapLock(HANDLE);
LPVOID WINAPI HeapReAlloc(HANDLE,DWORD,LPVOID,DWORD);
DWORD WINAPI HeapSize(HANDLE,DWORD,LPVOID);
BOOL WINAPI HeapUnlock(HANDLE);
BOOL WINAPI HeapValidate(HANDLE,DWORD,LPCVOID);
BOOL WINAPI HeapWalk(HANDLE,LPPROCESS_HEAP_ENTRY);
......
......@@ -524,11 +524,7 @@ typedef struct _SINGLE_LIST_ENTRY {
/* This flag allows it to create heaps shared by all processes under win95,
FIXME: correct name */
#define HEAP_SHARED 0x04000000
#define HEAP_WINE_SEGPTR 0x10000000 /* Not a Win32 flag */
#define HEAP_WINE_CODESEG 0x20000000 /* Not a Win32 flag */
#define HEAP_WINE_CODE16SEG 0x40000000 /* Not a Win32 flag */
#define HEAP_SHARED 0x04000000
/* Processor feature flags. */
#define PF_FLOATING_POINT_PRECISION_ERRATA 0
......
......@@ -12,1056 +12,103 @@
#include <stdio.h>
#include <string.h>
#include "wine/winbase16.h"
#include "wine/unicode.h"
#include "selectors.h"
#include "global.h"
#include "winbase.h"
#include "winerror.h"
#include "winnt.h"
#include "heap.h"
#include "toolhelp.h"
#include "debugtools.h"
#include "winnls.h"
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 */
WORD magic; /* Magic number */
WORD threadId; /* Allocating thread id */
void *callerEIP; /* EIP of caller upon allocation */
} ARENA_INUSE;
typedef struct tagARENA_FREE
{
DWORD size; /* Block size; must be the first field */
WORD magic; /* Magic number */
WORD threadId; /* Freeing thread id */
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 0xfffffffc
#define ARENA_INUSE_MAGIC 0x4842 /* Value for arena 'magic' field */
#define ARENA_FREE_MAGIC 0x4846 /* Value for arena 'magic' field */
#define ARENA_INUSE_FILLER 0x55
#define ARENA_FREE_FILLER 0xaa
#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, 0xffffffff
};
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 */
WORD selector; /* Selector for HEAP_WINE_SEGPTR heaps */
} 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 */
FREE_LIST_ENTRY freeList[HEAP_NB_FREE_LISTS]; /* Free lists */
CRITICAL_SECTION critSection; /* Critical section for serialization */
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 HEAP *systemHeap; /* globally shared heap */
static HEAP *processHeap; /* main process heap */
static HEAP *segptrHeap; /* main segptr heap */
static HEAP *firstHeap; /* head of secondary heaps list */
/* address where we try to map the system heap */
#define SYSTEM_HEAP_BASE ((void*)0x65430000)
static BOOL HEAP_IsRealArena( HEAP *heapPtr, DWORD flags, LPCVOID block, BOOL quiet );
#ifdef __GNUC__
#define GET_EIP() (__builtin_return_address(0))
#define SET_EIP(ptr) ((ARENA_INUSE*)(ptr) - 1)->callerEIP = GET_EIP()
#else
#define GET_EIP() 0
#define SET_EIP(ptr) /* nothing */
#endif /* __GNUC__ */
/***********************************************************************
* 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 %04x prev=%08lx next=%08lx\n",
(DWORD)&heap->freeList[i].arena, heap->freeList[i].arena.size,
heap->freeList[i].arena.threadId,
(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 %04x prev=%08lx next=%08lx\n",
(DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
pArena->threadId, (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 %04x back=%08lx EIP=%p\n",
(DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
pArena->threadId, *((DWORD *)pArena - 1),
pArena->callerEIP );
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 %04x EIP=%p\n",
(DWORD)pArena, pArena->size & ARENA_SIZE_MASK,
pArena->threadId, pArena->callerEIP );
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 %08x!\n", heap );
SetLastError( ERROR_INVALID_HANDLE );
return NULL;
}
if (TRACE_ON(heap) && !HEAP_IsRealArena( heapPtr, 0, NULL, NOISY ))
{
HEAP_Dump( heapPtr );
assert( FALSE );
SetLastError( ERROR_INVALID_HANDLE );
return NULL;
}
return heapPtr;
}
/***********************************************************************
* HEAP_InsertFreeBlock
*
* Insert a free block into the free list.
*/
static void HEAP_InsertFreeBlock( HEAP *heap, ARENA_FREE *pArena )
{
FREE_LIST_ENTRY *pEntry = heap->freeList;
while (pEntry->size < pArena->size) pEntry++;
pArena->size |= ARENA_FLAG_FREE;
pArena->next = pEntry->arena.next;
pArena->next->prev = pArena;
pArena->prev = &pEntry->arena;
pEntry->arena.next = pArena;
}
/***********************************************************************
* 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;
if (!VirtualAlloc( (char *)subheap + subheap->commitSize,
size - subheap->commitSize, MEM_COMMIT,
PAGE_EXECUTE_READWRITE))
{
WARN("Could not commit %08lx bytes at %08lx for heap %08lx\n",
size - subheap->commitSize,
(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 )
{
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;
if (!VirtualFree( (char *)subheap + size,
subheap->commitSize - size, MEM_DECOMMIT ))
{
WARN("Could not decommit %08lx bytes at %08lx for heap %08lx\n",
subheap->commitSize - size,
(DWORD)((char *)subheap + size),
(DWORD)subheap->heap );
return FALSE;
}
subheap->commitSize = 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;
/* Create a free arena */
pFree = (ARENA_FREE *)ptr;
pFree->threadId = GetCurrentTask();
pFree->magic = ARENA_FREE_MAGIC;
/* If debugging, erase the freed block content */
if (TRACE_ON(heap))
{
char *pEnd = (char *)ptr + size;
if (pEnd > (char *)subheap + subheap->commitSize)
pEnd = (char *)subheap + subheap->commitSize;
if (pEnd > (char *)(pFree + 1))
memset( pFree + 1, ARENA_FREE_FILLER, 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);
if (TRACE_ON(heap))
memset( pNext, ARENA_FREE_FILLER, sizeof(ARENA_FREE) );
}
/* Set the next block PREV_FREE flag and pointer */
if ((char *)ptr + size < (char *)subheap + subheap->size)
{
DWORD *pNext = (DWORD *)((char *)ptr + size);
*pNext |= ARENA_FLAG_PREV_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 );
}
/***********************************************************************
* 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))
{
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;
if (subheap->selector) FreeSelector16( subheap->selector );
VirtualFree( subheap, 0, 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 = (SUBHEAP *)address;
WORD selector = 0;
FREE_LIST_ENTRY *pEntry;
int i;
/* Commit memory */
if (flags & HEAP_SHARED)
commitSize = totalSize; /* always commit everything in a shared heap */
if (!VirtualAlloc(address, commitSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
{
WARN("Could not commit %08lx bytes for sub-heap %08lx\n",
commitSize, (DWORD)address );
return FALSE;
}
/* Allocate a selector if needed */
if (flags & HEAP_WINE_SEGPTR)
{
unsigned char selflags = WINE_LDT_FLAGS_DATA;
if (flags & (HEAP_WINE_CODESEG | HEAP_WINE_CODE16SEG))
selflags = WINE_LDT_FLAGS_CODE;
if (flags & HEAP_WINE_CODESEG)
selflags |= WINE_LDT_FLAGS_32BIT;
selector = SELECTOR_AllocBlock( address, totalSize, selflags );
if (!selector)
{
ERR("Could not allocate selector\n" );
return FALSE;
}
}
/* Fill the sub-heap structure */
subheap->heap = heap;
subheap->selector = selector;
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 = sizeof(SUBHEAP);
subheap->next = heap->subheap.next;
heap->subheap.next = subheap;
}
else
{
/* If this is a primary subheap, initialize main heap */
subheap->headerSize = 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.threadId = 0;
pEntry->arena.magic = ARENA_FREE_MAGIC;
}
/* Initialize critical section */
InitializeCriticalSection( &heap->critSection );
}
/* 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, DWORD flags,
DWORD commitSize, DWORD totalSize )
{
LPVOID address;
/* Round-up sizes on a 64K boundary */
if (flags & HEAP_WINE_SEGPTR)
{
totalSize = commitSize = 0x10000; /* Only 64K at a time for SEGPTRs */
}
else
{
totalSize = (totalSize + 0xffff) & 0xffff0000;
commitSize = (commitSize + 0xffff) & 0xffff0000;
if (!commitSize) commitSize = 0x10000;
if (totalSize < commitSize) totalSize = commitSize;
}
/* Allocate the memory block */
if (!(address = VirtualAlloc( NULL, totalSize,
MEM_RESERVE, PAGE_EXECUTE_READWRITE )))
{
WARN("Could not VirtualAlloc %08lx bytes\n",
totalSize );
return NULL;
}
/* Initialize subheap */
if (!HEAP_InitSubHeap( heap? heap : (HEAP *)address,
address, flags, commitSize, totalSize ))
{
VirtualFree( address, 0, 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 += sizeof(SUBHEAP) + sizeof(ARENA_INUSE) + HEAP_MIN_BLOCK_SIZE;
if (!(subheap = HEAP_CreateSubHeap( heap, 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;
#if !defined(ALLOW_UNALIGNED_ACCESS)
/* Check for unaligned pointers */
if ( (long)pArena % sizeof(void *) != 0 )
{
ERR( "Heap %08lx: unaligned arena pointer %08lx\n",
(DWORD)subheap->heap, (DWORD)pArena );
return FALSE;
}
#endif
/* 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;
#if !defined(ALLOW_UNALIGNED_ACCESS)
/* Check for unaligned pointers */
if ( (long)pArena % sizeof(void *) != 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;
}
#endif
/* 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 );
}
/* 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_GetSegptr
*
* Transform a linear pointer into a SEGPTR. The pointer must have been
* allocated from a HEAP_WINE_SEGPTR heap.
*/
SEGPTR HEAP_GetSegptr( HANDLE heap, DWORD flags, LPCVOID ptr )
{
HEAP *heapPtr = HEAP_GetPtr( heap );
SUBHEAP *subheap;
SEGPTR ret = 0;
/* Validate the parameters */
if (!heapPtr) return 0;
flags |= heapPtr->flags;
if (!(flags & HEAP_WINE_SEGPTR))
{
ERR("Heap %08x is not a SEGPTR heap\n",
heap );
return 0;
}
if (!(flags & HEAP_NO_SERIALIZE)) EnterCriticalSection( &heapPtr->critSection );
/* Get the subheap */
if ((subheap = HEAP_FindSubHeap( heapPtr, ptr )))
ret = MAKESEGPTR(subheap->selector, (char *)ptr - (char *)subheap);
if (!(flags & HEAP_NO_SERIALIZE)) LeaveCriticalSection( &heapPtr->critSection );
return ret;
}
/***********************************************************************
* MapLS (KERNEL32.@)
* MapLS (KERNEL.358)
*
* Maps linear pointer to segmented.
*/
SEGPTR WINAPI MapLS( LPCVOID ptr )
{
SUBHEAP *subheap;
SEGPTR ret = 0;
if (!HIWORD(ptr)) return (SEGPTR)ptr;
#include "winbase.h"
#include "wine/winbase16.h"
#include "winerror.h"
#include "winnt.h"
#include "ntddk.h"
#include "wine/unicode.h"
#include "selectors.h"
#include "global.h"
#include "thread.h"
#include "toolhelp.h"
#include "debugtools.h"
/* check if the pointer is inside the segptr heap */
EnterCriticalSection( &segptrHeap->critSection );
if ((subheap = HEAP_FindSubHeap( segptrHeap, ptr )))
ret = MAKESEGPTR( subheap->selector, (char *)ptr - (char *)subheap );
LeaveCriticalSection( &segptrHeap->critSection );
DEFAULT_DEBUG_CHANNEL(heap);
/* otherwise, allocate a brand-new selector */
if (!ret)
{
WORD sel = SELECTOR_AllocBlock( ptr, 0x10000, WINE_LDT_FLAGS_DATA );
ret = MAKESEGPTR( sel, 0 );
}
return ret;
}
/* address where we try to map the system heap */
#define SYSTEM_HEAP_BASE ((void*)0x65430000)
#define SYSTEM_HEAP_SIZE 0x100000 /* Default heap size = 1Mb */
/***********************************************************************
* UnMapLS (KERNEL32.@)
* UnMapLS (KERNEL.359)
*
* Free mapped selector.
*/
void WINAPI UnMapLS( SEGPTR sptr )
{
SUBHEAP *subheap;
if (!SELECTOROF(sptr)) return;
/* check if ptr is inside segptr heap */
EnterCriticalSection( &segptrHeap->critSection );
subheap = HEAP_FindSubHeap( segptrHeap, MapSL(sptr) );
if ((subheap) && (subheap->selector != SELECTOROF(sptr))) subheap = NULL;
LeaveCriticalSection( &segptrHeap->critSection );
/* if not inside heap, free the selector */
if (!subheap) FreeSelector16( SELECTOROF(sptr) );
}
#define HTABLE_SIZE 0x10000
#define HTABLE_PAGESIZE 0x1000
#define HTABLE_NPAGES (HTABLE_SIZE / HTABLE_PAGESIZE)
/***********************************************************************
* 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 */
#include "pshpack1.h"
typedef struct _LOCAL32HEADER
{
SUBHEAP *subheap;
BOOL ret = TRUE;
WORD freeListFirst[HTABLE_NPAGES];
WORD freeListSize[HTABLE_NPAGES];
WORD freeListLast[HTABLE_NPAGES];
if (!heapPtr || (heapPtr->magic != HEAP_MAGIC))
{
ERR("Invalid heap %p!\n", heapPtr );
return FALSE;
}
DWORD selectorTableOffset;
WORD selectorTableSize;
WORD selectorDelta;
flags &= HEAP_NO_SERIALIZE;
flags |= heapPtr->flags;
/* calling HeapLock may result in infinite recursion, so do the critsect directly */
if (!(flags & HEAP_NO_SERIALIZE))
EnterCriticalSection( &heapPtr->critSection );
DWORD segment;
LPBYTE base;
if (block)
{
/* Only check this single memory block */
DWORD limit;
DWORD flags;
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))
LeaveCriticalSection( &heapPtr->critSection );
return ret;
}
DWORD magic;
HANDLE heap;
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;
}
} LOCAL32HEADER;
#include "poppack.h"
if (!(flags & HEAP_NO_SERIALIZE))
LeaveCriticalSection( &heapPtr->critSection );
return ret;
}
#define LOCAL32_MAGIC ((DWORD)('L' | ('H'<<8) | ('3'<<16) | ('2'<<24)))
static HANDLE systemHeap; /* globally shared heap */
/***********************************************************************
* HEAP_CreateSystemHeap
*
* Create the system heap.
*/
static HANDLE HEAP_CreateSystemHeap(void)
inline static HANDLE HEAP_CreateSystemHeap(void)
{
int created;
void *base;
HANDLE map, event;
UNICODE_STRING event_name;
OBJECT_ATTRIBUTES event_attr;
HANDLE map = CreateFileMappingA( INVALID_HANDLE_VALUE, NULL, SEC_COMMIT | PAGE_READWRITE,
0, HEAP_DEF_SIZE, "__SystemHeap" );
if (!map) return 0;
if (!(map = CreateFileMappingA( INVALID_HANDLE_VALUE, NULL, SEC_COMMIT | PAGE_READWRITE,
0, SYSTEM_HEAP_SIZE, "__SystemHeap" ))) return 0;
created = (GetLastError() != ERROR_ALREADY_EXISTS);
if (!(systemHeap = MapViewOfFileEx( map, FILE_MAP_ALL_ACCESS, 0, 0, 0, SYSTEM_HEAP_BASE )))
if (!(base = MapViewOfFileEx( map, FILE_MAP_ALL_ACCESS, 0, 0, 0, SYSTEM_HEAP_BASE )))
{
/* pre-defined address not available, use any one */
/* pre-defined address not available */
ERR( "system heap base address %p not available\n", SYSTEM_HEAP_BASE );
return 0;
}
/* create the system heap event */
RtlCreateUnicodeStringFromAsciiz( &event_name, "__SystemHeapEvent" );
event_attr.Length = sizeof(event_attr);
event_attr.RootDirectory = 0;
event_attr.ObjectName = &event_name;
event_attr.Attributes = 0;
event_attr.SecurityDescriptor = NULL;
event_attr.SecurityQualityOfService = NULL;
NtCreateEvent( &event, EVENT_ALL_ACCESS, &event_attr, TRUE, FALSE );
if (created) /* newly created heap */
{
HEAP_InitSubHeap( systemHeap, systemHeap, HEAP_SHARED, 0, HEAP_DEF_SIZE );
MakeCriticalSectionGlobal( &systemHeap->critSection );
systemHeap = RtlCreateHeap( HEAP_SHARED, base, SYSTEM_HEAP_SIZE,
SYSTEM_HEAP_SIZE, NULL, NULL );
NtSetEvent( event, NULL );
}
else
{
/* wait for the heap to be initialized */
while (!systemHeap->critSection.LockSemaphore) Sleep(1);
WaitForSingleObject( event, INFINITE );
}
CloseHandle( map );
return (HANDLE)systemHeap;
return systemHeap;
}
......@@ -1076,49 +123,20 @@ HANDLE WINAPI HeapCreate(
DWORD initialSize, /* [in] Initial heap size */
DWORD maxSize /* [in] Maximum heap size */
) {
SUBHEAP *subheap;
HANDLE ret;
if ( flags & HEAP_SHARED ) {
if ( flags & HEAP_SHARED )
{
if (!systemHeap) HEAP_CreateSystemHeap();
else WARN( "Shared Heap requested, returning system heap.\n" );
return (HANDLE)systemHeap;
}
/* Allocate the heap block */
if (!maxSize)
{
maxSize = HEAP_DEF_SIZE;
flags |= HEAP_GROWABLE;
ret = systemHeap;
}
if (!(subheap = HEAP_CreateSubHeap( NULL, flags, initialSize, maxSize )))
{
SetLastError( ERROR_OUTOFMEMORY );
return 0;
}
/* link it into the per-process heap list */
if (processHeap)
{
HEAP *heapPtr = subheap->heap;
EnterCriticalSection( &processHeap->critSection );
heapPtr->next = firstHeap;
firstHeap = heapPtr;
LeaveCriticalSection( &processHeap->critSection );
}
else /* assume the first heap we create is the process main heap */
else
{
SUBHEAP *segptr;
processHeap = subheap->heap;
/* create the SEGPTR heap */
if (!(segptr = HEAP_CreateSubHeap( NULL, flags|HEAP_WINE_SEGPTR|HEAP_GROWABLE, 0, 0 )))
{
SetLastError( ERROR_OUTOFMEMORY );
return 0;
}
segptrHeap = segptr->heap;
ret = RtlCreateHeap( flags, NULL, maxSize, initialSize, NULL, NULL );
if (!ret) SetLastError( ERROR_NOT_ENOUGH_MEMORY );
}
return (HANDLE)subheap;
return ret;
}
/***********************************************************************
......@@ -1129,284 +147,14 @@ HANDLE WINAPI HeapCreate(
*/
BOOL WINAPI HeapDestroy( HANDLE heap /* [in] Handle of heap */ )
{
HEAP *heapPtr = HEAP_GetPtr( heap );
SUBHEAP *subheap;
TRACE("%08x\n", heap );
if (!heapPtr) return FALSE;
if (heapPtr == systemHeap)
if (heap == systemHeap)
{
WARN( "attempt to destroy system heap, returning TRUE!\n" );
return TRUE;
}
if (heapPtr == processHeap) /* cannot delete the main process heap */
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
else /* remove it from the per-process list */
{
HEAP **pptr;
EnterCriticalSection( &processHeap->critSection );
pptr = &firstHeap;
while (*pptr && *pptr != heapPtr) pptr = &(*pptr)->next;
if (*pptr) *pptr = (*pptr)->next;
LeaveCriticalSection( &processHeap->critSection );
}
DeleteCriticalSection( &heapPtr->critSection );
subheap = &heapPtr->subheap;
while (subheap)
{
SUBHEAP *next = subheap->next;
if (subheap->selector) FreeSelector16( subheap->selector );
VirtualFree( subheap, 0, MEM_RELEASE );
subheap = next;
}
return TRUE;
}
/***********************************************************************
* HeapAlloc (KERNEL32.@)
* RETURNS
* Pointer to allocated memory block
* NULL: Failure
*/
LPVOID WINAPI HeapAlloc(
HANDLE heap, /* [in] Handle of private heap block */
DWORD flags, /* [in] Heap allocation control flags */
DWORD size /* [in] Number of bytes to allocate */
) {
ARENA_FREE *pArena;
ARENA_INUSE *pInUse;
SUBHEAP *subheap;
HEAP *heapPtr = HEAP_GetPtr( heap );
/* Validate the parameters */
if ((flags & HEAP_WINE_SEGPTR) && size < 0x10000) heapPtr = segptrHeap;
if (!heapPtr) return NULL;
flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY;
flags |= heapPtr->flags;
size = (size + 3) & ~3;
if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
if (!(flags & HEAP_NO_SERIALIZE)) EnterCriticalSection( &heapPtr->critSection );
/* Locate a suitable free block */
if (!(pArena = HEAP_FindFreeBlock( heapPtr, size, &subheap )))
{
TRACE("(%08x,%08lx,%08lx): returning NULL\n",
heap, flags, size );
if (!(flags & HEAP_NO_SERIALIZE)) LeaveCriticalSection( &heapPtr->critSection );
SetLastError( ERROR_COMMITMENT_LIMIT );
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->callerEIP = GET_EIP();
pInUse->threadId = GetCurrentTask();
pInUse->magic = ARENA_INUSE_MAGIC;
/* Shrink the block */
HEAP_ShrinkBlock( subheap, pInUse, size );
if (flags & HEAP_ZERO_MEMORY)
memset( pInUse + 1, 0, pInUse->size & ARENA_SIZE_MASK );
else if (TRACE_ON(heap))
memset( pInUse + 1, ARENA_INUSE_FILLER, pInUse->size & ARENA_SIZE_MASK );
if (!(flags & HEAP_NO_SERIALIZE)) LeaveCriticalSection( &heapPtr->critSection );
TRACE("(%08x,%08lx,%08lx): returning %08lx\n",
heap, flags, size, (DWORD)(pInUse + 1) );
return (LPVOID)(pInUse + 1);
}
/***********************************************************************
* HeapFree (KERNEL32.@)
* RETURNS
* TRUE: Success
* FALSE: Failure
*/
BOOL WINAPI HeapFree(
HANDLE heap, /* [in] Handle of heap */
DWORD flags, /* [in] Heap freeing flags */
LPVOID ptr /* [in] Address of memory to free */
) {
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 (flags & HEAP_WINE_SEGPTR) heapPtr = segptrHeap;
if (!heapPtr) return FALSE;
flags &= HEAP_NO_SERIALIZE;
flags |= heapPtr->flags;
if (!(flags & HEAP_NO_SERIALIZE)) EnterCriticalSection( &heapPtr->critSection );
if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
{
if (!(flags & HEAP_NO_SERIALIZE)) LeaveCriticalSection( &heapPtr->critSection );
SetLastError( ERROR_INVALID_PARAMETER );
TRACE("(%08x,%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)) LeaveCriticalSection( &heapPtr->critSection );
TRACE("(%08x,%08lx,%08lx): returning TRUE\n",
heap, flags, (DWORD)ptr );
return TRUE;
}
/***********************************************************************
* HeapReAlloc (KERNEL32.@)
* RETURNS
* Pointer to reallocated memory block
* NULL: Failure
*/
LPVOID WINAPI HeapReAlloc(
HANDLE heap, /* [in] Handle of heap block */
DWORD flags, /* [in] Heap reallocation flags */
LPVOID ptr, /* [in] Address of memory to reallocate */
DWORD size /* [in] Number of bytes to reallocate */
) {
ARENA_INUSE *pArena;
DWORD oldSize;
HEAP *heapPtr;
SUBHEAP *subheap;
if (!ptr) return HeapAlloc( heap, flags, size ); /* FIXME: correct? */
if ((flags & HEAP_WINE_SEGPTR) && size < 0x10000) heapPtr = segptrHeap;
if (!(heapPtr = HEAP_GetPtr( heap ))) return FALSE;
/* Validate the parameters */
flags &= HEAP_GENERATE_EXCEPTIONS | HEAP_NO_SERIALIZE | HEAP_ZERO_MEMORY |
HEAP_REALLOC_IN_PLACE_ONLY;
flags |= heapPtr->flags;
size = (size + 3) & ~3;
if (size < HEAP_MIN_BLOCK_SIZE) size = HEAP_MIN_BLOCK_SIZE;
if (!(flags & HEAP_NO_SERIALIZE)) EnterCriticalSection( &heapPtr->critSection );
if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
{
if (!(flags & HEAP_NO_SERIALIZE)) LeaveCriticalSection( &heapPtr->critSection );
SetLastError( ERROR_INVALID_PARAMETER );
TRACE("(%08x,%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;
pArena->threadId = GetCurrentTask();
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)) LeaveCriticalSection( &heapPtr->critSection );
SetLastError( ERROR_OUTOFMEMORY );
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)) LeaveCriticalSection( &heapPtr->critSection );
SetLastError( ERROR_OUTOFMEMORY );
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->threadId = GetCurrentTask();
pInUse->magic = ARENA_INUSE_MAGIC;
HEAP_ShrinkBlock( newsubheap, pInUse, size );
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)
memset( (char *)(pArena + 1) + oldSize, 0,
(pArena->size & ARENA_SIZE_MASK) - oldSize );
else if (TRACE_ON(heap))
memset( (char *)(pArena + 1) + oldSize, ARENA_INUSE_FILLER,
(pArena->size & ARENA_SIZE_MASK) - oldSize );
}
/* Return the new arena */
pArena->callerEIP = GET_EIP();
if (!(flags & HEAP_NO_SERIALIZE)) LeaveCriticalSection( &heapPtr->critSection );
TRACE("(%08x,%08lx,%08lx,%08lx): returning %08lx\n",
heap, flags, (DWORD)ptr, size, (DWORD)(pArena + 1) );
return (LPVOID)(pArena + 1);
if (!RtlDestroyHeap( heap )) return TRUE;
SetLastError( ERROR_INVALID_HANDLE );
return FALSE;
}
......@@ -1415,8 +163,7 @@ LPVOID WINAPI HeapReAlloc(
*/
DWORD WINAPI HeapCompact( HANDLE heap, DWORD flags )
{
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return 0;
return RtlCompactHeap( heap, flags );
}
......@@ -1431,10 +178,7 @@ DWORD WINAPI HeapCompact( HANDLE heap, DWORD flags )
BOOL WINAPI HeapLock(
HANDLE heap /* [in] Handle of heap to lock for exclusive access */
) {
HEAP *heapPtr = HEAP_GetPtr( heap );
if (!heapPtr) return FALSE;
EnterCriticalSection( &heapPtr->critSection );
return TRUE;
return RtlLockHeap( heap );
}
......@@ -1449,47 +193,7 @@ BOOL WINAPI HeapLock(
BOOL WINAPI HeapUnlock(
HANDLE heap /* [in] Handle to the heap to unlock */
) {
HEAP *heapPtr = HEAP_GetPtr( heap );
if (!heapPtr) return FALSE;
LeaveCriticalSection( &heapPtr->critSection );
return TRUE;
}
/***********************************************************************
* HeapSize (KERNEL32.@)
* RETURNS
* Size in bytes of allocated memory
* 0xffffffff: Failure
*/
DWORD WINAPI HeapSize(
HANDLE heap, /* [in] Handle of heap */
DWORD flags, /* [in] Heap size control flags */
LPVOID ptr /* [in] Address of memory to return size for */
) {
DWORD ret;
HEAP *heapPtr = HEAP_GetPtr( heap );
if (flags & HEAP_WINE_SEGPTR) heapPtr = segptrHeap;
if (!heapPtr) return FALSE;
flags &= HEAP_NO_SERIALIZE;
flags |= heapPtr->flags;
if (!(flags & HEAP_NO_SERIALIZE)) EnterCriticalSection( &heapPtr->critSection );
if (!HEAP_IsRealArena( heapPtr, HEAP_NO_SERIALIZE, ptr, QUIET ))
{
SetLastError( ERROR_INVALID_PARAMETER );
ret = 0xffffffff;
}
else
{
ARENA_INUSE *pArena = (ARENA_INUSE *)ptr - 1;
ret = pArena->size & ARENA_SIZE_MASK;
}
if (!(flags & HEAP_NO_SERIALIZE)) LeaveCriticalSection( &heapPtr->critSection );
TRACE("(%08x,%08lx,%08lx): returning %08lx\n",
heap, flags, (DWORD)ptr, ret );
return ret;
return RtlUnlockHeap( heap );
}
......@@ -1509,17 +213,13 @@ BOOL WINAPI HeapValidate(
DWORD flags, /* [in] Bit flags that control access during operation */
LPCVOID block /* [in] Optional pointer to memory block to validate */
) {
HEAP *heapPtr = HEAP_GetPtr( heap );
if (flags & HEAP_WINE_SEGPTR) heapPtr = segptrHeap;
if (!heapPtr) return FALSE;
return HEAP_IsRealArena( heapPtr, flags, block, QUIET );
return RtlValidateHeap( heap, flags, block );
}
/***********************************************************************
* HeapWalk (KERNEL32.@)
* Enumerates the memory blocks in a specified heap.
* See HEAP_Dump() for info on heap structure.
*
* TODO
* - handling of PROCESS_HEAP_ENTRY_MOVEABLE and
......@@ -1533,110 +233,9 @@ BOOL WINAPI HeapWalk(
HANDLE heap, /* [in] Handle to heap to enumerate */
LPPROCESS_HEAP_ENTRY entry /* [out] Pointer to structure of enumeration info */
) {
HEAP *heapPtr = HEAP_GetPtr(heap);
SUBHEAP *sub, *currentheap = NULL;
BOOL ret = FALSE;
char *ptr;
int region_index = 0;
if (!heapPtr || !entry)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) EnterCriticalSection( &heapPtr->critSection );
/* set ptr to the next arena to be examined */
if (!entry->lpData) /* first call (init) ? */
{
TRACE("begin walking of heap 0x%08x.\n", heap);
/*HEAP_Dump(heapPtr);*/
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");
SetLastError(ERROR_NO_MORE_ITEMS);
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");
SetLastError(ERROR_NO_MORE_ITEMS);
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 = TRUE;
HW_end:
if (!(heapPtr->flags & HEAP_NO_SERIALIZE)) LeaveCriticalSection( &heapPtr->critSection );
return ret;
NTSTATUS ret = RtlWalkHeap( heap, entry );
if (ret) SetLastError( RtlNtStatusToDosError(ret) );
return !ret;
}
......@@ -1645,7 +244,8 @@ HW_end:
*/
HANDLE WINAPI GetProcessHeap(void)
{
return (HANDLE)processHeap;
HANDLE *pdb = (HANDLE *)NtCurrentTeb()->process;
return pdb[0x18 / sizeof(HANDLE)]; /* get dword at offset 0x18 in pdb */
}
......@@ -1654,20 +254,7 @@ HANDLE WINAPI GetProcessHeap(void)
*/
DWORD WINAPI GetProcessHeaps( DWORD count, HANDLE *heaps )
{
DWORD total;
HEAP *ptr;
if (!processHeap) return 0; /* should never happen */
total = 1; /* main heap */
EnterCriticalSection( &processHeap->critSection );
for (ptr = firstHeap; ptr; ptr = ptr->next) total++;
if (total <= count)
{
*heaps++ = (HANDLE)processHeap;
for (ptr = firstHeap; ptr; ptr = ptr->next) *heaps++ = (HANDLE)ptr;
}
LeaveCriticalSection( &processHeap->critSection );
return total;
return RtlGetProcessHeaps( count, heaps );
}
......@@ -1675,35 +262,6 @@ DWORD WINAPI GetProcessHeaps( DWORD count, HANDLE *heaps )
* 32-bit local heap functions (Win95; undocumented)
*/
#define HTABLE_SIZE 0x10000
#define HTABLE_PAGESIZE 0x1000
#define HTABLE_NPAGES (HTABLE_SIZE / HTABLE_PAGESIZE)
#include "pshpack1.h"
typedef struct _LOCAL32HEADER
{
WORD freeListFirst[HTABLE_NPAGES];
WORD freeListSize[HTABLE_NPAGES];
WORD freeListLast[HTABLE_NPAGES];
DWORD selectorTableOffset;
WORD selectorTableSize;
WORD selectorDelta;
DWORD segment;
LPBYTE base;
DWORD limit;
DWORD flags;
DWORD magic;
HANDLE heap;
} LOCAL32HEADER;
#include "poppack.h"
#define LOCAL32_MAGIC ((DWORD)('L' | ('H'<<8) | ('3'<<16) | ('2'<<24)))
/***********************************************************************
* K208 (KERNEL.208)
*/
......@@ -1713,7 +271,7 @@ HANDLE WINAPI Local32Init16( WORD segment, DWORD tableSize,
DWORD totSize, segSize = 0;
LPBYTE base;
LOCAL32HEADER *header;
HEAP *heap;
HANDLE heap;
WORD *selectorTable;
WORD selectorEven, selectorOdd;
int i, nrBlocks;
......@@ -1748,8 +306,7 @@ HANDLE WINAPI Local32Init16( WORD segment, DWORD tableSize,
return 0;
}
heap = (HEAP *)(base + segSize + HTABLE_SIZE);
if ( !HEAP_InitSubHeap( heap, (LPVOID)heap, 0, 0x10000, heapSize ) )
if (!(heap = RtlCreateHeap( 0, base + segSize + HTABLE_SIZE, heapSize, 0x10000, NULL, NULL )))
{
VirtualFree( base, 0, MEM_RELEASE );
return 0;
......@@ -1763,7 +320,7 @@ HANDLE WINAPI Local32Init16( WORD segment, DWORD tableSize,
header->limit = HTABLE_PAGESIZE-1;
header->flags = 0;
header->magic = LOCAL32_MAGIC;
header->heap = (HANDLE)heap;
header->heap = heap;
header->freeListFirst[0] = sizeof(LOCAL32HEADER);
header->freeListLast[0] = HTABLE_PAGESIZE - 4;
......@@ -2146,8 +703,7 @@ static LOCAL32HEADER *Local32_GetHeap( HGLOBAL16 handle )
*/
BOOL16 WINAPI Local32Info16( LOCAL32INFO *pLocal32Info, HGLOBAL16 handle )
{
SUBHEAP *heapPtr;
LPBYTE ptr;
PROCESS_HEAP_ENTRY entry;
int i;
LOCAL32HEADER *header = Local32_GetHeap( handle );
......@@ -2156,31 +712,24 @@ BOOL16 WINAPI Local32Info16( LOCAL32INFO *pLocal32Info, HGLOBAL16 handle )
if ( !pLocal32Info || pLocal32Info->dwSize < sizeof(LOCAL32INFO) )
return FALSE;
heapPtr = (SUBHEAP *)HEAP_GetPtr( header->heap );
pLocal32Info->dwMemReserved = heapPtr->size;
pLocal32Info->dwMemCommitted = heapPtr->commitSize;
pLocal32Info->dwTotalFree = 0L;
pLocal32Info->dwLargestFreeBlock = 0L;
pLocal32Info->dwMemReserved = 0;
pLocal32Info->dwMemCommitted = 0;
pLocal32Info->dwTotalFree = 0;
pLocal32Info->dwLargestFreeBlock = 0;
/* Note: Local32 heaps always have only one subheap! */
ptr = (LPBYTE)heapPtr + heapPtr->headerSize;
while ( ptr < (LPBYTE)heapPtr + heapPtr->size )
while (HeapWalk( header->heap, &entry ))
{
if (*(DWORD *)ptr & ARENA_FLAG_FREE)
if (entry.wFlags & PROCESS_HEAP_REGION)
{
ARENA_FREE *pArena = (ARENA_FREE *)ptr;
DWORD size = (pArena->size & ARENA_SIZE_MASK);
ptr += sizeof(*pArena) + size;
pLocal32Info->dwTotalFree += size;
if ( size > pLocal32Info->dwLargestFreeBlock )
pLocal32Info->dwLargestFreeBlock = size;
pLocal32Info->dwMemReserved += entry.u.Region.dwCommittedSize
+ entry.u.Region.dwUnCommittedSize;
pLocal32Info->dwMemCommitted = entry.u.Region.dwCommittedSize;
}
else
else if (!(entry.wFlags & PROCESS_HEAP_ENTRY_BUSY))
{
ARENA_INUSE *pArena = (ARENA_INUSE *)ptr;
DWORD size = (pArena->size & ARENA_SIZE_MASK);
ptr += sizeof(*pArena) + size;
DWORD size = entry.cbData + entry.cbOverhead;
pLocal32Info->dwTotalFree += size;
if (size > pLocal32Info->dwLargestFreeBlock) pLocal32Info->dwLargestFreeBlock = size;
}
}
......
......@@ -518,6 +518,84 @@ DWORD WINAPI MemoryWrite16( WORD sel, DWORD offset, void *buffer, DWORD count )
*
*/
struct mapls_entry
{
struct mapls_entry *next;
void *addr; /* linear address */
int count; /* ref count */
WORD sel; /* selector */
};
static struct mapls_entry *first_entry;
/***********************************************************************
* MapLS (KERNEL32.@)
* MapLS (KERNEL.358)
*
* Maps linear pointer to segmented.
*/
SEGPTR WINAPI MapLS( LPCVOID ptr )
{
struct mapls_entry *entry, *free = NULL;
void *base;
SEGPTR ret = 0;
if (!HIWORD(ptr)) return (SEGPTR)ptr;
base = (char *)ptr - ((unsigned int)ptr & 0x7fff);
HeapLock( GetProcessHeap() );
for (entry = first_entry; entry; entry = entry->next)
{
if (entry->addr == base) break;
if (!entry->count) free = entry;
}
if (!entry)
{
if (!free) /* no free entry found, create a new one */
{
if (!(free = HeapAlloc( GetProcessHeap(), 0, sizeof(*free) ))) goto done;
if (!(free->sel = SELECTOR_AllocBlock( base, 0x10000, WINE_LDT_FLAGS_DATA )))
{
HeapFree( GetProcessHeap(), 0, free );
goto done;
}
free->count = 0;
free->next = first_entry;
first_entry = free;
}
SetSelectorBase( free->sel, (DWORD)base );
free->addr = base;
entry = free;
}
entry->count++;
ret = MAKESEGPTR( entry->sel, (char *)ptr - (char *)entry->addr );
done:
HeapUnlock( GetProcessHeap() );
return ret;
}
/***********************************************************************
* UnMapLS (KERNEL32.@)
* UnMapLS (KERNEL.359)
*
* Free mapped selector.
*/
void WINAPI UnMapLS( SEGPTR sptr )
{
struct mapls_entry *entry;
WORD sel = SELECTOROF(sptr);
if (sel)
{
HeapLock( GetProcessHeap() );
for (entry = first_entry; entry; entry = entry->next) if (entry->sel == sel) break;
if (entry && entry->count > 0) entry->count--;
HeapUnlock( GetProcessHeap() );
}
}
/***********************************************************************
* MapSL (KERNEL32.@)
* MapSL (KERNEL.357)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment