Home | History | Annotate | Line # | Download | only in ld.elf_so
      1 /*	$NetBSD: xmalloc.c,v 1.28 2026/07/20 18:46:37 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright 1996 John D. Polstra.
      5  * Copyright 1996 Matt Thomas <matt (at) 3am-software.com>
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. All advertising materials mentioning features or use of this software
     17  *    must display the following acknowledgement:
     18  *      This product includes software developed by John Polstra.
     19  * 4. The name of the author may not be used to endorse or promote products
     20  *    derived from this software without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     27  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     31  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     32  */
     33 
     34 /*
     35  * Copyright (c) 1983 Regents of the University of California.
     36  * All rights reserved.
     37  *
     38  * Redistribution and use in source and binary forms, with or without
     39  * modification, are permitted provided that the following conditions
     40  * are met:
     41  * 1. Redistributions of source code must retain the above copyright
     42  *    notice, this list of conditions and the following disclaimer.
     43  * 2. Redistributions in binary form must reproduce the above copyright
     44  *    notice, this list of conditions and the following disclaimer in the
     45  *    documentation and/or other materials provided with the distribution.
     46  * 3. Neither the name of the University nor the names of its contributors
     47  *    may be used to endorse or promote products derived from this software
     48  *    without specific prior written permission.
     49  *
     50  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     51  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     53  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     54  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     55  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     56  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     57  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     58  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     59  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     60  * SUCH DAMAGE.
     61  */
     62 
     63 #if defined(LIBC_SCCS) && !defined(lint)
     64 /*static char *sccsid = "from: @(#)malloc.c	5.11 (Berkeley) 2/23/91";*/
     65 #endif /* LIBC_SCCS and not lint */
     66 
     67 /*
     68  * malloc.c (Caltech) 2/21/82
     69  * Chris Kingsley, kingsley@cit-20.
     70  *
     71  * This is a very fast storage allocator.  It allocates blocks of a small
     72  * number of different sizes, and keeps free lists of each size.  Blocks that
     73  * don't exactly fit are passed up to the next larger size.  In this
     74  * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long.
     75  * This is designed for use in a virtual memory environment.
     76  */
     77 
     78 #include <sys/cdefs.h>
     79 #ifndef lint
     80 __RCSID("$NetBSD: xmalloc.c,v 1.28 2026/07/20 18:46:37 riastradh Exp $");
     81 #endif /* not lint */
     82 
     83 #include <stdlib.h>
     84 #include <string.h>
     85 #include <unistd.h>
     86 #include <errno.h>
     87 
     88 #include <sys/types.h>
     89 #include <sys/param.h>
     90 #include <sys/mman.h>
     91 #include <sys/stat.h>
     92 
     93 #include "rtld.h"
     94 
     95 /*
     96  * Pre-allocate mmap'ed pages
     97  */
     98 #define	NPOOLPAGES	(32 * 1024 / pagesz)
     99 static char 		*pagepool_start, *pagepool_end;
    100 #define PAGEPOOL_SIZE	(size_t)(pagepool_end - pagepool_start)
    101 
    102 /*
    103  * The overhead on a block is at least 4 bytes.  When free, this space
    104  * contains a pointer to the next free block, and the bottom two bits must
    105  * be zero.  When in use, the first byte is set to MAGIC, and the second
    106  * byte is the size index.  The remaining bytes are for alignment.
    107  */
    108 union	overhead {
    109 	union	overhead *ov_next;	/* when free */
    110 	struct {
    111 		uint16_t	ovu_index;	/* bucket # */
    112 		uint8_t		ovu_magic;	/* magic number */
    113 	} ovu;
    114 #define	ov_magic	ovu.ovu_magic
    115 #define	ov_index	ovu.ovu_index
    116 };
    117 
    118 static void morecore(size_t);
    119 static int morepages(int);
    120 static void *imalloc(size_t);
    121 
    122 #define	MAGIC		0xef		/* magic # on accounting info */
    123 #define	AMAGIC		0xdf		/* magic # for aligned alloc */
    124 
    125 
    126 /*
    127  * nextf[i] is the pointer to the next free block of size
    128  * (FIRST_BUCKET_SIZE << i).  The overhead information precedes the data
    129  * area returned to the user.
    130  */
    131 #define	FIRST_BUCKET_SHIFT	3
    132 #define	FIRST_BUCKET_SIZE	(1U << FIRST_BUCKET_SHIFT)
    133 #define	NBUCKETS 30
    134 static	union overhead *nextf[NBUCKETS];
    135 
    136 static	size_t pagesz;			/* page size */
    137 static	size_t pageshift;		/* page size shift */
    138 
    139 #ifdef MSTATS
    140 /*
    141  * nmalloc[i] is the difference between the number of mallocs and frees
    142  * for a given block size.
    143  */
    144 static	u_int nmalloc[NBUCKETS];
    145 #endif
    146 
    147 #if defined(MALLOC_DEBUG)
    148 #define	ASSERT(p)	(__predict_false(!(p)) ? botch(#p) : (void)0)
    149 static void
    150 botch(const char *s)
    151 {
    152 	xwarnx("\r\nassertion botched: %s\r\n", s);
    153 	abort();
    154 }
    155 #else
    156 #define	ASSERT(p)	__nothing
    157 #endif
    158 
    159 #define TRACE()	xprintf("TRACE %s:%d\n", __FILE__, __LINE__)
    160 
    161 static void *
    162 cp2op(void *cp)
    163 {
    164 	return (((caddr_t)cp - sizeof(union overhead)));
    165 }
    166 
    167 static void *
    168 imalloc(size_t nbytes)
    169 {
    170 	union overhead *op;
    171 	size_t bucket;
    172 	size_t amt;
    173 
    174 	/*
    175 	 * First time malloc is called, setup page size.
    176 	 */
    177 	if (pagesz == 0) {
    178 		size_t n, m;
    179 		pagesz = n = _rtld_pagesz;
    180 		pageshift = ffs(pagesz) - 1;
    181 		if (morepages(NPOOLPAGES) == 0)
    182 			return NULL;
    183 		op = (union overhead *)(pagepool_start);
    184 		m = sizeof (*op) - (((char *)op - (char *)NULL) & (n - 1));
    185 		if (n < m)
    186 			n += pagesz - m;
    187 		else
    188 			n -= m;
    189 		if (n) {
    190 			pagepool_start += n;
    191 		}
    192 	}
    193 
    194 	/*
    195 	 * Convert amount of memory requested into closest block size
    196 	 * stored in hash buckets which satisfies request.
    197 	 * Account for space used per block for accounting.
    198 	 */
    199 	amt = FIRST_BUCKET_SIZE;
    200 	bucket = 0;
    201 	while (nbytes > amt - sizeof(*op)) {
    202 		amt <<= 1;
    203 		bucket++;
    204 		if (amt == 0 || bucket >= NBUCKETS)
    205 			return (NULL);
    206 	}
    207 	/*
    208 	 * If nothing in hash bucket right now,
    209 	 * request more memory from the system.
    210 	 */
    211 	if ((op = nextf[bucket]) == NULL) {
    212 		morecore(bucket);
    213 		if ((op = nextf[bucket]) == NULL)
    214 			return (NULL);
    215 	}
    216 	/* remove from linked list */
    217 	nextf[bucket] = op->ov_next;
    218 	op->ov_magic = MAGIC;
    219 	op->ov_index = bucket;
    220 #ifdef MSTATS
    221 	nmalloc[bucket]++;
    222 #endif
    223 	return ((char *)(op + 1));
    224 }
    225 
    226 void *
    227 xmalloc_aligned(size_t size, size_t align, size_t offset)
    228 {
    229 	void *mem;
    230 	union overhead *ov;
    231 	uintptr_t x;
    232 
    233 	ASSERT((align & (align - 1)) == 0);
    234 
    235 	if (align < FIRST_BUCKET_SIZE)
    236 		align = FIRST_BUCKET_SIZE;
    237 	offset &= align - 1;
    238 	mem = imalloc(size + align + offset + sizeof(union overhead));
    239 	if (mem == NULL)
    240 		return (NULL);
    241 	x = roundup2((uintptr_t)mem + sizeof(union overhead), align);
    242 	x += offset;
    243 	ov = cp2op((void *)x);
    244 	ov->ov_magic = AMAGIC;
    245 	ov->ov_index = x - (uintptr_t)mem + sizeof(union overhead);
    246 
    247 	return ((void *)x);
    248 }
    249 
    250 /*
    251  * Allocate more memory to the indicated bucket.
    252  */
    253 static void
    254 morecore(size_t bucket)
    255 {
    256 	union overhead *op;
    257 	size_t sz;		/* size of desired block */
    258 	size_t amt;		/* amount to allocate */
    259 	size_t nblks;		/* how many blocks we get */
    260 
    261 	sz = FIRST_BUCKET_SIZE << bucket;
    262 	if (sz < pagesz) {
    263 		amt = pagesz;
    264 		nblks = amt >> (bucket + FIRST_BUCKET_SHIFT);
    265 	} else {
    266 		amt = sz;
    267 		nblks = 1;
    268 	}
    269 	if (amt > PAGEPOOL_SIZE)
    270 		if (morepages((amt >> pageshift) + NPOOLPAGES) == 0)
    271 			return;
    272 	op = (union overhead *)pagepool_start;
    273 	pagepool_start += amt;
    274 
    275 	/*
    276 	 * Add new memory allocated to that on
    277 	 * free list for this hash bucket.
    278 	 */
    279 	nextf[bucket] = op;
    280 	while (--nblks > 0) {
    281 		op->ov_next = (union overhead *)((caddr_t)op + sz);
    282 		op = (union overhead *)((caddr_t)op + sz);
    283 	}
    284 }
    285 
    286 void
    287 xfree(void *cp)
    288 {
    289 	union overhead *op, *opx;
    290 	size_t size;
    291 
    292 	if (cp == NULL)
    293 		return;
    294 	opx = cp2op(cp);
    295 
    296 	op = opx->ov_magic == AMAGIC ? (void *)((caddr_t)cp - opx->ov_index) :
    297 	    opx;
    298 	ASSERT(op->ov_magic == MAGIC);		/* make sure it was in use */
    299 	if (op->ov_magic != MAGIC)
    300 		return;				/* sanity */
    301 	size = op->ov_index;
    302 	ASSERT(size < NBUCKETS);
    303 	op->ov_next = nextf[size];		/* also clobbers ov_magic */
    304 	nextf[size] = op;
    305 #ifdef MSTATS
    306 	nmalloc[size]--;
    307 #endif
    308 }
    309 
    310 static void *
    311 irealloc(void *cp, size_t nbytes)
    312 {
    313 	size_t onb;
    314 	size_t i;
    315 	union overhead *op;
    316 	char *res;
    317 
    318 	if (cp == NULL)
    319 		return (imalloc(nbytes));
    320 	op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
    321 	if (op->ov_magic != MAGIC) {
    322 		static const char *err_str =
    323 		    "memory corruption or double free in realloc\n";
    324 		extern char *__progname;
    325 		write(STDERR_FILENO, __progname, strlen(__progname));
    326 		write(STDERR_FILENO, err_str, strlen(err_str));
    327 		abort();
    328 	}
    329 
    330 	i = op->ov_index;
    331 	onb = 1 << (i + FIRST_BUCKET_SHIFT);
    332 	if (onb < pagesz)
    333 		onb -= sizeof (*op);
    334 	else
    335 		onb += pagesz - sizeof (*op);
    336 	/* avoid the copy if same size block */
    337 	if (i) {
    338 		i = 1 << (i + 2);
    339 		if (i < pagesz)
    340 			i -= sizeof (*op);
    341 		else
    342 			i += pagesz - sizeof (*op);
    343 	}
    344 	if (nbytes <= onb && nbytes > i) {
    345 		return(cp);
    346 	}
    347 	if ((res = imalloc(nbytes)) == NULL)
    348 		return (NULL);
    349 	if (cp != res) {	/* common optimization if "compacting" */
    350 		memcpy(res, cp, (nbytes < onb) ? nbytes : onb);
    351 		xfree(cp);
    352 	}
    353 	return (res);
    354 }
    355 
    356 #ifdef MSTATS
    357 /*
    358  * mstats - print out statistics about malloc
    359  *
    360  * Prints two lines of numbers, one showing the length of the free list
    361  * for each size category, the second showing the number of mallocs -
    362  * frees for each size category.
    363  */
    364 void
    365 mstats(char *s)
    366 {
    367 	int i, j;
    368 	union overhead *p;
    369 	int totfree = 0,
    370 	totused = 0;
    371 
    372 	xprintf("Memory allocation statistics %s\nfree:\t", s);
    373 	for (i = 0; i < NBUCKETS; i++) {
    374 		for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
    375 			;
    376 		xprintf(" %d", j);
    377 		totfree += j * (1 << (i + FIRST_BUCKET_SHIFT));
    378 	}
    379 	xprintf("\nused:\t");
    380 	for (i = 0; i < NBUCKETS; i++) {
    381 		xprintf(" %d", nmalloc[i]);
    382 		totused += nmalloc[i] * (1 << (i + FIRST_BUCKET_SHIFT));
    383 	}
    384 	xprintf("\n\tTotal in use: %d, total free: %d\n",
    385 	    totused, totfree);
    386 }
    387 #endif
    388 
    389 
    390 static int
    391 morepages(int n)
    392 {
    393 	int	fd = -1;
    394 	int	offset;
    395 
    396 #ifdef NEED_DEV_ZERO
    397 	fd = open("/dev/zero", O_RDWR, 0);
    398 	if (fd == -1)
    399 		xerr(1, "/dev/zero");
    400 #endif
    401 
    402 	if (PAGEPOOL_SIZE > pagesz) {
    403 		caddr_t	addr = (caddr_t)
    404 			(((long)pagepool_start + pagesz - 1) & ~(pagesz - 1));
    405 		if (munmap(addr, pagepool_end - addr) != 0)
    406 			xwarn("morepages: munmap %p", addr);
    407 	}
    408 
    409 	offset = (long)pagepool_start - ((long)pagepool_start & ~(pagesz - 1));
    410 
    411 	if ((pagepool_start = mmap(0, n * pagesz,
    412 			PROT_READ|PROT_WRITE,
    413 			MAP_ANON|MAP_PRIVATE, fd, 0)) == (caddr_t)-1) {
    414 		xprintf("Cannot map anonymous memory");
    415 		return 0;
    416 	}
    417 	pagepool_end = pagepool_start + n * pagesz;
    418 	pagepool_start += offset;
    419 
    420 #ifdef NEED_DEV_ZERO
    421 	close(fd);
    422 #endif
    423 	return n;
    424 }
    425 
    426 void *
    427 xcalloc(size_t size)
    428 {
    429 
    430 	return memset(xmalloc(size), 0, size);
    431 }
    432 
    433 void *
    434 xmalloc(size_t size)
    435 {
    436 	void *p = imalloc(size);
    437 
    438 	if (p == NULL)
    439 		xerr(1, "%s", xstrerror(errno));
    440 	return p;
    441 }
    442 
    443 void *
    444 xrealloc(void *p, size_t size)
    445 {
    446 	p = irealloc(p, size);
    447 
    448 	if (p == NULL)
    449 		xerr(1, "%s", xstrerror(errno));
    450 	return p;
    451 }
    452 
    453 char *
    454 xstrdup(const char *str)
    455 {
    456 	size_t len;
    457 	char *copy;
    458 
    459 	len = strlen(str) + 1;
    460 	copy = xmalloc(len);
    461 	memcpy(copy, str, len);
    462 	return (copy);
    463 }
    464