xref: /illumos-gate/usr/src/lib/libc/port/gen/malloc.c (revision 1da57d55)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52333b8a2Sraf  * Common Development and Distribution License (the "License").
62333b8a2Sraf  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
212333b8a2Sraf 
227c478bd9Sstevel@tonic-gate /*
238cd45542Sraf  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*	Copyright (c) 1988 AT&T	*/
287c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate  *	Memory management: malloc(), realloc(), free().
327c478bd9Sstevel@tonic-gate  *
337c478bd9Sstevel@tonic-gate  *	The following #-parameters may be redefined:
347c478bd9Sstevel@tonic-gate  *	SEGMENTED: if defined, memory requests are assumed to be
357c478bd9Sstevel@tonic-gate  *		non-contiguous across calls of GETCORE's.
367c478bd9Sstevel@tonic-gate  *	GETCORE: a function to get more core memory. If not SEGMENTED,
377c478bd9Sstevel@tonic-gate  *		GETCORE(0) is assumed to return the next available
387c478bd9Sstevel@tonic-gate  *		address. Default is 'sbrk'.
397c478bd9Sstevel@tonic-gate  *	ERRCORE: the error code as returned by GETCORE.
407c478bd9Sstevel@tonic-gate  *		Default is (char *)(-1).
417c478bd9Sstevel@tonic-gate  *	CORESIZE: a desired unit (measured in bytes) to be used
427c478bd9Sstevel@tonic-gate  *		with GETCORE. Default is (1024*ALIGN).
437c478bd9Sstevel@tonic-gate  *
447c478bd9Sstevel@tonic-gate  *	This algorithm is based on a  best fit strategy with lists of
457c478bd9Sstevel@tonic-gate  *	free elts maintained in a self-adjusting binary tree. Each list
467c478bd9Sstevel@tonic-gate  *	contains all elts of the same size. The tree is ordered by size.
477c478bd9Sstevel@tonic-gate  *	For results on self-adjusting trees, see the paper:
487c478bd9Sstevel@tonic-gate  *		Self-Adjusting Binary Trees,
497c478bd9Sstevel@tonic-gate  *		DD Sleator & RE Tarjan, JACM 1985.
507c478bd9Sstevel@tonic-gate  *
517c478bd9Sstevel@tonic-gate  *	The header of a block contains the size of the data part in bytes.
527c478bd9Sstevel@tonic-gate  *	Since the size of a block is 0%4, the low two bits of the header
537c478bd9Sstevel@tonic-gate  *	are free and used as follows:
547c478bd9Sstevel@tonic-gate  *
557c478bd9Sstevel@tonic-gate  *		BIT0:	1 for busy (block is in use), 0 for free.
567c478bd9Sstevel@tonic-gate  *		BIT1:	if the block is busy, this bit is 1 if the
577c478bd9Sstevel@tonic-gate  *			preceding block in contiguous memory is free.
587c478bd9Sstevel@tonic-gate  *			Otherwise, it is always 0.
597c478bd9Sstevel@tonic-gate  */
607c478bd9Sstevel@tonic-gate 
61*7257d1b4Sraf #include "lint.h"
627c478bd9Sstevel@tonic-gate #include "mallint.h"
637c478bd9Sstevel@tonic-gate #include "mtlib.h"
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate /*
667c478bd9Sstevel@tonic-gate  * Some abusers of the system (notably java1.2) acquire __malloc_lock
677c478bd9Sstevel@tonic-gate  * in order to prevent threads from holding it while they are being
687c478bd9Sstevel@tonic-gate  * suspended via thr_suspend() or thr_suspend_allmutators().
692333b8a2Sraf  * This never worked when alternate malloc() libraries were used
702333b8a2Sraf  * because they don't use __malloc_lock for their locking strategy.
712333b8a2Sraf  * We leave __malloc_lock as an external variable to satisfy these
722333b8a2Sraf  * old programs, but we define a new lock, private to libc, to do the
732333b8a2Sraf  * real locking: libc_malloc_lock.  This puts libc's malloc() package
742333b8a2Sraf  * on the same footing as all other malloc packages.
757c478bd9Sstevel@tonic-gate  */
767c478bd9Sstevel@tonic-gate mutex_t __malloc_lock = DEFAULTMUTEX;
777c478bd9Sstevel@tonic-gate mutex_t libc_malloc_lock = DEFAULTMUTEX;
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate static TREE	*Root,		/* root of the free tree */
807c478bd9Sstevel@tonic-gate 		*Bottom,	/* the last free chunk in the arena */
817c478bd9Sstevel@tonic-gate 		*_morecore(size_t);	/* function to get more core */
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate static char	*Baddr;		/* current high address of the arena */
847c478bd9Sstevel@tonic-gate static char	*Lfree;		/* last freed block with data intact */
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate static void	t_delete(TREE *);
877c478bd9Sstevel@tonic-gate static void	t_splay(TREE *);
887c478bd9Sstevel@tonic-gate static void	realfree(void *);
897c478bd9Sstevel@tonic-gate static void	cleanfree(void *);
907c478bd9Sstevel@tonic-gate static void	*_malloc_unlocked(size_t);
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate #define	FREESIZE (1<<5) /* size for preserving free blocks until next malloc */
937c478bd9Sstevel@tonic-gate #define	FREEMASK FREESIZE-1
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate static void *flist[FREESIZE];	/* list of blocks to be freed on next malloc */
967c478bd9Sstevel@tonic-gate static int freeidx;		/* index of free blocks in flist % FREESIZE */
977c478bd9Sstevel@tonic-gate 
982333b8a2Sraf /*
992333b8a2Sraf  * Interfaces used only by atfork_init() functions.
1002333b8a2Sraf  */
1012333b8a2Sraf void
malloc_locks(void)1022333b8a2Sraf malloc_locks(void)
1032333b8a2Sraf {
1048cd45542Sraf 	(void) mutex_lock(&libc_malloc_lock);
1052333b8a2Sraf }
1062333b8a2Sraf 
1072333b8a2Sraf void
malloc_unlocks(void)1082333b8a2Sraf malloc_unlocks(void)
1092333b8a2Sraf {
1108cd45542Sraf 	(void) mutex_unlock(&libc_malloc_lock);
1112333b8a2Sraf }
1122333b8a2Sraf 
1137c478bd9Sstevel@tonic-gate /*
1147c478bd9Sstevel@tonic-gate  *	Allocation of small blocks
1157c478bd9Sstevel@tonic-gate  */
1167c478bd9Sstevel@tonic-gate static TREE	*List[MINSIZE/WORDSIZE-1]; /* lists of small blocks */
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate static void *
_smalloc(size_t size)1197c478bd9Sstevel@tonic-gate _smalloc(size_t size)
1207c478bd9Sstevel@tonic-gate {
1217c478bd9Sstevel@tonic-gate 	TREE	*tp;
1227c478bd9Sstevel@tonic-gate 	size_t	i;
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate 	ASSERT(size % WORDSIZE == 0);
1257c478bd9Sstevel@tonic-gate 	/* want to return a unique pointer on malloc(0) */
1267c478bd9Sstevel@tonic-gate 	if (size == 0)
1277c478bd9Sstevel@tonic-gate 		size = WORDSIZE;
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 	/* list to use */
1307c478bd9Sstevel@tonic-gate 	i = size / WORDSIZE - 1;
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	if (List[i] == NULL) {
1337c478bd9Sstevel@tonic-gate 		TREE *np;
1347c478bd9Sstevel@tonic-gate 		int n;
1357c478bd9Sstevel@tonic-gate 		/* number of blocks to get at one time */
1367c478bd9Sstevel@tonic-gate #define	NPS (WORDSIZE*8)
1377c478bd9Sstevel@tonic-gate 		ASSERT((size + WORDSIZE) * NPS >= MINSIZE);
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate 		/* get NPS of these block types */
1407c478bd9Sstevel@tonic-gate 		if ((List[i] = _malloc_unlocked((size + WORDSIZE) * NPS)) == 0)
1417c478bd9Sstevel@tonic-gate 			return (0);
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 		/* make them into a link list */
1447c478bd9Sstevel@tonic-gate 		for (n = 0, np = List[i]; n < NPS; ++n) {
1457c478bd9Sstevel@tonic-gate 			tp = np;
1467c478bd9Sstevel@tonic-gate 			SIZE(tp) = size;
1477c478bd9Sstevel@tonic-gate 			np = NEXT(tp);
1487c478bd9Sstevel@tonic-gate 			AFTER(tp) = np;
1497c478bd9Sstevel@tonic-gate 		}
1507c478bd9Sstevel@tonic-gate 		AFTER(tp) = NULL;
1517c478bd9Sstevel@tonic-gate 	}
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	/* allocate from the head of the queue */
1547c478bd9Sstevel@tonic-gate 	tp = List[i];
1557c478bd9Sstevel@tonic-gate 	List[i] = AFTER(tp);
1567c478bd9Sstevel@tonic-gate 	SETBIT0(SIZE(tp));
1577c478bd9Sstevel@tonic-gate 	return (DATA(tp));
1587c478bd9Sstevel@tonic-gate }
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate void *
malloc(size_t size)1617c478bd9Sstevel@tonic-gate malloc(size_t size)
1627c478bd9Sstevel@tonic-gate {
1637c478bd9Sstevel@tonic-gate 	void *ret;
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	if (!primary_link_map) {
1667c478bd9Sstevel@tonic-gate 		errno = ENOTSUP;
1677c478bd9Sstevel@tonic-gate 		return (NULL);
1687c478bd9Sstevel@tonic-gate 	}
1697c478bd9Sstevel@tonic-gate 	assert_no_libc_locks_held();
1708cd45542Sraf 	(void) mutex_lock(&libc_malloc_lock);
1717c478bd9Sstevel@tonic-gate 	ret = _malloc_unlocked(size);
1728cd45542Sraf 	(void) mutex_unlock(&libc_malloc_lock);
1737c478bd9Sstevel@tonic-gate 	return (ret);
1747c478bd9Sstevel@tonic-gate }
1757c478bd9Sstevel@tonic-gate 
1767c478bd9Sstevel@tonic-gate static void *
_malloc_unlocked(size_t size)1777c478bd9Sstevel@tonic-gate _malloc_unlocked(size_t size)
1787c478bd9Sstevel@tonic-gate {
1797c478bd9Sstevel@tonic-gate 	size_t	n;
1807c478bd9Sstevel@tonic-gate 	TREE	*tp, *sp;
1817c478bd9Sstevel@tonic-gate 	size_t	o_bit1;
1827c478bd9Sstevel@tonic-gate 
1837c478bd9Sstevel@tonic-gate 	COUNT(nmalloc);
1847c478bd9Sstevel@tonic-gate 	ASSERT(WORDSIZE == ALIGN);
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 	/* check for size that could overflow calculations */
1877c478bd9Sstevel@tonic-gate 	if (size > MAX_MALLOC) {
1887c478bd9Sstevel@tonic-gate 		errno = ENOMEM;
1897c478bd9Sstevel@tonic-gate 		return (NULL);
1907c478bd9Sstevel@tonic-gate 	}
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	/* make sure that size is 0 mod ALIGN */
1937c478bd9Sstevel@tonic-gate 	ROUND(size);
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	/* see if the last free block can be used */
1967c478bd9Sstevel@tonic-gate 	if (Lfree) {
1977c478bd9Sstevel@tonic-gate 		sp = BLOCK(Lfree);
1987c478bd9Sstevel@tonic-gate 		n = SIZE(sp);
1997c478bd9Sstevel@tonic-gate 		CLRBITS01(n);
2007c478bd9Sstevel@tonic-gate 		if (n == size) {
2017c478bd9Sstevel@tonic-gate 			/*
2027c478bd9Sstevel@tonic-gate 			 * exact match, use it as is
2037c478bd9Sstevel@tonic-gate 			 */
2047c478bd9Sstevel@tonic-gate 			freeidx = (freeidx + FREESIZE - 1) &
2058cd45542Sraf 			    FREEMASK; /* 1 back */
2067c478bd9Sstevel@tonic-gate 			flist[freeidx] = Lfree = NULL;
2077c478bd9Sstevel@tonic-gate 			return (DATA(sp));
2087c478bd9Sstevel@tonic-gate 		} else if (size >= MINSIZE && n > size) {
2097c478bd9Sstevel@tonic-gate 			/*
2107c478bd9Sstevel@tonic-gate 			 * got a big enough piece
2117c478bd9Sstevel@tonic-gate 			 */
2127c478bd9Sstevel@tonic-gate 			freeidx = (freeidx + FREESIZE - 1) &
2138cd45542Sraf 			    FREEMASK; /* 1 back */
2147c478bd9Sstevel@tonic-gate 			flist[freeidx] = Lfree = NULL;
2157c478bd9Sstevel@tonic-gate 			o_bit1 = SIZE(sp) & BIT1;
2167c478bd9Sstevel@tonic-gate 			SIZE(sp) = n;
2177c478bd9Sstevel@tonic-gate 			goto leftover;
2187c478bd9Sstevel@tonic-gate 		}
2197c478bd9Sstevel@tonic-gate 	}
2207c478bd9Sstevel@tonic-gate 	o_bit1 = 0;
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	/* perform free's of space since last malloc */
2237c478bd9Sstevel@tonic-gate 	cleanfree(NULL);
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	/* small blocks */
2267c478bd9Sstevel@tonic-gate 	if (size < MINSIZE)
2277c478bd9Sstevel@tonic-gate 		return (_smalloc(size));
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	/* search for an elt of the right size */
2307c478bd9Sstevel@tonic-gate 	sp = NULL;
2317c478bd9Sstevel@tonic-gate 	n  = 0;
2327c478bd9Sstevel@tonic-gate 	if (Root) {
2337c478bd9Sstevel@tonic-gate 		tp = Root;
2347c478bd9Sstevel@tonic-gate 		for (;;) {
2357c478bd9Sstevel@tonic-gate 			/* branch left */
2367c478bd9Sstevel@tonic-gate 			if (SIZE(tp) >= size) {
2377c478bd9Sstevel@tonic-gate 				if (n == 0 || n >= SIZE(tp)) {
2387c478bd9Sstevel@tonic-gate 					sp = tp;
2397c478bd9Sstevel@tonic-gate 					n = SIZE(tp);
2407c478bd9Sstevel@tonic-gate 				}
2417c478bd9Sstevel@tonic-gate 				if (LEFT(tp))
2427c478bd9Sstevel@tonic-gate 					tp = LEFT(tp);
2437c478bd9Sstevel@tonic-gate 				else
2447c478bd9Sstevel@tonic-gate 					break;
2457c478bd9Sstevel@tonic-gate 			} else { /* branch right */
2467c478bd9Sstevel@tonic-gate 				if (RIGHT(tp))
2477c478bd9Sstevel@tonic-gate 					tp = RIGHT(tp);
2487c478bd9Sstevel@tonic-gate 				else
2497c478bd9Sstevel@tonic-gate 					break;
2507c478bd9Sstevel@tonic-gate 			}
2517c478bd9Sstevel@tonic-gate 		}
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate 		if (sp) {
2547c478bd9Sstevel@tonic-gate 			t_delete(sp);
2557c478bd9Sstevel@tonic-gate 		} else if (tp != Root) {
2567c478bd9Sstevel@tonic-gate 			/* make the searched-to element the root */
2577c478bd9Sstevel@tonic-gate 			t_splay(tp);
2587c478bd9Sstevel@tonic-gate 			Root = tp;
2597c478bd9Sstevel@tonic-gate 		}
2607c478bd9Sstevel@tonic-gate 	}
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	/* if found none fitted in the tree */
2637c478bd9Sstevel@tonic-gate 	if (!sp) {
2647c478bd9Sstevel@tonic-gate 		if (Bottom && size <= SIZE(Bottom)) {
2657c478bd9Sstevel@tonic-gate 			sp = Bottom;
2667c478bd9Sstevel@tonic-gate 			CLRBITS01(SIZE(sp));
2677c478bd9Sstevel@tonic-gate 		} else if ((sp = _morecore(size)) == NULL) /* no more memory */
2687c478bd9Sstevel@tonic-gate 			return (NULL);
2697c478bd9Sstevel@tonic-gate 	}
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate 	/* tell the forward neighbor that we're busy */
2727c478bd9Sstevel@tonic-gate 	CLRBIT1(SIZE(NEXT(sp)));
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	ASSERT(ISBIT0(SIZE(NEXT(sp))));
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate leftover:
2777c478bd9Sstevel@tonic-gate 	/* if the leftover is enough for a new free piece */
2787c478bd9Sstevel@tonic-gate 	if ((n = (SIZE(sp) - size)) >= MINSIZE + WORDSIZE) {
2797c478bd9Sstevel@tonic-gate 		n -= WORDSIZE;
2807c478bd9Sstevel@tonic-gate 		SIZE(sp) = size;
2817c478bd9Sstevel@tonic-gate 		tp = NEXT(sp);
2827c478bd9Sstevel@tonic-gate 		SIZE(tp) = n|BIT0;
2837c478bd9Sstevel@tonic-gate 		realfree(DATA(tp));
2847c478bd9Sstevel@tonic-gate 	} else if (BOTTOM(sp))
2857c478bd9Sstevel@tonic-gate 		Bottom = NULL;
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 	/* return the allocated space */
2887c478bd9Sstevel@tonic-gate 	SIZE(sp) |= BIT0 | o_bit1;
2897c478bd9Sstevel@tonic-gate 	return (DATA(sp));
2907c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 
2937c478bd9Sstevel@tonic-gate /*
2947c478bd9Sstevel@tonic-gate  * realloc().
2957c478bd9Sstevel@tonic-gate  *
2967c478bd9Sstevel@tonic-gate  * If the block size is increasing, we try forward merging first.
2977c478bd9Sstevel@tonic-gate  * This is not best-fit but it avoids some data recopying.
2987c478bd9Sstevel@tonic-gate  */
2997c478bd9Sstevel@tonic-gate void *
realloc(void * old,size_t size)3007c478bd9Sstevel@tonic-gate realloc(void *old, size_t size)
3017c478bd9Sstevel@tonic-gate {
3027c478bd9Sstevel@tonic-gate 	TREE	*tp, *np;
3037c478bd9Sstevel@tonic-gate 	size_t	ts;
3047c478bd9Sstevel@tonic-gate 	char	*new;
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	if (!primary_link_map) {
3077c478bd9Sstevel@tonic-gate 		errno = ENOTSUP;
3087c478bd9Sstevel@tonic-gate 		return (NULL);
3097c478bd9Sstevel@tonic-gate 	}
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	assert_no_libc_locks_held();
3127c478bd9Sstevel@tonic-gate 	COUNT(nrealloc);
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 	/* check for size that could overflow calculations */
3157c478bd9Sstevel@tonic-gate 	if (size > MAX_MALLOC) {
3167c478bd9Sstevel@tonic-gate 		errno = ENOMEM;
3177c478bd9Sstevel@tonic-gate 		return (NULL);
3187c478bd9Sstevel@tonic-gate 	}
3197c478bd9Sstevel@tonic-gate 
3207c478bd9Sstevel@tonic-gate 	/* pointer to the block */
3218cd45542Sraf 	(void) mutex_lock(&libc_malloc_lock);
3227c478bd9Sstevel@tonic-gate 	if (old == NULL) {
3237c478bd9Sstevel@tonic-gate 		new = _malloc_unlocked(size);
3248cd45542Sraf 		(void) mutex_unlock(&libc_malloc_lock);
3257c478bd9Sstevel@tonic-gate 		return (new);
3267c478bd9Sstevel@tonic-gate 	}
3277c478bd9Sstevel@tonic-gate 
3287c478bd9Sstevel@tonic-gate 	/* perform free's of space since last malloc */
3297c478bd9Sstevel@tonic-gate 	cleanfree(old);
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 	/* make sure that size is 0 mod ALIGN */
3327c478bd9Sstevel@tonic-gate 	ROUND(size);
3337c478bd9Sstevel@tonic-gate 
3347c478bd9Sstevel@tonic-gate 	tp = BLOCK(old);
3357c478bd9Sstevel@tonic-gate 	ts = SIZE(tp);
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	/* if the block was freed, data has been destroyed. */
3387c478bd9Sstevel@tonic-gate 	if (!ISBIT0(ts)) {
3398cd45542Sraf 		(void) mutex_unlock(&libc_malloc_lock);
3407c478bd9Sstevel@tonic-gate 		return (NULL);
3417c478bd9Sstevel@tonic-gate 	}
3427c478bd9Sstevel@tonic-gate 
3437c478bd9Sstevel@tonic-gate 	/* nothing to do */
3447c478bd9Sstevel@tonic-gate 	CLRBITS01(SIZE(tp));
3457c478bd9Sstevel@tonic-gate 	if (size == SIZE(tp)) {
3467c478bd9Sstevel@tonic-gate 		SIZE(tp) = ts;
3478cd45542Sraf 		(void) mutex_unlock(&libc_malloc_lock);
3487c478bd9Sstevel@tonic-gate 		return (old);
3497c478bd9Sstevel@tonic-gate 	}
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	/* special cases involving small blocks */
3527c478bd9Sstevel@tonic-gate 	if (size < MINSIZE || SIZE(tp) < MINSIZE) {
3537c478bd9Sstevel@tonic-gate 		/* free is size is zero */
3547c478bd9Sstevel@tonic-gate 		if (size == 0) {
3557c478bd9Sstevel@tonic-gate 			SETOLD01(SIZE(tp), ts);
3567c478bd9Sstevel@tonic-gate 			_free_unlocked(old);
3578cd45542Sraf 			(void) mutex_unlock(&libc_malloc_lock);
3587c478bd9Sstevel@tonic-gate 			return (NULL);
3597c478bd9Sstevel@tonic-gate 		} else {
3607c478bd9Sstevel@tonic-gate 			goto call_malloc;
3617c478bd9Sstevel@tonic-gate 		}
3627c478bd9Sstevel@tonic-gate 	}
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	/* block is increasing in size, try merging the next block */
3657c478bd9Sstevel@tonic-gate 	if (size > SIZE(tp)) {
3667c478bd9Sstevel@tonic-gate 		np = NEXT(tp);
3677c478bd9Sstevel@tonic-gate 		if (!ISBIT0(SIZE(np))) {
3687c478bd9Sstevel@tonic-gate 			ASSERT(SIZE(np) >= MINSIZE);
3697c478bd9Sstevel@tonic-gate 			ASSERT(!ISBIT1(SIZE(np)));
3707c478bd9Sstevel@tonic-gate 			SIZE(tp) += SIZE(np) + WORDSIZE;
3717c478bd9Sstevel@tonic-gate 			if (np != Bottom)
3727c478bd9Sstevel@tonic-gate 				t_delete(np);
3737c478bd9Sstevel@tonic-gate 			else
3747c478bd9Sstevel@tonic-gate 				Bottom = NULL;
3757c478bd9Sstevel@tonic-gate 			CLRBIT1(SIZE(NEXT(np)));
3767c478bd9Sstevel@tonic-gate 		}
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate #ifndef SEGMENTED
3797c478bd9Sstevel@tonic-gate 		/* not enough & at TRUE end of memory, try extending core */
3807c478bd9Sstevel@tonic-gate 		if (size > SIZE(tp) && BOTTOM(tp) && GETCORE(0) == Baddr) {
3817c478bd9Sstevel@tonic-gate 			Bottom = tp;
3827c478bd9Sstevel@tonic-gate 			if ((tp = _morecore(size)) == NULL) {
3837c478bd9Sstevel@tonic-gate 				tp = Bottom;
3847c478bd9Sstevel@tonic-gate 				Bottom = NULL;
3857c478bd9Sstevel@tonic-gate 				}
3867c478bd9Sstevel@tonic-gate 		}
3877c478bd9Sstevel@tonic-gate #endif
3887c478bd9Sstevel@tonic-gate 	}
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	/* got enough space to use */
3917c478bd9Sstevel@tonic-gate 	if (size <= SIZE(tp)) {
3927c478bd9Sstevel@tonic-gate 		size_t n;
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate chop_big:
3957c478bd9Sstevel@tonic-gate 		if ((n = (SIZE(tp) - size)) >= MINSIZE + WORDSIZE) {
3967c478bd9Sstevel@tonic-gate 			n -= WORDSIZE;
3977c478bd9Sstevel@tonic-gate 			SIZE(tp) = size;
3987c478bd9Sstevel@tonic-gate 			np = NEXT(tp);
3997c478bd9Sstevel@tonic-gate 			SIZE(np) = n|BIT0;
4007c478bd9Sstevel@tonic-gate 			realfree(DATA(np));
4017c478bd9Sstevel@tonic-gate 		} else if (BOTTOM(tp))
4027c478bd9Sstevel@tonic-gate 			Bottom = NULL;
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate 		/* the previous block may be free */
4057c478bd9Sstevel@tonic-gate 		SETOLD01(SIZE(tp), ts);
4068cd45542Sraf 		(void) mutex_unlock(&libc_malloc_lock);
4077c478bd9Sstevel@tonic-gate 		return (old);
4087c478bd9Sstevel@tonic-gate 	}
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 	/* call malloc to get a new block */
4117c478bd9Sstevel@tonic-gate call_malloc:
4127c478bd9Sstevel@tonic-gate 	SETOLD01(SIZE(tp), ts);
4137c478bd9Sstevel@tonic-gate 	if ((new = _malloc_unlocked(size)) != NULL) {
4147c478bd9Sstevel@tonic-gate 		CLRBITS01(ts);
4157c478bd9Sstevel@tonic-gate 		if (ts > size)
4167c478bd9Sstevel@tonic-gate 			ts = size;
4177c478bd9Sstevel@tonic-gate 		MEMCOPY(new, old, ts);
4187c478bd9Sstevel@tonic-gate 		_free_unlocked(old);
4198cd45542Sraf 		(void) mutex_unlock(&libc_malloc_lock);
4207c478bd9Sstevel@tonic-gate 		return (new);
4217c478bd9Sstevel@tonic-gate 	}
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 	/*
4247c478bd9Sstevel@tonic-gate 	 * Attempt special case recovery allocations since malloc() failed:
4257c478bd9Sstevel@tonic-gate 	 *
4267c478bd9Sstevel@tonic-gate 	 * 1. size <= SIZE(tp) < MINSIZE
4277c478bd9Sstevel@tonic-gate 	 *	Simply return the existing block
4287c478bd9Sstevel@tonic-gate 	 * 2. SIZE(tp) < size < MINSIZE
4297c478bd9Sstevel@tonic-gate 	 *	malloc() may have failed to allocate the chunk of
4307c478bd9Sstevel@tonic-gate 	 *	small blocks. Try asking for MINSIZE bytes.
4317c478bd9Sstevel@tonic-gate 	 * 3. size < MINSIZE <= SIZE(tp)
4327c478bd9Sstevel@tonic-gate 	 *	malloc() may have failed as with 2.  Change to
4337c478bd9Sstevel@tonic-gate 	 *	MINSIZE allocation which is taken from the beginning
4347c478bd9Sstevel@tonic-gate 	 *	of the current block.
4357c478bd9Sstevel@tonic-gate 	 * 4. MINSIZE <= SIZE(tp) < size
4367c478bd9Sstevel@tonic-gate 	 *	If the previous block is free and the combination of
4377c478bd9Sstevel@tonic-gate 	 *	these two blocks has at least size bytes, then merge
4387c478bd9Sstevel@tonic-gate 	 *	the two blocks copying the existing contents backwards.
4397c478bd9Sstevel@tonic-gate 	 */
4407c478bd9Sstevel@tonic-gate 	CLRBITS01(SIZE(tp));
4417c478bd9Sstevel@tonic-gate 	if (SIZE(tp) < MINSIZE) {
4427c478bd9Sstevel@tonic-gate 		if (size < SIZE(tp)) {			/* case 1. */
4437c478bd9Sstevel@tonic-gate 			SETOLD01(SIZE(tp), ts);
4448cd45542Sraf 			(void) mutex_unlock(&libc_malloc_lock);
4457c478bd9Sstevel@tonic-gate 			return (old);
4467c478bd9Sstevel@tonic-gate 		} else if (size < MINSIZE) {		/* case 2. */
4477c478bd9Sstevel@tonic-gate 			size = MINSIZE;
4487c478bd9Sstevel@tonic-gate 			goto call_malloc;
4497c478bd9Sstevel@tonic-gate 		}
4507c478bd9Sstevel@tonic-gate 	} else if (size < MINSIZE) {			/* case 3. */
4517c478bd9Sstevel@tonic-gate 		size = MINSIZE;
4527c478bd9Sstevel@tonic-gate 		goto chop_big;
4537c478bd9Sstevel@tonic-gate 	} else if (ISBIT1(ts) &&
4547c478bd9Sstevel@tonic-gate 	    (SIZE(np = LAST(tp)) + SIZE(tp) + WORDSIZE) >= size) {
4557c478bd9Sstevel@tonic-gate 		ASSERT(!ISBIT0(SIZE(np)));
4567c478bd9Sstevel@tonic-gate 		t_delete(np);
4577c478bd9Sstevel@tonic-gate 		SIZE(np) += SIZE(tp) + WORDSIZE;
4587c478bd9Sstevel@tonic-gate 		/*
4597c478bd9Sstevel@tonic-gate 		 * Since the copy may overlap, use memmove() if available.
4607c478bd9Sstevel@tonic-gate 		 * Otherwise, copy by hand.
4617c478bd9Sstevel@tonic-gate 		 */
4627c478bd9Sstevel@tonic-gate 		(void) memmove(DATA(np), old, SIZE(tp));
4637c478bd9Sstevel@tonic-gate 		old = DATA(np);
4647c478bd9Sstevel@tonic-gate 		tp = np;
4657c478bd9Sstevel@tonic-gate 		CLRBIT1(ts);
4667c478bd9Sstevel@tonic-gate 		goto chop_big;
4677c478bd9Sstevel@tonic-gate 	}
4687c478bd9Sstevel@tonic-gate 	SETOLD01(SIZE(tp), ts);
4698cd45542Sraf 	(void) mutex_unlock(&libc_malloc_lock);
4707c478bd9Sstevel@tonic-gate 	return (NULL);
4717c478bd9Sstevel@tonic-gate }
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 
4747c478bd9Sstevel@tonic-gate /*
4757c478bd9Sstevel@tonic-gate  * realfree().
4767c478bd9Sstevel@tonic-gate  *
4777c478bd9Sstevel@tonic-gate  * Coalescing of adjacent free blocks is done first.
4787c478bd9Sstevel@tonic-gate  * Then, the new free block is leaf-inserted into the free tree
4797c478bd9Sstevel@tonic-gate  * without splaying. This strategy does not guarantee the amortized
4807c478bd9Sstevel@tonic-gate  * O(nlogn) behaviour for the insert/delete/find set of operations
4817c478bd9Sstevel@tonic-gate  * on the tree. In practice, however, free is much more infrequent
4827c478bd9Sstevel@tonic-gate  * than malloc/realloc and the tree searches performed by these
4837c478bd9Sstevel@tonic-gate  * functions adequately keep the tree in balance.
4847c478bd9Sstevel@tonic-gate  */
4857c478bd9Sstevel@tonic-gate static void
realfree(void * old)4867c478bd9Sstevel@tonic-gate realfree(void *old)
4877c478bd9Sstevel@tonic-gate {
4887c478bd9Sstevel@tonic-gate 	TREE	*tp, *sp, *np;
4897c478bd9Sstevel@tonic-gate 	size_t	ts, size;
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	COUNT(nfree);
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 	/* pointer to the block */
4947c478bd9Sstevel@tonic-gate 	tp = BLOCK(old);
4957c478bd9Sstevel@tonic-gate 	ts = SIZE(tp);
4967c478bd9Sstevel@tonic-gate 	if (!ISBIT0(ts))
4977c478bd9Sstevel@tonic-gate 		return;
4987c478bd9Sstevel@tonic-gate 	CLRBITS01(SIZE(tp));
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate 	/* small block, put it in the right linked list */
5017c478bd9Sstevel@tonic-gate 	if (SIZE(tp) < MINSIZE) {
5027c478bd9Sstevel@tonic-gate 		ASSERT(SIZE(tp) / WORDSIZE >= 1);
5037c478bd9Sstevel@tonic-gate 		ts = SIZE(tp) / WORDSIZE - 1;
5047c478bd9Sstevel@tonic-gate 		AFTER(tp) = List[ts];
5057c478bd9Sstevel@tonic-gate 		List[ts] = tp;
5067c478bd9Sstevel@tonic-gate 		return;
5077c478bd9Sstevel@tonic-gate 	}
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate 	/* see if coalescing with next block is warranted */
5107c478bd9Sstevel@tonic-gate 	np = NEXT(tp);
5117c478bd9Sstevel@tonic-gate 	if (!ISBIT0(SIZE(np))) {
5127c478bd9Sstevel@tonic-gate 		if (np != Bottom)
5137c478bd9Sstevel@tonic-gate 			t_delete(np);
5147c478bd9Sstevel@tonic-gate 		SIZE(tp) += SIZE(np) + WORDSIZE;
5157c478bd9Sstevel@tonic-gate 	}
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate 	/* the same with the preceding block */
5187c478bd9Sstevel@tonic-gate 	if (ISBIT1(ts)) {
5197c478bd9Sstevel@tonic-gate 		np = LAST(tp);
5207c478bd9Sstevel@tonic-gate 		ASSERT(!ISBIT0(SIZE(np)));
5217c478bd9Sstevel@tonic-gate 		ASSERT(np != Bottom);
5227c478bd9Sstevel@tonic-gate 		t_delete(np);
5237c478bd9Sstevel@tonic-gate 		SIZE(np) += SIZE(tp) + WORDSIZE;
5247c478bd9Sstevel@tonic-gate 		tp = np;
5257c478bd9Sstevel@tonic-gate 	}
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 	/* initialize tree info */
5287c478bd9Sstevel@tonic-gate 	PARENT(tp) = LEFT(tp) = RIGHT(tp) = LINKFOR(tp) = NULL;
5297c478bd9Sstevel@tonic-gate 
5307c478bd9Sstevel@tonic-gate 	/* the last word of the block contains self's address */
5317c478bd9Sstevel@tonic-gate 	*(SELFP(tp)) = tp;
5327c478bd9Sstevel@tonic-gate 
5337c478bd9Sstevel@tonic-gate 	/* set bottom block, or insert in the free tree */
5347c478bd9Sstevel@tonic-gate 	if (BOTTOM(tp))
5357c478bd9Sstevel@tonic-gate 		Bottom = tp;
5367c478bd9Sstevel@tonic-gate 	else {
5377c478bd9Sstevel@tonic-gate 		/* search for the place to insert */
5387c478bd9Sstevel@tonic-gate 		if (Root) {
5397c478bd9Sstevel@tonic-gate 			size = SIZE(tp);
5407c478bd9Sstevel@tonic-gate 			np = Root;
5417c478bd9Sstevel@tonic-gate 			for (;;) {
5427c478bd9Sstevel@tonic-gate 				if (SIZE(np) > size) {
5437c478bd9Sstevel@tonic-gate 					if (LEFT(np))
5447c478bd9Sstevel@tonic-gate 						np = LEFT(np);
5457c478bd9Sstevel@tonic-gate 					else {
5467c478bd9Sstevel@tonic-gate 						LEFT(np) = tp;
5477c478bd9Sstevel@tonic-gate 						PARENT(tp) = np;
5487c478bd9Sstevel@tonic-gate 						break;
5497c478bd9Sstevel@tonic-gate 					}
5507c478bd9Sstevel@tonic-gate 				} else if (SIZE(np) < size) {
5517c478bd9Sstevel@tonic-gate 					if (RIGHT(np))
5527c478bd9Sstevel@tonic-gate 						np = RIGHT(np);
5537c478bd9Sstevel@tonic-gate 					else {
5547c478bd9Sstevel@tonic-gate 						RIGHT(np) = tp;
5557c478bd9Sstevel@tonic-gate 						PARENT(tp) = np;
5567c478bd9Sstevel@tonic-gate 						break;
5577c478bd9Sstevel@tonic-gate 					}
5587c478bd9Sstevel@tonic-gate 				} else {
5597c478bd9Sstevel@tonic-gate 					if ((sp = PARENT(np)) != NULL) {
5607c478bd9Sstevel@tonic-gate 						if (np == LEFT(sp))
5617c478bd9Sstevel@tonic-gate 							LEFT(sp) = tp;
5627c478bd9Sstevel@tonic-gate 						else
5637c478bd9Sstevel@tonic-gate 							RIGHT(sp) = tp;
5647c478bd9Sstevel@tonic-gate 						PARENT(tp) = sp;
5657c478bd9Sstevel@tonic-gate 					} else
5667c478bd9Sstevel@tonic-gate 						Root = tp;
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate 					/* insert to head of list */
5697c478bd9Sstevel@tonic-gate 					if ((sp = LEFT(np)) != NULL)
5707c478bd9Sstevel@tonic-gate 						PARENT(sp) = tp;
5717c478bd9Sstevel@tonic-gate 					LEFT(tp) = sp;
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 					if ((sp = RIGHT(np)) != NULL)
5747c478bd9Sstevel@tonic-gate 						PARENT(sp) = tp;
5757c478bd9Sstevel@tonic-gate 					RIGHT(tp) = sp;
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 					/* doubly link list */
5787c478bd9Sstevel@tonic-gate 					LINKFOR(tp) = np;
5797c478bd9Sstevel@tonic-gate 					LINKBAK(np) = tp;
5807c478bd9Sstevel@tonic-gate 					SETNOTREE(np);
5817c478bd9Sstevel@tonic-gate 
5827c478bd9Sstevel@tonic-gate 					break;
5837c478bd9Sstevel@tonic-gate 				}
5847c478bd9Sstevel@tonic-gate 			}
5857c478bd9Sstevel@tonic-gate 		} else
5867c478bd9Sstevel@tonic-gate 			Root = tp;
5877c478bd9Sstevel@tonic-gate 	}
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	/* tell next block that this one is free */
5907c478bd9Sstevel@tonic-gate 	SETBIT1(SIZE(NEXT(tp)));
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 	ASSERT(ISBIT0(SIZE(NEXT(tp))));
5937c478bd9Sstevel@tonic-gate }
5947c478bd9Sstevel@tonic-gate 
5957c478bd9Sstevel@tonic-gate /*
5967c478bd9Sstevel@tonic-gate  * Get more core. Gaps in memory are noted as busy blocks.
5977c478bd9Sstevel@tonic-gate  */
5987c478bd9Sstevel@tonic-gate static TREE *
_morecore(size_t size)5997c478bd9Sstevel@tonic-gate _morecore(size_t size)
6007c478bd9Sstevel@tonic-gate {
6017c478bd9Sstevel@tonic-gate 	TREE	*tp;
6027c478bd9Sstevel@tonic-gate 	ssize_t	n, offset;
6037c478bd9Sstevel@tonic-gate 	char	*addr;
6047c478bd9Sstevel@tonic-gate 	ssize_t	nsize;
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate 	/* compute new amount of memory to get */
6077c478bd9Sstevel@tonic-gate 	tp = Bottom;
6087c478bd9Sstevel@tonic-gate 	n = (ssize_t)size + 2 * WORDSIZE;
6097c478bd9Sstevel@tonic-gate 	addr = GETCORE(0);
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate 	if (addr == ERRCORE)
6127c478bd9Sstevel@tonic-gate 		return (NULL);
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	/* need to pad size out so that addr is aligned */
6157c478bd9Sstevel@tonic-gate 	if ((((uintptr_t)addr) % ALIGN) != 0)
6167c478bd9Sstevel@tonic-gate 		offset = ALIGN - (uintptr_t)addr % ALIGN;
6177c478bd9Sstevel@tonic-gate 	else
6187c478bd9Sstevel@tonic-gate 		offset = 0;
6197c478bd9Sstevel@tonic-gate 
6207c478bd9Sstevel@tonic-gate #ifndef SEGMENTED
6217c478bd9Sstevel@tonic-gate 	/* if not segmented memory, what we need may be smaller */
6227c478bd9Sstevel@tonic-gate 	if (addr == Baddr) {
6237c478bd9Sstevel@tonic-gate 		n -= WORDSIZE;
6247c478bd9Sstevel@tonic-gate 		if (tp != NULL)
6257c478bd9Sstevel@tonic-gate 			n -= SIZE(tp);
6267c478bd9Sstevel@tonic-gate 	}
6277c478bd9Sstevel@tonic-gate #endif
6287c478bd9Sstevel@tonic-gate 
6297c478bd9Sstevel@tonic-gate 	/* get a multiple of CORESIZE */
6307c478bd9Sstevel@tonic-gate 	n = ((n - 1) / CORESIZE + 1) * CORESIZE;
6317c478bd9Sstevel@tonic-gate 	nsize = n + offset;
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	/* check if nsize request could overflow in GETCORE */
6347c478bd9Sstevel@tonic-gate 	if ((size_t)nsize > MAX_MALLOC - (uintptr_t)addr) {
6357c478bd9Sstevel@tonic-gate 		errno = ENOMEM;
6367c478bd9Sstevel@tonic-gate 		return (NULL);
6377c478bd9Sstevel@tonic-gate 	}
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 	if ((size_t)nsize <= MAX_GETCORE) {
6407c478bd9Sstevel@tonic-gate 		if (GETCORE(nsize) == ERRCORE)
6417c478bd9Sstevel@tonic-gate 			return (NULL);
6427c478bd9Sstevel@tonic-gate 	} else {
6437c478bd9Sstevel@tonic-gate 		intptr_t	delta;
6447c478bd9Sstevel@tonic-gate 		/*
6457c478bd9Sstevel@tonic-gate 		 * the value required is too big for GETCORE() to deal with
6467c478bd9Sstevel@tonic-gate 		 * in one go, so use GETCORE() at most 2 times instead.
6477c478bd9Sstevel@tonic-gate 		 * Argument to GETCORE() must be multiple of ALIGN.
6487c478bd9Sstevel@tonic-gate 		 * If not, GETCORE(-MAX_GETCORE) will not return brk point
6497c478bd9Sstevel@tonic-gate 		 * to previous value, but will be ALIGN more.
6507c478bd9Sstevel@tonic-gate 		 * This would leave a small hole.
6517c478bd9Sstevel@tonic-gate 		 */
6527c478bd9Sstevel@tonic-gate 		delta = MAX_GETCORE;
6537c478bd9Sstevel@tonic-gate 		while (delta > 0) {
6547c478bd9Sstevel@tonic-gate 			if (GETCORE(delta) == ERRCORE) {
6557c478bd9Sstevel@tonic-gate 				if (addr != GETCORE(0))
6567c478bd9Sstevel@tonic-gate 					(void) GETCORE(-MAX_GETCORE);
6577c478bd9Sstevel@tonic-gate 				return (NULL);
6587c478bd9Sstevel@tonic-gate 			}
6597c478bd9Sstevel@tonic-gate 			nsize -= MAX_GETCORE;
6607c478bd9Sstevel@tonic-gate 			delta = nsize;
6617c478bd9Sstevel@tonic-gate 		}
6627c478bd9Sstevel@tonic-gate 	}
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 	/* contiguous memory */
6657c478bd9Sstevel@tonic-gate 	if (addr == Baddr) {
6667c478bd9Sstevel@tonic-gate 		ASSERT(offset == 0);
6677c478bd9Sstevel@tonic-gate 		if (tp) {
6687c478bd9Sstevel@tonic-gate 			addr = (char *)tp;
6697c478bd9Sstevel@tonic-gate 			n += SIZE(tp) + 2 * WORDSIZE;
6707c478bd9Sstevel@tonic-gate 		} else {
6717c478bd9Sstevel@tonic-gate 			addr = Baddr - WORDSIZE;
6727c478bd9Sstevel@tonic-gate 			n += WORDSIZE;
6737c478bd9Sstevel@tonic-gate 		}
6747c478bd9Sstevel@tonic-gate 	} else
6757c478bd9Sstevel@tonic-gate 		addr += offset;
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 	/* new bottom address */
6787c478bd9Sstevel@tonic-gate 	Baddr = addr + n;
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	/* new bottom block */
6817c478bd9Sstevel@tonic-gate 	tp = (TREE *)(uintptr_t)addr;
6827c478bd9Sstevel@tonic-gate 	SIZE(tp) = n - 2 * WORDSIZE;
6837c478bd9Sstevel@tonic-gate 	ASSERT((SIZE(tp) % ALIGN) == 0);
6847c478bd9Sstevel@tonic-gate 
6857c478bd9Sstevel@tonic-gate 	/* reserved the last word to head any noncontiguous memory */
6867c478bd9Sstevel@tonic-gate 	SETBIT0(SIZE(NEXT(tp)));
6877c478bd9Sstevel@tonic-gate 
6887c478bd9Sstevel@tonic-gate 	/* non-contiguous memory, free old bottom block */
6897c478bd9Sstevel@tonic-gate 	if (Bottom && Bottom != tp) {
6907c478bd9Sstevel@tonic-gate 		SETBIT0(SIZE(Bottom));
6917c478bd9Sstevel@tonic-gate 		realfree(DATA(Bottom));
6927c478bd9Sstevel@tonic-gate 	}
6937c478bd9Sstevel@tonic-gate 
6947c478bd9Sstevel@tonic-gate 	return (tp);
6957c478bd9Sstevel@tonic-gate }
6967c478bd9Sstevel@tonic-gate 
6977c478bd9Sstevel@tonic-gate 
6987c478bd9Sstevel@tonic-gate /*
6997c478bd9Sstevel@tonic-gate  * Tree rotation functions (BU: bottom-up, TD: top-down)
7007c478bd9Sstevel@tonic-gate  */
7017c478bd9Sstevel@tonic-gate 
7027c478bd9Sstevel@tonic-gate #define	LEFT1(x, y)		\
7037c478bd9Sstevel@tonic-gate 			if ((RIGHT(x) = LEFT(y)) != NULL) PARENT(RIGHT(x)) = x;\
7047c478bd9Sstevel@tonic-gate 			if ((PARENT(y) = PARENT(x)) != NULL)\
7057c478bd9Sstevel@tonic-gate 				if (LEFT(PARENT(x)) == x) LEFT(PARENT(y)) = y;\
7067c478bd9Sstevel@tonic-gate 				else RIGHT(PARENT(y)) = y;\
7077c478bd9Sstevel@tonic-gate 			LEFT(y) = x; PARENT(x) = y
7087c478bd9Sstevel@tonic-gate 
7097c478bd9Sstevel@tonic-gate #define	RIGHT1(x, y)		\
7107c478bd9Sstevel@tonic-gate 			if ((LEFT(x) = RIGHT(y)) != NULL) PARENT(LEFT(x)) = x;\
7117c478bd9Sstevel@tonic-gate 			if ((PARENT(y) = PARENT(x)) != NULL)\
7127c478bd9Sstevel@tonic-gate 				if (LEFT(PARENT(x)) == x) LEFT(PARENT(y)) = y;\
7137c478bd9Sstevel@tonic-gate 				else RIGHT(PARENT(y)) = y;\
7147c478bd9Sstevel@tonic-gate 			RIGHT(y) = x; PARENT(x) = y
7157c478bd9Sstevel@tonic-gate 
7167c478bd9Sstevel@tonic-gate #define	BULEFT2(x, y, z)	\
7177c478bd9Sstevel@tonic-gate 			if ((RIGHT(x) = LEFT(y)) != NULL) PARENT(RIGHT(x)) = x;\
7187c478bd9Sstevel@tonic-gate 			if ((RIGHT(y) = LEFT(z)) != NULL) PARENT(RIGHT(y)) = y;\
7197c478bd9Sstevel@tonic-gate 			if ((PARENT(z) = PARENT(x)) != NULL)\
7207c478bd9Sstevel@tonic-gate 				if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\
7217c478bd9Sstevel@tonic-gate 				else RIGHT(PARENT(z)) = z;\
7227c478bd9Sstevel@tonic-gate 			LEFT(z) = y; PARENT(y) = z; LEFT(y) = x; PARENT(x) = y
7237c478bd9Sstevel@tonic-gate 
7247c478bd9Sstevel@tonic-gate #define	BURIGHT2(x, y, z)	\
7257c478bd9Sstevel@tonic-gate 			if ((LEFT(x) = RIGHT(y)) != NULL) PARENT(LEFT(x)) = x;\
7267c478bd9Sstevel@tonic-gate 			if ((LEFT(y) = RIGHT(z)) != NULL) PARENT(LEFT(y)) = y;\
7277c478bd9Sstevel@tonic-gate 			if ((PARENT(z) = PARENT(x)) != NULL)\
7287c478bd9Sstevel@tonic-gate 				if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\
7297c478bd9Sstevel@tonic-gate 				else RIGHT(PARENT(z)) = z;\
7307c478bd9Sstevel@tonic-gate 			RIGHT(z) = y; PARENT(y) = z; RIGHT(y) = x; PARENT(x) = y
7317c478bd9Sstevel@tonic-gate 
7327c478bd9Sstevel@tonic-gate #define	TDLEFT2(x, y, z)	\
7337c478bd9Sstevel@tonic-gate 			if ((RIGHT(y) = LEFT(z)) != NULL) PARENT(RIGHT(y)) = y;\
7347c478bd9Sstevel@tonic-gate 			if ((PARENT(z) = PARENT(x)) != NULL)\
7357c478bd9Sstevel@tonic-gate 				if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\
7367c478bd9Sstevel@tonic-gate 				else RIGHT(PARENT(z)) = z;\
7377c478bd9Sstevel@tonic-gate 			PARENT(x) = z; LEFT(z) = x;
7387c478bd9Sstevel@tonic-gate 
7397c478bd9Sstevel@tonic-gate #define	TDRIGHT2(x, y, z)	\
7407c478bd9Sstevel@tonic-gate 			if ((LEFT(y) = RIGHT(z)) != NULL) PARENT(LEFT(y)) = y;\
7417c478bd9Sstevel@tonic-gate 			if ((PARENT(z) = PARENT(x)) != NULL)\
7427c478bd9Sstevel@tonic-gate 				if (LEFT(PARENT(x)) == x) LEFT(PARENT(z)) = z;\
7437c478bd9Sstevel@tonic-gate 				else RIGHT(PARENT(z)) = z;\
7447c478bd9Sstevel@tonic-gate 			PARENT(x) = z; RIGHT(z) = x;
7457c478bd9Sstevel@tonic-gate 
7467c478bd9Sstevel@tonic-gate /*
7477c478bd9Sstevel@tonic-gate  * Delete a tree element
7487c478bd9Sstevel@tonic-gate  */
7497c478bd9Sstevel@tonic-gate static void
t_delete(TREE * op)7507c478bd9Sstevel@tonic-gate t_delete(TREE *op)
7517c478bd9Sstevel@tonic-gate {
7527c478bd9Sstevel@tonic-gate 	TREE	*tp, *sp, *gp;
7537c478bd9Sstevel@tonic-gate 
7547c478bd9Sstevel@tonic-gate 	/* if this is a non-tree node */
7557c478bd9Sstevel@tonic-gate 	if (ISNOTREE(op)) {
7567c478bd9Sstevel@tonic-gate 		tp = LINKBAK(op);
7577c478bd9Sstevel@tonic-gate 		if ((sp = LINKFOR(op)) != NULL)
7587c478bd9Sstevel@tonic-gate 			LINKBAK(sp) = tp;
7597c478bd9Sstevel@tonic-gate 		LINKFOR(tp) = sp;
7607c478bd9Sstevel@tonic-gate 		return;
7617c478bd9Sstevel@tonic-gate 	}
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate 	/* make op the root of the tree */
7647c478bd9Sstevel@tonic-gate 	if (PARENT(op))
7657c478bd9Sstevel@tonic-gate 		t_splay(op);
7667c478bd9Sstevel@tonic-gate 
7677c478bd9Sstevel@tonic-gate 	/* if this is the start of a list */
7687c478bd9Sstevel@tonic-gate 	if ((tp = LINKFOR(op)) != NULL) {
7697c478bd9Sstevel@tonic-gate 		PARENT(tp) = NULL;
7707c478bd9Sstevel@tonic-gate 		if ((sp = LEFT(op)) != NULL)
7717c478bd9Sstevel@tonic-gate 			PARENT(sp) = tp;
7727c478bd9Sstevel@tonic-gate 		LEFT(tp) = sp;
7737c478bd9Sstevel@tonic-gate 
7747c478bd9Sstevel@tonic-gate 		if ((sp = RIGHT(op)) != NULL)
7757c478bd9Sstevel@tonic-gate 			PARENT(sp) = tp;
7767c478bd9Sstevel@tonic-gate 		RIGHT(tp) = sp;
7777c478bd9Sstevel@tonic-gate 
7787c478bd9Sstevel@tonic-gate 		Root = tp;
7797c478bd9Sstevel@tonic-gate 		return;
7807c478bd9Sstevel@tonic-gate 	}
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate 	/* if op has a non-null left subtree */
7837c478bd9Sstevel@tonic-gate 	if ((tp = LEFT(op)) != NULL) {
7847c478bd9Sstevel@tonic-gate 		PARENT(tp) = NULL;
7857c478bd9Sstevel@tonic-gate 
7867c478bd9Sstevel@tonic-gate 		if (RIGHT(op)) {
7877c478bd9Sstevel@tonic-gate 			/* make the right-end of the left subtree its root */
7887c478bd9Sstevel@tonic-gate 			while ((sp = RIGHT(tp)) != NULL) {
7897c478bd9Sstevel@tonic-gate 				if ((gp = RIGHT(sp)) != NULL) {
7907c478bd9Sstevel@tonic-gate 					TDLEFT2(tp, sp, gp);
7917c478bd9Sstevel@tonic-gate 					tp = gp;
7927c478bd9Sstevel@tonic-gate 				} else {
7937c478bd9Sstevel@tonic-gate 					LEFT1(tp, sp);
7947c478bd9Sstevel@tonic-gate 					tp = sp;
7957c478bd9Sstevel@tonic-gate 				}
7967c478bd9Sstevel@tonic-gate 			}
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 			/* hook the right subtree of op to the above elt */
7997c478bd9Sstevel@tonic-gate 			RIGHT(tp) = RIGHT(op);
8007c478bd9Sstevel@tonic-gate 			PARENT(RIGHT(tp)) = tp;
8017c478bd9Sstevel@tonic-gate 		}
8027c478bd9Sstevel@tonic-gate 	} else if ((tp = RIGHT(op)) != NULL)	/* no left subtree */
8037c478bd9Sstevel@tonic-gate 		PARENT(tp) = NULL;
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate 	Root = tp;
8067c478bd9Sstevel@tonic-gate }
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate /*
8097c478bd9Sstevel@tonic-gate  * Bottom up splaying (simple version).
8107c478bd9Sstevel@tonic-gate  * The basic idea is to roughly cut in half the
8117c478bd9Sstevel@tonic-gate  * path from Root to tp and make tp the new root.
8127c478bd9Sstevel@tonic-gate  */
8137c478bd9Sstevel@tonic-gate static void
t_splay(TREE * tp)8147c478bd9Sstevel@tonic-gate t_splay(TREE *tp)
8157c478bd9Sstevel@tonic-gate {
8167c478bd9Sstevel@tonic-gate 	TREE	*pp, *gp;
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 	/* iterate until tp is the root */
8197c478bd9Sstevel@tonic-gate 	while ((pp = PARENT(tp)) != NULL) {
8207c478bd9Sstevel@tonic-gate 		/* grandparent of tp */
8217c478bd9Sstevel@tonic-gate 		gp = PARENT(pp);
8227c478bd9Sstevel@tonic-gate 
8237c478bd9Sstevel@tonic-gate 		/* x is a left child */
8247c478bd9Sstevel@tonic-gate 		if (LEFT(pp) == tp) {
8257c478bd9Sstevel@tonic-gate 			if (gp && LEFT(gp) == pp) {
8267c478bd9Sstevel@tonic-gate 				BURIGHT2(gp, pp, tp);
8277c478bd9Sstevel@tonic-gate 			} else {
8287c478bd9Sstevel@tonic-gate 				RIGHT1(pp, tp);
8297c478bd9Sstevel@tonic-gate 			}
8307c478bd9Sstevel@tonic-gate 		} else {
8317c478bd9Sstevel@tonic-gate 			ASSERT(RIGHT(pp) == tp);
8327c478bd9Sstevel@tonic-gate 			if (gp && RIGHT(gp) == pp) {
8337c478bd9Sstevel@tonic-gate 				BULEFT2(gp, pp, tp);
8347c478bd9Sstevel@tonic-gate 			} else {
8357c478bd9Sstevel@tonic-gate 				LEFT1(pp, tp);
8367c478bd9Sstevel@tonic-gate 			}
8377c478bd9Sstevel@tonic-gate 		}
8387c478bd9Sstevel@tonic-gate 	}
8397c478bd9Sstevel@tonic-gate }
8407c478bd9Sstevel@tonic-gate 
8417c478bd9Sstevel@tonic-gate 
8427c478bd9Sstevel@tonic-gate /*
8437c478bd9Sstevel@tonic-gate  *	free().
8447c478bd9Sstevel@tonic-gate  *	Performs a delayed free of the block pointed to
8457c478bd9Sstevel@tonic-gate  *	by old. The pointer to old is saved on a list, flist,
8467c478bd9Sstevel@tonic-gate  *	until the next malloc or realloc. At that time, all the
8477c478bd9Sstevel@tonic-gate  *	blocks pointed to in flist are actually freed via
8487c478bd9Sstevel@tonic-gate  *	realfree(). This allows the contents of free blocks to
8497c478bd9Sstevel@tonic-gate  *	remain undisturbed until the next malloc or realloc.
8507c478bd9Sstevel@tonic-gate  */
8517c478bd9Sstevel@tonic-gate void
free(void * old)8527c478bd9Sstevel@tonic-gate free(void *old)
8537c478bd9Sstevel@tonic-gate {
8548cd45542Sraf 	if (!primary_link_map) {
8558cd45542Sraf 		errno = ENOTSUP;
8568cd45542Sraf 		return;
8578cd45542Sraf 	}
8587c478bd9Sstevel@tonic-gate 	assert_no_libc_locks_held();
8598cd45542Sraf 	(void) mutex_lock(&libc_malloc_lock);
8607c478bd9Sstevel@tonic-gate 	_free_unlocked(old);
8618cd45542Sraf 	(void) mutex_unlock(&libc_malloc_lock);
8627c478bd9Sstevel@tonic-gate }
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 
8657c478bd9Sstevel@tonic-gate void
_free_unlocked(void * old)8667c478bd9Sstevel@tonic-gate _free_unlocked(void *old)
8677c478bd9Sstevel@tonic-gate {
8687c478bd9Sstevel@tonic-gate 	int	i;
8697c478bd9Sstevel@tonic-gate 
8708cd45542Sraf 	if (old == NULL)
8717c478bd9Sstevel@tonic-gate 		return;
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	/*
8747c478bd9Sstevel@tonic-gate 	 * Make sure the same data block is not freed twice.
8757c478bd9Sstevel@tonic-gate 	 * 3 cases are checked.  It returns immediately if either
8767c478bd9Sstevel@tonic-gate 	 * one of the conditions is true.
8777c478bd9Sstevel@tonic-gate 	 *	1. Last freed.
8787c478bd9Sstevel@tonic-gate 	 *	2. Not in use or freed already.
8797c478bd9Sstevel@tonic-gate 	 *	3. In the free list.
8807c478bd9Sstevel@tonic-gate 	 */
8817c478bd9Sstevel@tonic-gate 	if (old == Lfree)
8827c478bd9Sstevel@tonic-gate 		return;
8837c478bd9Sstevel@tonic-gate 	if (!ISBIT0(SIZE(BLOCK(old))))
8847c478bd9Sstevel@tonic-gate 		return;
8857c478bd9Sstevel@tonic-gate 	for (i = 0; i < freeidx; i++)
8867c478bd9Sstevel@tonic-gate 		if (old == flist[i])
8877c478bd9Sstevel@tonic-gate 			return;
8887c478bd9Sstevel@tonic-gate 
8897c478bd9Sstevel@tonic-gate 	if (flist[freeidx] != NULL)
8907c478bd9Sstevel@tonic-gate 		realfree(flist[freeidx]);
8917c478bd9Sstevel@tonic-gate 	flist[freeidx] = Lfree = old;
8927c478bd9Sstevel@tonic-gate 	freeidx = (freeidx + 1) & FREEMASK; /* one forward */
8937c478bd9Sstevel@tonic-gate }
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate /*
8967c478bd9Sstevel@tonic-gate  * cleanfree() frees all the blocks pointed to be flist.
8977c478bd9Sstevel@tonic-gate  *
8987c478bd9Sstevel@tonic-gate  * realloc() should work if it is called with a pointer
8997c478bd9Sstevel@tonic-gate  * to a block that was freed since the last call to malloc() or
9007c478bd9Sstevel@tonic-gate  * realloc(). If cleanfree() is called from realloc(), ptr
9017c478bd9Sstevel@tonic-gate  * is set to the old block and that block should not be
9027c478bd9Sstevel@tonic-gate  * freed since it is actually being reallocated.
9037c478bd9Sstevel@tonic-gate  */
9047c478bd9Sstevel@tonic-gate static void
cleanfree(void * ptr)9057c478bd9Sstevel@tonic-gate cleanfree(void *ptr)
9067c478bd9Sstevel@tonic-gate {
9077c478bd9Sstevel@tonic-gate 	char	**flp;
9087c478bd9Sstevel@tonic-gate 
9097c478bd9Sstevel@tonic-gate 	flp = (char **)&(flist[freeidx]);
9107c478bd9Sstevel@tonic-gate 	for (;;) {
9117c478bd9Sstevel@tonic-gate 		if (flp == (char **)&(flist[0]))
9127c478bd9Sstevel@tonic-gate 			flp = (char **)&(flist[FREESIZE]);
9137c478bd9Sstevel@tonic-gate 		if (*--flp == NULL)
9147c478bd9Sstevel@tonic-gate 			break;
9157c478bd9Sstevel@tonic-gate 		if (*flp != ptr)
9167c478bd9Sstevel@tonic-gate 			realfree(*flp);
9177c478bd9Sstevel@tonic-gate 		*flp = NULL;
9187c478bd9Sstevel@tonic-gate 	}
9197c478bd9Sstevel@tonic-gate 	freeidx = 0;
9207c478bd9Sstevel@tonic-gate 	Lfree = NULL;
9217c478bd9Sstevel@tonic-gate }
922