1d49ca25dSKonstantin Belousov /*- 2d49ca25dSKonstantin Belousov * SPDX-License-Identifier: BSD-3-Clause 3d49ca25dSKonstantin Belousov * 4d49ca25dSKonstantin Belousov * Copyright (c) 1983 Regents of the University of California. 5d49ca25dSKonstantin Belousov * All rights reserved. 6d49ca25dSKonstantin Belousov * 7d49ca25dSKonstantin Belousov * Redistribution and use in source and binary forms, with or without 8d49ca25dSKonstantin Belousov * modification, are permitted provided that the following conditions 9d49ca25dSKonstantin Belousov * are met: 10d49ca25dSKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 11d49ca25dSKonstantin Belousov * notice, this list of conditions and the following disclaimer. 12d49ca25dSKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 13d49ca25dSKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 14d49ca25dSKonstantin Belousov * documentation and/or other materials provided with the distribution. 15d49ca25dSKonstantin Belousov * 3. Neither the name of the University nor the names of its contributors 16d49ca25dSKonstantin Belousov * may be used to endorse or promote products derived from this software 17d49ca25dSKonstantin Belousov * without specific prior written permission. 18d49ca25dSKonstantin Belousov * 19d49ca25dSKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20d49ca25dSKonstantin Belousov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21d49ca25dSKonstantin Belousov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22d49ca25dSKonstantin Belousov * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23d49ca25dSKonstantin Belousov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24d49ca25dSKonstantin Belousov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25d49ca25dSKonstantin Belousov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26d49ca25dSKonstantin Belousov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27d49ca25dSKonstantin Belousov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28d49ca25dSKonstantin Belousov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29d49ca25dSKonstantin Belousov * SUCH DAMAGE. 30d49ca25dSKonstantin Belousov */ 31d49ca25dSKonstantin Belousov 32d49ca25dSKonstantin Belousov #if defined(LIBC_SCCS) && !defined(lint) 33d49ca25dSKonstantin Belousov /*static char *sccsid = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91";*/ 34d49ca25dSKonstantin Belousov static char *rcsid = "$FreeBSD$"; 35d49ca25dSKonstantin Belousov #endif /* LIBC_SCCS and not lint */ 36d49ca25dSKonstantin Belousov 37d49ca25dSKonstantin Belousov /* 38d49ca25dSKonstantin Belousov * malloc.c (Caltech) 2/21/82 39d49ca25dSKonstantin Belousov * Chris Kingsley, kingsley@cit-20. 40d49ca25dSKonstantin Belousov * 41d49ca25dSKonstantin Belousov * This is a very fast storage allocator. It allocates blocks of a small 42d49ca25dSKonstantin Belousov * number of different sizes, and keeps free lists of each size. Blocks that 43d49ca25dSKonstantin Belousov * don't exactly fit are passed up to the next larger size. In this 44d49ca25dSKonstantin Belousov * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long. 45d49ca25dSKonstantin Belousov * This is designed for use in a virtual memory environment. 46d49ca25dSKonstantin Belousov */ 47d49ca25dSKonstantin Belousov 485cac2021SKonstantin Belousov #include <sys/param.h> 49d49ca25dSKonstantin Belousov #include <sys/sysctl.h> 505cac2021SKonstantin Belousov #include <sys/mman.h> 51d49ca25dSKonstantin Belousov #include <errno.h> 52d49ca25dSKonstantin Belousov #include <stdarg.h> 53d49ca25dSKonstantin Belousov #include <stddef.h> 54d49ca25dSKonstantin Belousov #include <string.h> 55d49ca25dSKonstantin Belousov #include <unistd.h> 56d49ca25dSKonstantin Belousov #include "rtld.h" 57d49ca25dSKonstantin Belousov #include "rtld_printf.h" 58d49ca25dSKonstantin Belousov #include "paths.h" 59d49ca25dSKonstantin Belousov 60d49ca25dSKonstantin Belousov /* 61d49ca25dSKonstantin Belousov * Pre-allocate mmap'ed pages 62d49ca25dSKonstantin Belousov */ 63d49ca25dSKonstantin Belousov #define NPOOLPAGES (128*1024/pagesz) 64d49ca25dSKonstantin Belousov static caddr_t pagepool_start, pagepool_end; 65d49ca25dSKonstantin Belousov 66d49ca25dSKonstantin Belousov /* 67d49ca25dSKonstantin Belousov * The overhead on a block is at least 4 bytes. When free, this space 68d49ca25dSKonstantin Belousov * contains a pointer to the next free block, and the bottom two bits must 69d49ca25dSKonstantin Belousov * be zero. When in use, the first byte is set to MAGIC, and the second 70d49ca25dSKonstantin Belousov * byte is the size index. The remaining bytes are for alignment. 71d49ca25dSKonstantin Belousov * If range checking is enabled then a second word holds the size of the 72d49ca25dSKonstantin Belousov * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC). 73d49ca25dSKonstantin Belousov * The order of elements is critical: ov_magic must overlay the low order 74d49ca25dSKonstantin Belousov * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern. 75d49ca25dSKonstantin Belousov */ 76d49ca25dSKonstantin Belousov union overhead { 77d49ca25dSKonstantin Belousov union overhead *ov_next; /* when free */ 78d49ca25dSKonstantin Belousov struct { 79d49ca25dSKonstantin Belousov u_char ovu_magic; /* magic number */ 80d49ca25dSKonstantin Belousov u_char ovu_index; /* bucket # */ 81d49ca25dSKonstantin Belousov } ovu; 82d49ca25dSKonstantin Belousov #define ov_magic ovu.ovu_magic 83d49ca25dSKonstantin Belousov #define ov_index ovu.ovu_index 84d49ca25dSKonstantin Belousov }; 85d49ca25dSKonstantin Belousov 86d49ca25dSKonstantin Belousov static void morecore(int bucket); 87d49ca25dSKonstantin Belousov static int morepages(int n); 88d49ca25dSKonstantin Belousov 89d49ca25dSKonstantin Belousov #define MAGIC 0xef /* magic # on accounting info */ 90d49ca25dSKonstantin Belousov 91d49ca25dSKonstantin Belousov /* 92d49ca25dSKonstantin Belousov * nextf[i] is the pointer to the next free block of size 2^(i+3). The 93d49ca25dSKonstantin Belousov * smallest allocatable block is 8 bytes. The overhead information 94d49ca25dSKonstantin Belousov * precedes the data area returned to the user. 95d49ca25dSKonstantin Belousov */ 96d49ca25dSKonstantin Belousov #define NBUCKETS 30 97d49ca25dSKonstantin Belousov static union overhead *nextf[NBUCKETS]; 98d49ca25dSKonstantin Belousov 99d49ca25dSKonstantin Belousov static int pagesz; /* page size */ 100d49ca25dSKonstantin Belousov static int pagebucket; /* page size bucket */ 101d49ca25dSKonstantin Belousov 102d49ca25dSKonstantin Belousov /* 103d49ca25dSKonstantin Belousov * The array of supported page sizes is provided by the user, i.e., the 104d49ca25dSKonstantin Belousov * program that calls this storage allocator. That program must initialize 105d49ca25dSKonstantin Belousov * the array before making its first call to allocate storage. The array 106d49ca25dSKonstantin Belousov * must contain at least one page size. The page sizes must be stored in 107d49ca25dSKonstantin Belousov * increasing order. 108d49ca25dSKonstantin Belousov */ 109d49ca25dSKonstantin Belousov 110d49ca25dSKonstantin Belousov void * 111d49ca25dSKonstantin Belousov __crt_malloc(size_t nbytes) 112d49ca25dSKonstantin Belousov { 113d49ca25dSKonstantin Belousov union overhead *op; 114d49ca25dSKonstantin Belousov int bucket; 115d49ca25dSKonstantin Belousov ssize_t n; 116d49ca25dSKonstantin Belousov size_t amt; 117d49ca25dSKonstantin Belousov 118d49ca25dSKonstantin Belousov /* 119d49ca25dSKonstantin Belousov * First time malloc is called, setup page size and 120d49ca25dSKonstantin Belousov * align break pointer so all data will be page aligned. 121d49ca25dSKonstantin Belousov */ 122d49ca25dSKonstantin Belousov if (pagesz == 0) { 123d49ca25dSKonstantin Belousov pagesz = n = pagesizes[0]; 124d49ca25dSKonstantin Belousov if (morepages(NPOOLPAGES) == 0) 125d49ca25dSKonstantin Belousov return NULL; 126d49ca25dSKonstantin Belousov op = (union overhead *)(pagepool_start); 127d49ca25dSKonstantin Belousov n = n - sizeof (*op) - ((long)op & (n - 1)); 128d49ca25dSKonstantin Belousov if (n < 0) 129d49ca25dSKonstantin Belousov n += pagesz; 130d49ca25dSKonstantin Belousov if (n) { 131d49ca25dSKonstantin Belousov pagepool_start += n; 132d49ca25dSKonstantin Belousov } 133d49ca25dSKonstantin Belousov bucket = 0; 134d49ca25dSKonstantin Belousov amt = 8; 135d49ca25dSKonstantin Belousov while ((unsigned)pagesz > amt) { 136d49ca25dSKonstantin Belousov amt <<= 1; 137d49ca25dSKonstantin Belousov bucket++; 138d49ca25dSKonstantin Belousov } 139d49ca25dSKonstantin Belousov pagebucket = bucket; 140d49ca25dSKonstantin Belousov } 141d49ca25dSKonstantin Belousov /* 142d49ca25dSKonstantin Belousov * Convert amount of memory requested into closest block size 143d49ca25dSKonstantin Belousov * stored in hash buckets which satisfies request. 144d49ca25dSKonstantin Belousov * Account for space used per block for accounting. 145d49ca25dSKonstantin Belousov */ 1465cac2021SKonstantin Belousov if (nbytes <= (unsigned long)(n = pagesz - sizeof(*op))) { 147d49ca25dSKonstantin Belousov amt = 8; /* size of first bucket */ 148d49ca25dSKonstantin Belousov bucket = 0; 1495cac2021SKonstantin Belousov n = -sizeof(*op); 150d49ca25dSKonstantin Belousov } else { 151d49ca25dSKonstantin Belousov amt = pagesz; 152d49ca25dSKonstantin Belousov bucket = pagebucket; 153d49ca25dSKonstantin Belousov } 154d49ca25dSKonstantin Belousov while (nbytes > amt + n) { 155d49ca25dSKonstantin Belousov amt <<= 1; 156d49ca25dSKonstantin Belousov if (amt == 0) 157d49ca25dSKonstantin Belousov return (NULL); 158d49ca25dSKonstantin Belousov bucket++; 159d49ca25dSKonstantin Belousov } 160d49ca25dSKonstantin Belousov /* 161d49ca25dSKonstantin Belousov * If nothing in hash bucket right now, 162d49ca25dSKonstantin Belousov * request more memory from the system. 163d49ca25dSKonstantin Belousov */ 164d49ca25dSKonstantin Belousov if ((op = nextf[bucket]) == NULL) { 165d49ca25dSKonstantin Belousov morecore(bucket); 166d49ca25dSKonstantin Belousov if ((op = nextf[bucket]) == NULL) 167d49ca25dSKonstantin Belousov return (NULL); 168d49ca25dSKonstantin Belousov } 169d49ca25dSKonstantin Belousov /* remove from linked list */ 170d49ca25dSKonstantin Belousov nextf[bucket] = op->ov_next; 171d49ca25dSKonstantin Belousov op->ov_magic = MAGIC; 172d49ca25dSKonstantin Belousov op->ov_index = bucket; 173d49ca25dSKonstantin Belousov return ((char *)(op + 1)); 174d49ca25dSKonstantin Belousov } 175d49ca25dSKonstantin Belousov 176d49ca25dSKonstantin Belousov void * 177d49ca25dSKonstantin Belousov __crt_calloc(size_t num, size_t size) 178d49ca25dSKonstantin Belousov { 179d49ca25dSKonstantin Belousov void *ret; 180d49ca25dSKonstantin Belousov 181d49ca25dSKonstantin Belousov if (size != 0 && (num * size) / size != num) { 182d49ca25dSKonstantin Belousov /* size_t overflow. */ 183d49ca25dSKonstantin Belousov return (NULL); 184d49ca25dSKonstantin Belousov } 185d49ca25dSKonstantin Belousov 186d49ca25dSKonstantin Belousov if ((ret = __crt_malloc(num * size)) != NULL) 187d49ca25dSKonstantin Belousov memset(ret, 0, num * size); 188d49ca25dSKonstantin Belousov 189d49ca25dSKonstantin Belousov return (ret); 190d49ca25dSKonstantin Belousov } 191d49ca25dSKonstantin Belousov 192d49ca25dSKonstantin Belousov /* 193d49ca25dSKonstantin Belousov * Allocate more memory to the indicated bucket. 194d49ca25dSKonstantin Belousov */ 195d49ca25dSKonstantin Belousov static void 196d49ca25dSKonstantin Belousov morecore(int bucket) 197d49ca25dSKonstantin Belousov { 198d49ca25dSKonstantin Belousov union overhead *op; 199d49ca25dSKonstantin Belousov int sz; /* size of desired block */ 200d49ca25dSKonstantin Belousov int amt; /* amount to allocate */ 201d49ca25dSKonstantin Belousov int nblks; /* how many blocks we get */ 202d49ca25dSKonstantin Belousov 203d49ca25dSKonstantin Belousov /* 204d49ca25dSKonstantin Belousov * sbrk_size <= 0 only for big, FLUFFY, requests (about 205d49ca25dSKonstantin Belousov * 2^30 bytes on a VAX, I think) or for a negative arg. 206d49ca25dSKonstantin Belousov */ 2075cac2021SKonstantin Belousov if ((unsigned)bucket >= NBBY * sizeof(int) - 4) 208d49ca25dSKonstantin Belousov return; 2095cac2021SKonstantin Belousov sz = 1 << (bucket + 3); 210d49ca25dSKonstantin Belousov if (sz < pagesz) { 211d49ca25dSKonstantin Belousov amt = pagesz; 212d49ca25dSKonstantin Belousov nblks = amt / sz; 213d49ca25dSKonstantin Belousov } else { 214d49ca25dSKonstantin Belousov amt = sz + pagesz; 215d49ca25dSKonstantin Belousov nblks = 1; 216d49ca25dSKonstantin Belousov } 217d49ca25dSKonstantin Belousov if (amt > pagepool_end - pagepool_start) 218d49ca25dSKonstantin Belousov if (morepages(amt/pagesz + NPOOLPAGES) == 0) 219d49ca25dSKonstantin Belousov return; 220d49ca25dSKonstantin Belousov op = (union overhead *)pagepool_start; 221d49ca25dSKonstantin Belousov pagepool_start += amt; 222d49ca25dSKonstantin Belousov 223d49ca25dSKonstantin Belousov /* 224d49ca25dSKonstantin Belousov * Add new memory allocated to that on 225d49ca25dSKonstantin Belousov * free list for this hash bucket. 226d49ca25dSKonstantin Belousov */ 227d49ca25dSKonstantin Belousov nextf[bucket] = op; 228d49ca25dSKonstantin Belousov while (--nblks > 0) { 229d49ca25dSKonstantin Belousov op->ov_next = (union overhead *)((caddr_t)op + sz); 230d49ca25dSKonstantin Belousov op = (union overhead *)((caddr_t)op + sz); 231d49ca25dSKonstantin Belousov } 232d49ca25dSKonstantin Belousov } 233d49ca25dSKonstantin Belousov 234d49ca25dSKonstantin Belousov void 235d49ca25dSKonstantin Belousov __crt_free(void *cp) 236d49ca25dSKonstantin Belousov { 237d49ca25dSKonstantin Belousov int size; 238d49ca25dSKonstantin Belousov union overhead *op; 239d49ca25dSKonstantin Belousov 240d49ca25dSKonstantin Belousov if (cp == NULL) 241d49ca25dSKonstantin Belousov return; 242d49ca25dSKonstantin Belousov op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 243d49ca25dSKonstantin Belousov if (op->ov_magic != MAGIC) 244d49ca25dSKonstantin Belousov return; /* sanity */ 245d49ca25dSKonstantin Belousov size = op->ov_index; 246d49ca25dSKonstantin Belousov op->ov_next = nextf[size]; /* also clobbers ov_magic */ 247d49ca25dSKonstantin Belousov nextf[size] = op; 248d49ca25dSKonstantin Belousov } 249d49ca25dSKonstantin Belousov 250d49ca25dSKonstantin Belousov void * 251d49ca25dSKonstantin Belousov __crt_realloc(void *cp, size_t nbytes) 252d49ca25dSKonstantin Belousov { 253d49ca25dSKonstantin Belousov u_int onb; 254d49ca25dSKonstantin Belousov int i; 255d49ca25dSKonstantin Belousov union overhead *op; 256d49ca25dSKonstantin Belousov char *res; 257d49ca25dSKonstantin Belousov 258d49ca25dSKonstantin Belousov if (cp == NULL) 259d49ca25dSKonstantin Belousov return (__crt_malloc(nbytes)); 260d49ca25dSKonstantin Belousov op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); 261*98ab7906SBrooks Davis if (op->ov_magic != MAGIC) 262*98ab7906SBrooks Davis return (NULL); /* Double-free or bad argument */ 263d49ca25dSKonstantin Belousov i = op->ov_index; 264d49ca25dSKonstantin Belousov onb = 1 << (i + 3); 265d49ca25dSKonstantin Belousov if (onb < (u_int)pagesz) 2665cac2021SKonstantin Belousov onb -= sizeof(*op); 267d49ca25dSKonstantin Belousov else 2685cac2021SKonstantin Belousov onb += pagesz - sizeof(*op); 269d49ca25dSKonstantin Belousov /* avoid the copy if same size block */ 270*98ab7906SBrooks Davis if (i != 0) { 271d49ca25dSKonstantin Belousov i = 1 << (i + 2); 272d49ca25dSKonstantin Belousov if (i < pagesz) 2735cac2021SKonstantin Belousov i -= sizeof(*op); 274d49ca25dSKonstantin Belousov else 2755cac2021SKonstantin Belousov i += pagesz - sizeof(*op); 276d49ca25dSKonstantin Belousov } 2775cac2021SKonstantin Belousov if (nbytes <= onb && nbytes > (size_t)i) 278d49ca25dSKonstantin Belousov return (cp); 279d49ca25dSKonstantin Belousov if ((res = __crt_malloc(nbytes)) == NULL) 280d49ca25dSKonstantin Belousov return (NULL); 281d49ca25dSKonstantin Belousov bcopy(cp, res, (nbytes < onb) ? nbytes : onb); 282*98ab7906SBrooks Davis __crt_free(cp); 283d49ca25dSKonstantin Belousov return (res); 284d49ca25dSKonstantin Belousov } 285d49ca25dSKonstantin Belousov 286d49ca25dSKonstantin Belousov static int 287d49ca25dSKonstantin Belousov morepages(int n) 288d49ca25dSKonstantin Belousov { 2893cac4083SKonstantin Belousov caddr_t addr; 290d49ca25dSKonstantin Belousov int offset; 291d49ca25dSKonstantin Belousov 292d49ca25dSKonstantin Belousov if (pagepool_end - pagepool_start > pagesz) { 2933cac4083SKonstantin Belousov addr = (caddr_t)roundup2((long)pagepool_start, pagesz); 294d49ca25dSKonstantin Belousov if (munmap(addr, pagepool_end - addr) != 0) { 295d49ca25dSKonstantin Belousov #ifdef IN_RTLD 296d49ca25dSKonstantin Belousov rtld_fdprintf(STDERR_FILENO, _BASENAME_RTLD ": " 297d49ca25dSKonstantin Belousov "morepages: cannot munmap %p: %s\n", 298d49ca25dSKonstantin Belousov addr, rtld_strerror(errno)); 299d49ca25dSKonstantin Belousov #endif 300d49ca25dSKonstantin Belousov } 301d49ca25dSKonstantin Belousov } 302d49ca25dSKonstantin Belousov 3033cac4083SKonstantin Belousov offset = (long)pagepool_start - rounddown2((long)pagepool_start, 3043cac4083SKonstantin Belousov pagesz); 305d49ca25dSKonstantin Belousov 3063cac4083SKonstantin Belousov pagepool_start = mmap(0, n * pagesz, PROT_READ | PROT_WRITE, 3073cac4083SKonstantin Belousov MAP_ANON | MAP_PRIVATE, -1, 0); 3083cac4083SKonstantin Belousov if (pagepool_start == MAP_FAILED) { 309d49ca25dSKonstantin Belousov #ifdef IN_RTLD 310d49ca25dSKonstantin Belousov rtld_fdprintf(STDERR_FILENO, _BASENAME_RTLD ": morepages: " 311d49ca25dSKonstantin Belousov "cannot mmap anonymous memory: %s\n", 312d49ca25dSKonstantin Belousov rtld_strerror(errno)); 313d49ca25dSKonstantin Belousov #endif 3143cac4083SKonstantin Belousov return (0); 315d49ca25dSKonstantin Belousov } 316d49ca25dSKonstantin Belousov pagepool_end = pagepool_start + n * pagesz; 317d49ca25dSKonstantin Belousov pagepool_start += offset; 318d49ca25dSKonstantin Belousov 3193cac4083SKonstantin Belousov return (n); 320d49ca25dSKonstantin Belousov } 321