/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "primpl.h" /* ** We override malloc etc. on any platform which has preemption + ** nspr20 user level threads. When we're debugging, we can make our ** version of malloc fail occasionally. */ #ifdef _PR_OVERRIDE_MALLOC /* ** Thread safe version of malloc, calloc, realloc, free */ # include # ifdef DEBUG # define SANITY # define EXTRA_SANITY # else # undef SANITY # undef EXTRA_SANITY # endif /* Forward decls */ void* _PR_UnlockedMalloc(size_t size); void _PR_UnlockedFree(void* ptr); void* _PR_UnlockedRealloc(void* ptr, size_t size); void* _PR_UnlockedCalloc(size_t n, size_t elsize); /************************************************************************/ /* * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp * ---------------------------------------------------------------------------- * */ /* * Defining SANITY will enable some checks which will tell you if the users * program did botch something */ /* * Defining EXTRA_SANITY will enable some checks which are mostly related * to internal conditions in malloc.c */ /* * Very verbose progress on stdout... */ # if 0 # define TRACE(foo) printf foo static int malloc_event; # else # define TRACE(foo) # endif /* XXX Pick a number, any number */ # define malloc_pagesize 4096UL # define malloc_pageshift 12UL # ifdef XP_UNIX # include # include # include # include # include # include # include # endif /* * This structure describes a page's worth of chunks. */ struct pginfo { struct pginfo* next; /* next on the free list */ char* page; /* Pointer to the page */ u_short size; /* size of this page's chunks */ u_short shift; /* How far to shift for this size chunks */ u_short free; /* How many free chunks */ u_short total; /* How many chunk */ u_long bits[1]; /* Which chunks are free */ }; struct pgfree { struct pgfree* next; /* next run of free pages */ struct pgfree* prev; /* prev run of free pages */ char* page; /* pointer to free pages */ char* end; /* pointer to end of free pages */ u_long size; /* number of bytes free */ }; /* * How many bits per u_long in the bitmap. * Change only if not 8 bits/byte */ # define MALLOC_BITS (8 * sizeof(u_long)) /* * Magic values to put in the page_directory */ # define MALLOC_NOT_MINE ((struct pginfo*)0) # define MALLOC_FREE ((struct pginfo*)1) # define MALLOC_FIRST ((struct pginfo*)2) # define MALLOC_FOLLOW ((struct pginfo*)3) # define MALLOC_MAGIC ((struct pginfo*)4) /* * Set to one when malloc_init has been called */ static unsigned initialized; /* * The size of a page. * Must be a integral multiplum of the granularity of mmap(2). * Your toes will curl if it isn't a power of two */ # define malloc_pagemask ((malloc_pagesize) - 1) /* * The size of the largest chunk. * Half a page. */ # define malloc_maxsize ((malloc_pagesize) >> 1) /* * malloc_pagesize == 1 << malloc_pageshift */ # ifndef malloc_pageshift static unsigned malloc_pageshift; # endif /* malloc_pageshift */ /* * The smallest allocation we bother about. * Must be power of two */ # ifndef malloc_minsize static unsigned malloc_minsize; # endif /* malloc_minsize */ /* * The largest chunk we care about. * Must be smaller than pagesize * Must be power of two */ # ifndef malloc_maxsize static unsigned malloc_maxsize; # endif /* malloc_maxsize */ # ifndef malloc_cache static unsigned malloc_cache; # endif /* malloc_cache */ /* * The offset from pagenumber to index into the page directory */ static u_long malloc_origo; /* * The last index in the page directory we care about */ static u_long last_index; /* * Pointer to page directory. * Allocated "as if with" malloc */ static struct pginfo** page_dir; /* * How many slots in the page directory */ static unsigned malloc_ninfo; /* * Free pages line up here */ static struct pgfree free_list; /* * Abort() if we fail to get VM ? */ static int malloc_abort; # ifdef SANITY /* * Are we trying to die ? */ static int suicide; # endif /* * dump statistics */ static int malloc_stats; /* * always realloc ? */ static int malloc_realloc; /* * my last break. */ static void* malloc_brk; /* * one location cache for free-list holders */ static struct pgfree* px; static int set_pgdir(void* ptr, struct pginfo* info); static int extend_page_directory(u_long index); # ifdef SANITY void malloc_dump(FILE* fd) { struct pginfo** pd; struct pgfree* pf; int j; pd = page_dir; /* print out all the pages */ for (j = 0; j <= last_index; j++) { fprintf(fd, "%08lx %5d ", (j + malloc_origo) << malloc_pageshift, j); if (pd[j] == MALLOC_NOT_MINE) { for (j++; j <= last_index && pd[j] == MALLOC_NOT_MINE; j++); j--; fprintf(fd, ".. %5d not mine\n", j); } else if (pd[j] == MALLOC_FREE) { for (j++; j <= last_index && pd[j] == MALLOC_FREE; j++); j--; fprintf(fd, ".. %5d free\n", j); } else if (pd[j] == MALLOC_FIRST) { for (j++; j <= last_index && pd[j] == MALLOC_FOLLOW; j++); j--; fprintf(fd, ".. %5d in use\n", j); } else if (pd[j] < MALLOC_MAGIC) { fprintf(fd, "(%p)\n", pd[j]); } else { fprintf(fd, "%p %d (of %d) x %d @ %p --> %p\n", pd[j], pd[j]->free, pd[j]->total, pd[j]->size, pd[j]->page, pd[j]->next); } } for (pf = free_list.next; pf; pf = pf->next) { fprintf(fd, "Free: @%p [%p...%p[ %ld ->%p <-%p\n", pf, pf->page, pf->end, pf->size, pf->prev, pf->next); if (pf == pf->next) { fprintf(fd, "Free_list loops.\n"); break; } } /* print out various info */ fprintf(fd, "Minsize\t%d\n", malloc_minsize); fprintf(fd, "Maxsize\t%ld\n", malloc_maxsize); fprintf(fd, "Pagesize\t%ld\n", malloc_pagesize); fprintf(fd, "Pageshift\t%ld\n", malloc_pageshift); fprintf(fd, "FirstPage\t%ld\n", malloc_origo); fprintf(fd, "LastPage\t%ld %lx\n", last_index + malloc_pageshift, (last_index + malloc_pageshift) << malloc_pageshift); fprintf(fd, "Break\t%ld\n", (u_long)sbrk(0) >> malloc_pageshift); } static void wrterror(char* fmt, ...) { char* q = "malloc() error: "; char buf[100]; va_list ap; suicide = 1; va_start(ap, fmt); PR_vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); fputs(q, stderr); fputs(buf, stderr); malloc_dump(stderr); PR_Abort(); } static void wrtwarning(char* fmt, ...) { char* q = "malloc() warning: "; char buf[100]; va_list ap; va_start(ap, fmt); PR_vsnprintf(buf, sizeof(buf), fmt, ap); va_end(ap); fputs(q, stderr); fputs(buf, stderr); } # endif /* SANITY */ /* * Allocate a number of pages from the OS */ static caddr_t map_pages(int pages, int update) { caddr_t result, tail; result = ((caddr_t)sbrk(0)) + malloc_pagemask - 1; result = (caddr_t)((u_long)result & ~malloc_pagemask); tail = result + (pages << malloc_pageshift); if (!brk(tail)) { last_index = ((u_long)tail >> malloc_pageshift) - malloc_origo - 1; malloc_brk = tail; TRACE(("%6d S %p .. %p\n", malloc_event++, result, tail)); if (!update || last_index < malloc_ninfo || extend_page_directory(last_index)) { return result; } } TRACE(("%6d s %d %p %d\n", malloc_event++, pages, sbrk(0), errno)); # ifdef EXTRA_SANITY wrterror("map_pages fails\n"); # endif return 0; } # define set_bit(_pi, _bit) \ (_pi)->bits[(_bit) / MALLOC_BITS] |= 1L << ((_bit) % MALLOC_BITS) # define clr_bit(_pi, _bit) \ (_pi)->bits[(_bit) / MALLOC_BITS] &= ~(1L << ((_bit) % MALLOC_BITS)); # define tst_bit(_pi, _bit) \ ((_pi)->bits[(_bit) / MALLOC_BITS] & (1L << ((_bit) % MALLOC_BITS))) /* * Extend page directory */ static int extend_page_directory(u_long index) { struct pginfo **young, **old; int i; TRACE(("%6d E %lu\n", malloc_event++, index)); /* Make it this many pages */ i = index * sizeof *page_dir; i /= malloc_pagesize; i += 2; /* Get new pages, if you used this much mem you don't care :-) */ young = (struct pginfo**)map_pages(i, 0); if (!young) { return 0; } /* Copy the old stuff */ memset(young, 0, i * malloc_pagesize); memcpy(young, page_dir, malloc_ninfo * sizeof *page_dir); /* register the new size */ malloc_ninfo = i * malloc_pagesize / sizeof *page_dir; /* swap the pointers */ old = page_dir; page_dir = young; /* Mark the pages */ index = ((u_long)young >> malloc_pageshift) - malloc_origo; page_dir[index] = MALLOC_FIRST; while (--i) { page_dir[++index] = MALLOC_FOLLOW; } /* Now free the old stuff */ _PR_UnlockedFree(old); return 1; } /* * Set entry in page directory. * Extend page directory if need be. */ static int set_pgdir(void* ptr, struct pginfo* info) { u_long index = ((u_long)ptr >> malloc_pageshift) - malloc_origo; if (index >= malloc_ninfo && !extend_page_directory(index)) { return 0; } page_dir[index] = info; return 1; } /* * Initialize the world */ static void malloc_init(void) { int i; char* p; TRACE(("%6d I\n", malloc_event++)); # ifdef DEBUG for (p = getenv("MALLOC_OPTIONS"); p && *p; p++) { switch (*p) { case 'a': malloc_abort = 0; break; case 'A': malloc_abort = 1; break; case 'd': malloc_stats = 0; break; case 'D': malloc_stats = 1; break; case 'r': malloc_realloc = 0; break; case 'R': malloc_realloc = 1; break; default: wrtwarning("Unknown chars in MALLOC_OPTIONS\n"); break; } } # endif # ifndef malloc_pagesize /* determine our pagesize */ malloc_pagesize = getpagesize(); # endif /* malloc_pagesize */ # ifndef malloc_pageshift /* determine how much we shift by to get there */ for (i = malloc_pagesize; i > 1; i >>= 1) { malloc_pageshift++; } # endif /* malloc_pageshift */ # ifndef malloc_cache malloc_cache = 50 << malloc_pageshift; # endif /* malloc_cache */ # ifndef malloc_minsize /* * find the smallest size allocation we will bother about. * this is determined as the smallest allocation that can hold * it's own pginfo; */ i = 2; for (;;) { int j; /* Figure out the size of the bits */ j = malloc_pagesize / i; j /= 8; if (j < sizeof(u_long)) { j = sizeof(u_long); } if (sizeof(struct pginfo) + j - sizeof(u_long) <= i) { break; } i += i; } malloc_minsize = i; # endif /* malloc_minsize */ /* Allocate one page for the page directory */ page_dir = (struct pginfo**)map_pages(1, 0); # ifdef SANITY if (!page_dir) { wrterror("fatal: my first mmap failed. (check limits ?)\n"); } # endif /* * We need a maximum of malloc_pageshift buckets, steal these from the * front of the page_directory; */ malloc_origo = (u_long)page_dir >> malloc_pageshift; malloc_origo -= malloc_pageshift; /* Clear it */ memset(page_dir, 0, malloc_pagesize); /* Find out how much it tells us */ malloc_ninfo = malloc_pagesize / sizeof *page_dir; /* Plug the page directory into itself */ i = set_pgdir(page_dir, MALLOC_FIRST); # ifdef SANITY if (!i) { wrterror("fatal: couldn't set myself in the page directory\n"); } # endif /* Been here, done that */ initialized++; } /* * Allocate a number of complete pages */ static void* malloc_pages(size_t size) { void *p, *delay_free = 0; int i; struct pgfree* pf; u_long index; /* How many pages ? */ size += (malloc_pagesize - 1); size &= ~malloc_pagemask; p = 0; /* Look for free pages before asking for more */ for (pf = free_list.next; pf; pf = pf->next) { # ifdef EXTRA_SANITY if (pf->page == pf->end) { wrterror("zero entry on free_list\n"); } if (pf->page > pf->end) { TRACE(("%6d !s %p %p %p <%d>\n", malloc_event++, pf, pf->page, pf->end, __LINE__)); wrterror("sick entry on free_list\n"); } if ((void*)pf->page >= (void*)sbrk(0)) { wrterror("entry on free_list past brk\n"); } if (page_dir[((u_long)pf->page >> malloc_pageshift) - malloc_origo] != MALLOC_FREE) { TRACE(("%6d !f %p %p %p <%d>\n", malloc_event++, pf, pf->page, pf->end, __LINE__)); wrterror("non-free first page on free-list\n"); } if (page_dir[((u_long)pf->end >> malloc_pageshift) - 1 - malloc_origo] != MALLOC_FREE) { wrterror("non-free last page on free-list\n"); } # endif /* EXTRA_SANITY */ if (pf->size < size) { continue; } else if (pf->size == size) { p = pf->page; if (pf->next) { pf->next->prev = pf->prev; } pf->prev->next = pf->next; delay_free = pf; break; } else { p = pf->page; pf->page += size; pf->size -= size; break; } } # ifdef EXTRA_SANITY if (p && page_dir[((u_long)p >> malloc_pageshift) - malloc_origo] != MALLOC_FREE) { wrterror("allocated non-free page on free-list\n"); } # endif /* EXTRA_SANITY */ size >>= malloc_pageshift; /* Map new pages */ if (!p) { p = map_pages(size, 1); } if (p) { /* Mark the pages in the directory */ index = ((u_long)p >> malloc_pageshift) - malloc_origo; page_dir[index] = MALLOC_FIRST; for (i = 1; i < size; i++) { page_dir[index + i] = MALLOC_FOLLOW; } } if (delay_free) { if (!px) { px = (struct pgfree*)delay_free; } else { _PR_UnlockedFree(delay_free); } } return p; } /* * Allocate a page of fragments */ static int malloc_make_chunks(int bits) { struct pginfo* bp; void* pp; int i, k, l; /* Allocate a new bucket */ pp = malloc_pages(malloc_pagesize); if (!pp) { return 0; } l = sizeof *bp - sizeof(u_long); l += sizeof(u_long) * (((malloc_pagesize >> bits) + MALLOC_BITS - 1) / MALLOC_BITS); if ((1 << (bits)) <= l + l) { bp = (struct pginfo*)pp; } else { bp = (struct pginfo*)_PR_UnlockedMalloc(l); } if (!bp) { return 0; } bp->size = (1 << bits); bp->shift = bits; bp->total = bp->free = malloc_pagesize >> bits; bp->next = page_dir[bits]; bp->page = (char*)pp; i = set_pgdir(pp, bp); if (!i) { return 0; } /* We can safely assume that there is nobody in this chain */ page_dir[bits] = bp; /* set all valid bits in the bits */ k = bp->total; i = 0; /* for(;k-i >= MALLOC_BITS; i += MALLOC_BITS) bp->bits[i / MALLOC_BITS] = ~0; */ for (; i < k; i++) { set_bit(bp, i); } if (bp != pp) { return 1; } /* We may have used the first ones already */ for (i = 0; l > 0; i++) { clr_bit(bp, i); bp->free--; bp->total--; l -= (1 << bits); } return 1; } /* * Allocate a fragment */ static void* malloc_bytes(size_t size) { size_t s; int j; struct pginfo* bp; int k; u_long *lp, bf; /* Don't bother with anything less than this */ if (size < malloc_minsize) { size = malloc_minsize; } /* Find the right bucket */ j = 1; s = size - 1; while (s >>= 1) { j++; } /* If it's empty, make a page more of that size chunks */ if (!page_dir[j] && !malloc_make_chunks(j)) { return 0; } /* Find first word of bitmap which isn't empty */ bp = page_dir[j]; for (lp = bp->bits; !*lp; lp++); /* Find that bit */ bf = *lp; k = 0; while ((bf & 1) == 0) { bf >>= 1; k++; } *lp ^= 1L << k; /* clear it */ bp->free--; if (!bp->free) { page_dir[j] = bp->next; bp->next = 0; } k += (lp - bp->bits) * MALLOC_BITS; return bp->page + (k << bp->shift); } void* _PR_UnlockedMalloc(size_t size) { void* result; /* Round up to a multiple of 8 bytes */ if (size & 7) { size = size + 8 - (size & 7); } if (!initialized) { malloc_init(); } # ifdef SANITY if (suicide) { PR_Abort(); } # endif if (size <= malloc_maxsize) { result = malloc_bytes(size); } else { result = malloc_pages(size); } # ifdef SANITY if (malloc_abort && !result) { wrterror("malloc() returns NULL\n"); } # endif TRACE(("%6d M %p %d\n", malloc_event++, result, size)); return result; } void* _PR_UnlockedMemalign(size_t alignment, size_t size) { void* result; /* * alignment has to be a power of 2 */ if ((size <= alignment) && (alignment <= malloc_maxsize)) { size = alignment; } else { size += alignment - 1; } /* Round up to a multiple of 8 bytes */ if (size & 7) { size = size + 8 - (size & 7); } if (!initialized) { malloc_init(); } # ifdef SANITY if (suicide) { abort(); } # endif if (size <= malloc_maxsize) { result = malloc_bytes(size); } else { result = malloc_pages(size); } # ifdef SANITY if (malloc_abort && !result) { wrterror("malloc() returns NULL\n"); } # endif TRACE(("%6d A %p %d\n", malloc_event++, result, size)); if ((u_long)result & (alignment - 1)) { return ((void*)(((u_long)result + alignment) & ~(alignment - 1))); } else { return result; } } void* _PR_UnlockedCalloc(size_t n, size_t nelem) { void* p; /* Compute total size and then round up to a double word amount */ n *= nelem; if (n & 7) { n = n + 8 - (n & 7); } /* Get the memory */ p = _PR_UnlockedMalloc(n); if (p) { /* Zero it */ memset(p, 0, n); } return p; } /* * Change an allocation's size */ void* _PR_UnlockedRealloc(void* ptr, size_t size) { void* p; u_long osize, page, index, tmp_index; struct pginfo** mp; if (!initialized) { malloc_init(); } # ifdef SANITY if (suicide) { PR_Abort(); } # endif /* used as free() */ TRACE(("%6d R %p %d\n", malloc_event++, ptr, size)); if (ptr && !size) { _PR_UnlockedFree(ptr); return _PR_UnlockedMalloc(1); } /* used as malloc() */ if (!ptr) { p = _PR_UnlockedMalloc(size); return p; } /* Find the page directory entry for the page in question */ page = (u_long)ptr >> malloc_pageshift; index = page - malloc_origo; /* * check if memory was allocated by memalign */ tmp_index = index; while (page_dir[tmp_index] == MALLOC_FOLLOW) { tmp_index--; } if (tmp_index != index) { /* * memalign-allocated memory */ index = tmp_index; page = index + malloc_origo; ptr = (void*)(page << malloc_pageshift); } TRACE(("%6d R2 %p %d\n", malloc_event++, ptr, size)); /* make sure it makes sense in some fashion */ if (index < malloc_pageshift || index > last_index) { # ifdef SANITY wrtwarning("junk pointer passed to realloc()\n"); # endif return 0; } /* find the size of that allocation, and see if we need to relocate */ mp = &page_dir[index]; if (*mp == MALLOC_FIRST) { osize = malloc_pagesize; while (mp[1] == MALLOC_FOLLOW) { osize += malloc_pagesize; mp++; } if (!malloc_realloc && size < osize && size > malloc_maxsize && size > (osize - malloc_pagesize)) { return ptr; } } else if (*mp >= MALLOC_MAGIC) { osize = (*mp)->size; if (!malloc_realloc && size < osize && (size > (*mp)->size / 2 || (*mp)->size == malloc_minsize)) { return ptr; } } else { # ifdef SANITY wrterror("realloc() of wrong page.\n"); # endif } /* try to reallocate */ p = _PR_UnlockedMalloc(size); if (p) { /* copy the lesser of the two sizes */ if (osize < size) { memcpy(p, ptr, osize); } else { memcpy(p, ptr, size); } _PR_UnlockedFree(ptr); } # ifdef DEBUG else if (malloc_abort) { wrterror("realloc() returns NULL\n"); } # endif return p; } /* * Free a sequence of pages */ static void free_pages(char* ptr, u_long page, int index, struct pginfo* info) { int i; struct pgfree *pf, *pt; u_long l; char* tail; TRACE(("%6d FP %p %d\n", malloc_event++, ptr, page)); /* Is it free already ? */ if (info == MALLOC_FREE) { # ifdef SANITY wrtwarning("freeing free page at %p.\n", ptr); # endif return; } # ifdef SANITY /* Is it not the right place to begin ? */ if (info != MALLOC_FIRST) { wrterror("freeing wrong page.\n"); } /* Is this really a pointer to a page ? */ if ((u_long)ptr & malloc_pagemask) { wrterror("freeing messed up page pointer.\n"); } # endif /* Count how many pages it is anyway */ page_dir[index] = MALLOC_FREE; for (i = 1; page_dir[index + i] == MALLOC_FOLLOW; i++) { page_dir[index + i] = MALLOC_FREE; } l = i << malloc_pageshift; tail = ptr + l; /* add to free-list */ if (!px) { px = (struct pgfree*)_PR_UnlockedMalloc(sizeof *pt); } /* XXX check success */ px->page = ptr; px->end = tail; px->size = l; if (!free_list.next) { px->next = free_list.next; px->prev = &free_list; free_list.next = px; pf = px; px = 0; } else { tail = ptr + l; for (pf = free_list.next; pf->next && pf->end < ptr; pf = pf->next); for (; pf; pf = pf->next) { if (pf->end == ptr) { /* append to entry */ pf->end += l; pf->size += l; if (pf->next && pf->end == pf->next->page) { pt = pf->next; pf->end = pt->end; pf->size += pt->size; pf->next = pt->next; if (pf->next) { pf->next->prev = pf; } _PR_UnlockedFree(pt); } } else if (pf->page == tail) { /* prepend to entry */ pf->size += l; pf->page = ptr; } else if (pf->page > ptr) { px->next = pf; px->prev = pf->prev; pf->prev = px; px->prev->next = px; pf = px; px = 0; } else if (!pf->next) { px->next = 0; px->prev = pf; pf->next = px; pf = px; px = 0; } else { continue; } break; } } if (!pf->next && pf->size > malloc_cache && pf->end == malloc_brk && malloc_brk == (void*)sbrk(0)) { pf->end = pf->page + malloc_cache; pf->size = malloc_cache; TRACE(("%6d U %p %d\n", malloc_event++, pf->end, pf->end - pf->page)); brk(pf->end); malloc_brk = pf->end; /* Find the page directory entry for the page in question */ page = (u_long)pf->end >> malloc_pageshift; index = page - malloc_origo; /* Now update the directory */ for (i = index; i <= last_index;) { page_dir[i++] = MALLOC_NOT_MINE; } last_index = index - 1; } } /* * Free a chunk, and possibly the page it's on, if the page becomes empty. */ static void free_bytes(void* ptr, u_long page, int index, struct pginfo* info) { int i; struct pginfo** mp; void* vp; /* Make sure that pointer is multiplum of chunk-size */ # ifdef SANITY if ((u_long)ptr & (info->size - 1)) { wrterror("freeing messed up chunk pointer\n"); } # endif /* Find the chunk number on the page */ i = ((u_long)ptr & malloc_pagemask) >> info->shift; /* See if it's free already */ if (tst_bit(info, i)) { # ifdef SANITY wrtwarning("freeing free chunk at %p\n", ptr); # endif return; } /* Mark it free */ set_bit(info, i); info->free++; /* If the page was full before, we need to put it on the queue now */ if (info->free == 1) { mp = page_dir + info->shift; while (*mp && (*mp)->next && (*mp)->next->page < info->page) { mp = &(*mp)->next; } info->next = *mp; *mp = info; return; } /* If this page isn't empty, don't do anything. */ if (info->free != info->total) { return; } /* We may want to keep at least one page of each size chunks around. */ mp = page_dir + info->shift; if (0 && (*mp == info) && !info->next) { return; } /* Find & remove this page in the queue */ while (*mp != info) { mp = &((*mp)->next); # ifdef EXTRA_SANITY if (!*mp) { TRACE(("%6d !q %p\n", malloc_event++, info)); wrterror("Not on queue\n"); } # endif } *mp = info->next; /* Free the page & the info structure if need be */ set_pgdir(info->page, MALLOC_FIRST); if ((void*)info->page == (void*)info) { _PR_UnlockedFree(info->page); } else { vp = info->page; _PR_UnlockedFree(info); _PR_UnlockedFree(vp); } } void _PR_UnlockedFree(void* ptr) { u_long page; struct pginfo* info; int index, tmp_index; TRACE(("%6d F %p\n", malloc_event++, ptr)); /* This is legal */ if (!ptr) { return; } # ifdef SANITY /* There wouldn't be anything to free */ if (!initialized) { wrtwarning("free() called before malloc() ever got called\n"); return; } # endif # ifdef SANITY if (suicide) { PR_Abort(); } # endif /* Find the page directory entry for the page in question */ page = (u_long)ptr >> malloc_pageshift; index = page - malloc_origo; /* * check if memory was allocated by memalign */ tmp_index = index; while (page_dir[tmp_index] == MALLOC_FOLLOW) { tmp_index--; } if (tmp_index != index) { /* * memalign-allocated memory */ index = tmp_index; page = index + malloc_origo; ptr = (void*)(page << malloc_pageshift); } /* make sure it makes sense in some fashion */ if (index < malloc_pageshift) { # ifdef SANITY wrtwarning("junk pointer %p (low) passed to free()\n", ptr); # endif return; } if (index > last_index) { # ifdef SANITY wrtwarning("junk pointer %p (high) passed to free()\n", ptr); # endif return; } /* handle as page-allocation or chunk allocation */ info = page_dir[index]; if (info < MALLOC_MAGIC) { free_pages((char*)ptr, page, index, info); } else { free_bytes(ptr, page, index, info); } return; } #endif /* _PR_OVERRIDE_MALLOC */