1*229bcb83Sriastradh /* $NetBSD: malloc.c,v 1.11 2025/01/20 20:00:52 riastradh Exp $ */ 2645dee56Selric 3645dee56Selric /* 4645dee56Selric * Copyright (c) 1983, 1993 5645dee56Selric * The Regents of the University of California. All rights reserved. 6645dee56Selric * 7645dee56Selric * Redistribution and use in source and binary forms, with or without 8645dee56Selric * modification, are permitted provided that the following conditions 9645dee56Selric * are met: 10645dee56Selric * 1. Redistributions of source code must retain the above copyright 11645dee56Selric * notice, this list of conditions and the following disclaimer. 12645dee56Selric * 2. Redistributions in binary form must reproduce the above copyright 13645dee56Selric * notice, this list of conditions and the following disclaimer in the 14645dee56Selric * documentation and/or other materials provided with the distribution. 15eb7c1594Sagc * 3. Neither the name of the University nor the names of its contributors 16645dee56Selric * may be used to endorse or promote products derived from this software 17645dee56Selric * without specific prior written permission. 18645dee56Selric * 19645dee56Selric * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20645dee56Selric * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21645dee56Selric * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22645dee56Selric * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23645dee56Selric * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24645dee56Selric * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25645dee56Selric * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26645dee56Selric * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27645dee56Selric * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28645dee56Selric * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29645dee56Selric * SUCH DAMAGE. 30645dee56Selric */ 31645dee56Selric 32645dee56Selric #include <sys/cdefs.h> 33645dee56Selric #if defined(LIBC_SCCS) && !defined(lint) 34645dee56Selric #if 0 35645dee56Selric static char sccsid[] = "@(#)malloc.c 8.1 (Berkeley) 6/4/93"; 36645dee56Selric #else 37*229bcb83Sriastradh __RCSID("$NetBSD: malloc.c,v 1.11 2025/01/20 20:00:52 riastradh Exp $"); 38645dee56Selric #endif 39645dee56Selric #endif /* LIBC_SCCS and not lint */ 40645dee56Selric 41645dee56Selric /* 42645dee56Selric * malloc.c (Caltech) 2/21/82 43645dee56Selric * Chris Kingsley, kingsley@cit-20. 44645dee56Selric * 45645dee56Selric * This is a very fast storage allocator. It allocates blocks of a small 46645dee56Selric * number of different sizes, and keeps free lists of each size. Blocks that 47645dee56Selric * don't exactly fit are passed up to the next larger size. In this 48645dee56Selric * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long. 49645dee56Selric * This is designed for use in a virtual memory environment. 50645dee56Selric */ 51645dee56Selric 52645dee56Selric #include <sys/types.h> 53645dee56Selric #if defined(DEBUG) || defined(RCHECK) 54645dee56Selric #include <sys/uio.h> 55645dee56Selric #endif 56d6f78684Sriastradh 57d6f78684Sriastradh #include <errno.h> 58d6f78684Sriastradh #include <limits.h> 59d6f78684Sriastradh #include <stddef.h> 60d6f78684Sriastradh #include <stdint.h> 61645dee56Selric #if defined(RCHECK) || defined(MSTATS) 62645dee56Selric #include <stdio.h> 63645dee56Selric #endif 64645dee56Selric #include <stdlib.h> 65645dee56Selric #include <string.h> 66645dee56Selric #include <unistd.h> 67d6f78684Sriastradh 68645dee56Selric #include "reentrant.h" 69645dee56Selric 70645dee56Selric 71645dee56Selric /* 72645dee56Selric * The overhead on a block is at least 4 bytes. When free, this space 73645dee56Selric * contains a pointer to the next free block, and the bottom two bits must 74645dee56Selric * be zero. When in use, the first byte is set to MAGIC, and the second 75645dee56Selric * byte is the size index. The remaining bytes are for alignment. 76645dee56Selric * If range checking is enabled then a second word holds the size of the 77645dee56Selric * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC). 78645dee56Selric * The order of elements is critical: ov_magic must overlay the low order 79645dee56Selric * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern. 80645dee56Selric */ 81645dee56Selric union overhead { 82645dee56Selric union overhead *ov_next; /* when free */ 83645dee56Selric struct { 84645dee56Selric u_char ovu_magic; /* magic number */ 85645dee56Selric u_char ovu_index; /* bucket # */ 86645dee56Selric #ifdef RCHECK 87645dee56Selric u_short ovu_rmagic; /* range magic number */ 88645dee56Selric u_long ovu_size; /* actual block size */ 89645dee56Selric #endif 90645dee56Selric } ovu; 91645dee56Selric #define ov_magic ovu.ovu_magic 92645dee56Selric #define ov_index ovu.ovu_index 93645dee56Selric #define ov_rmagic ovu.ovu_rmagic 94645dee56Selric #define ov_size ovu.ovu_size 95645dee56Selric }; 96645dee56Selric 97645dee56Selric #define MAGIC 0xef /* magic # on accounting info */ 98645dee56Selric #ifdef RCHECK 99645dee56Selric #define RMAGIC 0x5555 /* magic # on range info */ 100645dee56Selric #endif 101645dee56Selric 102645dee56Selric #ifdef RCHECK 103645dee56Selric #define RSLOP sizeof (u_short) 104645dee56Selric #else 105645dee56Selric #define RSLOP 0 106645dee56Selric #endif 107645dee56Selric 108645dee56Selric /* 109645dee56Selric * nextf[i] is the pointer to the next free block of size 2^(i+3). The 110645dee56Selric * smallest allocatable block is 8 bytes. The overhead information 111645dee56Selric * precedes the data area returned to the user. 112645dee56Selric */ 113645dee56Selric #define NBUCKETS 30 114645dee56Selric static union overhead *nextf[NBUCKETS]; 115645dee56Selric 116645dee56Selric static long pagesz; /* page size */ 117645dee56Selric static int pagebucket; /* page size bucket */ 118645dee56Selric 119645dee56Selric #ifdef MSTATS 120645dee56Selric /* 121645dee56Selric * nmalloc[i] is the difference between the number of mallocs and frees 122645dee56Selric * for a given block size. 123645dee56Selric */ 124645dee56Selric static u_int nmalloc[NBUCKETS]; 125645dee56Selric #endif 126645dee56Selric 127645dee56Selric #ifdef _REENT 128645dee56Selric static mutex_t malloc_mutex = MUTEX_INITIALIZER; 129645dee56Selric #endif 130645dee56Selric 1314651d9e0Sriastradh static void morecore(int); 1324651d9e0Sriastradh static int findbucket(union overhead *, int); 133645dee56Selric #ifdef MSTATS 1344651d9e0Sriastradh void mstats(const char *); 135645dee56Selric #endif 136645dee56Selric 137645dee56Selric #if defined(DEBUG) || defined(RCHECK) 138645dee56Selric #define ASSERT(p) if (!(p)) botch(__STRING(p)) 139645dee56Selric 1404651d9e0Sriastradh static void botch(const char *); 141645dee56Selric 142645dee56Selric /* 143645dee56Selric * NOTE: since this may be called while malloc_mutex is locked, stdio must not 144645dee56Selric * be used in this function. 145645dee56Selric */ 146645dee56Selric static void 1474651d9e0Sriastradh botch(const char *s) 148645dee56Selric { 149645dee56Selric struct iovec iov[3]; 150645dee56Selric 1514651d9e0Sriastradh iov[0].iov_base = __UNCONST("\nassertion botched: "); 152645dee56Selric iov[0].iov_len = 20; 1534651d9e0Sriastradh iov[1].iov_base = __UNCONST(s); 154645dee56Selric iov[1].iov_len = strlen(s); 1554651d9e0Sriastradh iov[2].iov_base = __UNCONST("\n"); 156645dee56Selric iov[2].iov_len = 1; 157645dee56Selric 158645dee56Selric /* 159645dee56Selric * This place deserves a word of warning: a cancellation point will 160645dee56Selric * occur when executing writev(), and we might be still owning 161645dee56Selric * malloc_mutex. At this point we need to disable cancellation 162645dee56Selric * until `after' abort() because i) establishing a cancellation handler 163645dee56Selric * might, depending on the implementation, result in another malloc() 164645dee56Selric * to be executed, and ii) it is really not desirable to let execution 165645dee56Selric * continue. `Fix me.' 166645dee56Selric * 167645dee56Selric * Note that holding mutex_lock during abort() is safe. 168645dee56Selric */ 169645dee56Selric 170645dee56Selric (void)writev(STDERR_FILENO, iov, 3); 171645dee56Selric abort(); 172645dee56Selric } 173645dee56Selric #else 174d6f78684Sriastradh #define ASSERT(p) ((void)sizeof((long)(p))) 175645dee56Selric #endif 176645dee56Selric 177645dee56Selric void * 1786c3f4940Sriastradh malloc(size_t nbytes) 179645dee56Selric { 180645dee56Selric union overhead *op; 181645dee56Selric int bucket; 182645dee56Selric long n; 183645dee56Selric unsigned amt; 184645dee56Selric 185645dee56Selric mutex_lock(&malloc_mutex); 186645dee56Selric 187645dee56Selric /* 188645dee56Selric * First time malloc is called, setup page size and 189645dee56Selric * align break pointer so all data will be page aligned. 190645dee56Selric */ 191645dee56Selric if (pagesz == 0) { 192645dee56Selric pagesz = n = getpagesize(); 193645dee56Selric ASSERT(pagesz > 0); 194645dee56Selric op = (union overhead *)(void *)sbrk(0); 195645dee56Selric n = n - sizeof (*op) - ((long)op & (n - 1)); 196645dee56Selric if (n < 0) 197645dee56Selric n += pagesz; 198645dee56Selric if (n) { 199645dee56Selric if (sbrk((int)n) == (void *)-1) { 200645dee56Selric mutex_unlock(&malloc_mutex); 201645dee56Selric return (NULL); 202645dee56Selric } 203645dee56Selric } 204645dee56Selric bucket = 0; 205645dee56Selric amt = 8; 206645dee56Selric while (pagesz > amt) { 207645dee56Selric amt <<= 1; 208645dee56Selric bucket++; 209645dee56Selric } 210645dee56Selric pagebucket = bucket; 211645dee56Selric } 212645dee56Selric /* 213645dee56Selric * Convert amount of memory requested into closest block size 214645dee56Selric * stored in hash buckets which satisfies request. 215645dee56Selric * Account for space used per block for accounting. 216645dee56Selric */ 217645dee56Selric if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) { 218645dee56Selric #ifndef RCHECK 219645dee56Selric amt = 8; /* size of first bucket */ 220645dee56Selric bucket = 0; 221645dee56Selric #else 222645dee56Selric amt = 16; /* size of first bucket */ 223645dee56Selric bucket = 1; 224645dee56Selric #endif 225645dee56Selric n = -((long)sizeof (*op) + RSLOP); 226645dee56Selric } else { 227645dee56Selric amt = (unsigned)pagesz; 228645dee56Selric bucket = pagebucket; 229645dee56Selric } 230645dee56Selric while (nbytes > amt + n) { 231645dee56Selric amt <<= 1; 232645dee56Selric if (amt == 0) 233645dee56Selric return (NULL); 234645dee56Selric bucket++; 235645dee56Selric } 236645dee56Selric /* 237645dee56Selric * If nothing in hash bucket right now, 238645dee56Selric * request more memory from the system. 239645dee56Selric */ 240645dee56Selric if ((op = nextf[bucket]) == NULL) { 241645dee56Selric morecore(bucket); 242645dee56Selric if ((op = nextf[bucket]) == NULL) { 243645dee56Selric mutex_unlock(&malloc_mutex); 244645dee56Selric return (NULL); 245645dee56Selric } 246645dee56Selric } 247645dee56Selric /* remove from linked list */ 248645dee56Selric nextf[bucket] = op->ov_next; 249645dee56Selric op->ov_magic = MAGIC; 250645dee56Selric op->ov_index = bucket; 251645dee56Selric #ifdef MSTATS 252645dee56Selric nmalloc[bucket]++; 253645dee56Selric #endif 254645dee56Selric mutex_unlock(&malloc_mutex); 255645dee56Selric #ifdef RCHECK 256645dee56Selric /* 257645dee56Selric * Record allocated size of block and 258645dee56Selric * bound space with magic numbers. 259645dee56Selric */ 260645dee56Selric op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); 261645dee56Selric op->ov_rmagic = RMAGIC; 262645dee56Selric *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; 263645dee56Selric #endif 264645dee56Selric return ((void *)(op + 1)); 265645dee56Selric } 266645dee56Selric 267645dee56Selric /* 268645dee56Selric * Allocate more memory to the indicated bucket. 269645dee56Selric */ 270645dee56Selric static void 2716c3f4940Sriastradh morecore(int bucket) 272645dee56Selric { 273645dee56Selric union overhead *op; 274645dee56Selric long sz; /* size of desired block */ 275645dee56Selric long amt; /* amount to allocate */ 276645dee56Selric long nblks; /* how many blocks we get */ 277645dee56Selric 278645dee56Selric /* 279645dee56Selric * sbrk_size <= 0 only for big, FLUFFY, requests (about 280645dee56Selric * 2^30 bytes on a VAX, I think) or for a negative arg. 281645dee56Selric */ 282645dee56Selric sz = 1 << (bucket + 3); 283645dee56Selric #ifdef DEBUG 284645dee56Selric ASSERT(sz > 0); 285645dee56Selric #else 286645dee56Selric if (sz <= 0) 287645dee56Selric return; 288645dee56Selric #endif 289645dee56Selric if (sz < pagesz) { 290645dee56Selric amt = pagesz; 291645dee56Selric nblks = amt / sz; 292645dee56Selric } else { 293645dee56Selric amt = sz + pagesz; 294645dee56Selric nblks = 1; 295645dee56Selric } 296645dee56Selric op = (union overhead *)(void *)sbrk((int)amt); 297645dee56Selric /* no more room! */ 298645dee56Selric if ((long)op == -1) 299645dee56Selric return; 300645dee56Selric /* 301645dee56Selric * Add new memory allocated to that on 302645dee56Selric * free list for this hash bucket. 303645dee56Selric */ 304645dee56Selric nextf[bucket] = op; 305645dee56Selric while (--nblks > 0) { 306645dee56Selric op->ov_next = 307645dee56Selric (union overhead *)(void *)((caddr_t)(void *)op+(size_t)sz); 308645dee56Selric op = op->ov_next; 309645dee56Selric } 310645dee56Selric } 311645dee56Selric 312645dee56Selric void 3136c3f4940Sriastradh free(void *cp) 314645dee56Selric { 315645dee56Selric long size; 316645dee56Selric union overhead *op; 317645dee56Selric 318645dee56Selric if (cp == NULL) 319645dee56Selric return; 320645dee56Selric op = (union overhead *)(void *)((caddr_t)cp - sizeof (union overhead)); 321645dee56Selric #ifdef DEBUG 322645dee56Selric ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ 323645dee56Selric #else 324645dee56Selric if (op->ov_magic != MAGIC) 325645dee56Selric return; /* sanity */ 326645dee56Selric #endif 327645dee56Selric #ifdef RCHECK 328645dee56Selric ASSERT(op->ov_rmagic == RMAGIC); 329645dee56Selric ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC); 330645dee56Selric #endif 331645dee56Selric size = op->ov_index; 332645dee56Selric ASSERT(size < NBUCKETS); 333645dee56Selric mutex_lock(&malloc_mutex); 334645dee56Selric op->ov_next = nextf[(unsigned int)size];/* also clobbers ov_magic */ 335645dee56Selric nextf[(unsigned int)size] = op; 336645dee56Selric #ifdef MSTATS 337645dee56Selric nmalloc[(size_t)size]--; 338645dee56Selric #endif 339645dee56Selric mutex_unlock(&malloc_mutex); 340645dee56Selric } 341645dee56Selric 342645dee56Selric /* 343645dee56Selric * When a program attempts "storage compaction" as mentioned in the 344645dee56Selric * old malloc man page, it realloc's an already freed block. Usually 345645dee56Selric * this is the last block it freed; occasionally it might be farther 346645dee56Selric * back. We have to search all the free lists for the block in order 347645dee56Selric * to determine its bucket: 1st we make one pass thru the lists 348645dee56Selric * checking only the first block in each; if that fails we search 349645dee56Selric * ``__realloc_srchlen'' blocks in each list for a match (the variable 350645dee56Selric * is extern so the caller can modify it). If that fails we just copy 351645dee56Selric * however many bytes was given to realloc() and hope it's not huge. 352645dee56Selric */ 353645dee56Selric int __realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */ 354645dee56Selric 355645dee56Selric void * 3566c3f4940Sriastradh realloc(void *cp, size_t nbytes) 357645dee56Selric { 358645dee56Selric u_long onb; 359645dee56Selric long i; 360645dee56Selric union overhead *op; 361645dee56Selric char *res; 362645dee56Selric int was_alloced = 0; 363645dee56Selric 364645dee56Selric if (cp == NULL) 365645dee56Selric return (malloc(nbytes)); 366645dee56Selric if (nbytes == 0) { 367645dee56Selric free (cp); 368645dee56Selric return (NULL); 369645dee56Selric } 370645dee56Selric op = (union overhead *)(void *)((caddr_t)cp - sizeof (union overhead)); 371645dee56Selric mutex_lock(&malloc_mutex); 372645dee56Selric if (op->ov_magic == MAGIC) { 373645dee56Selric was_alloced++; 374645dee56Selric i = op->ov_index; 375645dee56Selric } else { 376645dee56Selric /* 377645dee56Selric * Already free, doing "compaction". 378645dee56Selric * 379645dee56Selric * Search for the old block of memory on the 380645dee56Selric * free list. First, check the most common 381645dee56Selric * case (last element free'd), then (this failing) 382645dee56Selric * the last ``__realloc_srchlen'' items free'd. 383645dee56Selric * If all lookups fail, then assume the size of 384645dee56Selric * the memory block being realloc'd is the 385645dee56Selric * largest possible (so that all "nbytes" of new 386645dee56Selric * memory are copied into). Note that this could cause 387645dee56Selric * a memory fault if the old area was tiny, and the moon 388645dee56Selric * is gibbous. However, that is very unlikely. 389645dee56Selric */ 390645dee56Selric if ((i = findbucket(op, 1)) < 0 && 391645dee56Selric (i = findbucket(op, __realloc_srchlen)) < 0) 392645dee56Selric i = NBUCKETS; 393645dee56Selric } 394645dee56Selric onb = (u_long)1 << (u_long)(i + 3); 395645dee56Selric if (onb < pagesz) 396645dee56Selric onb -= sizeof (*op) + RSLOP; 397645dee56Selric else 398645dee56Selric onb += pagesz - sizeof (*op) - RSLOP; 399645dee56Selric /* avoid the copy if same size block */ 400645dee56Selric if (was_alloced) { 401645dee56Selric if (i) { 402645dee56Selric i = (long)1 << (long)(i + 2); 403645dee56Selric if (i < pagesz) 404645dee56Selric i -= sizeof (*op) + RSLOP; 405645dee56Selric else 406645dee56Selric i += pagesz - sizeof (*op) - RSLOP; 407645dee56Selric } 408645dee56Selric if (nbytes <= onb && nbytes > i) { 409645dee56Selric #ifdef RCHECK 410645dee56Selric op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); 411645dee56Selric *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; 412645dee56Selric #endif 413645dee56Selric mutex_unlock(&malloc_mutex); 414645dee56Selric return (cp); 415645dee56Selric 416645dee56Selric } 417645dee56Selric #ifndef _REENT 418645dee56Selric else 419645dee56Selric free(cp); 420645dee56Selric #endif 421645dee56Selric } 422645dee56Selric mutex_unlock(&malloc_mutex); 423645dee56Selric if ((res = malloc(nbytes)) == NULL) { 424645dee56Selric #ifdef _REENT 425645dee56Selric free(cp); 426645dee56Selric #endif 427645dee56Selric return (NULL); 428645dee56Selric } 429645dee56Selric #ifndef _REENT 430645dee56Selric if (cp != res) /* common optimization if "compacting" */ 431645dee56Selric (void)memmove(res, cp, (size_t)((nbytes < onb) ? nbytes : onb)); 432645dee56Selric #else 433645dee56Selric (void)memmove(res, cp, (size_t)((nbytes < onb) ? nbytes : onb)); 434645dee56Selric free(cp); 435645dee56Selric #endif 436645dee56Selric return (res); 437645dee56Selric } 438645dee56Selric 439645dee56Selric /* 440645dee56Selric * Search ``srchlen'' elements of each free list for a block whose 441645dee56Selric * header starts at ``freep''. If srchlen is -1 search the whole list. 442645dee56Selric * Return bucket number, or -1 if not found. 443645dee56Selric */ 444645dee56Selric static int 4456c3f4940Sriastradh findbucket(union overhead *freep, int srchlen) 446645dee56Selric { 447645dee56Selric union overhead *p; 448645dee56Selric int i, j; 449645dee56Selric 450645dee56Selric for (i = 0; i < NBUCKETS; i++) { 451645dee56Selric j = 0; 452645dee56Selric for (p = nextf[i]; p && j != srchlen; p = p->ov_next) { 453645dee56Selric if (p == freep) 454645dee56Selric return (i); 455645dee56Selric j++; 456645dee56Selric } 457645dee56Selric } 458645dee56Selric return (-1); 459645dee56Selric } 460645dee56Selric 461645dee56Selric #ifdef MSTATS 462645dee56Selric /* 463645dee56Selric * mstats - print out statistics about malloc 464645dee56Selric * 465645dee56Selric * Prints two lines of numbers, one showing the length of the free list 466645dee56Selric * for each size category, the second showing the number of mallocs - 467645dee56Selric * frees for each size category. 468645dee56Selric */ 469645dee56Selric void 4702a758386Ssimonb mstats(const char *s) 471645dee56Selric { 472645dee56Selric int i, j; 473645dee56Selric union overhead *p; 474645dee56Selric int totfree = 0, 475645dee56Selric totused = 0; 476645dee56Selric 477645dee56Selric fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s); 478645dee56Selric for (i = 0; i < NBUCKETS; i++) { 479645dee56Selric for (j = 0, p = nextf[i]; p; p = p->ov_next, j++) 480645dee56Selric ; 481645dee56Selric fprintf(stderr, " %d", j); 482645dee56Selric totfree += j * (1 << (i + 3)); 483645dee56Selric } 484645dee56Selric fprintf(stderr, "\nused:\t"); 485645dee56Selric for (i = 0; i < NBUCKETS; i++) { 486645dee56Selric fprintf(stderr, " %d", nmalloc[i]); 487645dee56Selric totused += nmalloc[i] * (1 << (i + 3)); 488645dee56Selric } 489645dee56Selric fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n", 490645dee56Selric totused, totfree); 491645dee56Selric } 492645dee56Selric #endif 493d6f78684Sriastradh 494d6f78684Sriastradh /* 495d6f78684Sriastradh * Additional front ends: 496d6f78684Sriastradh * - aligned_alloc (C11) 497d6f78684Sriastradh * - calloc(n,m) = malloc(n*m) without overflow 498d6f78684Sriastradh * - posix_memalign (POSIX) 499d6f78684Sriastradh * 500d6f78684Sriastradh * These must all be in the same compilation unit as malloc, realloc, 501d6f78684Sriastradh * and free (or -lbsdmalloc must be surrounded by -Wl,--whole-archive 502d6f78684Sriastradh * -lbsdmalloc -Wl,--no-whole-archive) in order to override the libc 503d6f78684Sriastradh * built-in malloc implementation. 504d6f78684Sriastradh * 505d6f78684Sriastradh * Allocations of size n, up to and including the page size, are 506d6f78684Sriastradh * already aligned by malloc on multiples of n. Larger alignment is 507d6f78684Sriastradh * not supported. 508d6f78684Sriastradh */ 509d6f78684Sriastradh 510d6f78684Sriastradh static long __constfunc 511d6f78684Sriastradh cachedpagesize(void) 512d6f78684Sriastradh { 513d6f78684Sriastradh long n; 514d6f78684Sriastradh 515d6f78684Sriastradh /* XXX atomic_load_relaxed, but that's not defined in userland atm */ 516d6f78684Sriastradh if (__predict_false((n = pagesz) == 0)) { 517d6f78684Sriastradh mutex_lock(&malloc_mutex); 518d6f78684Sriastradh if ((n = pagesz) == 0) 519d6f78684Sriastradh n = pagesz = getpagesize(); 520d6f78684Sriastradh mutex_unlock(&malloc_mutex); 521d6f78684Sriastradh } 522d6f78684Sriastradh 523d6f78684Sriastradh return n; 524d6f78684Sriastradh } 525d6f78684Sriastradh 526d6f78684Sriastradh void * 527d6f78684Sriastradh aligned_alloc(size_t alignment, size_t size) 528d6f78684Sriastradh { 529d6f78684Sriastradh char *p; 530d6f78684Sriastradh 531d6f78684Sriastradh if (alignment == 0 || 532d6f78684Sriastradh (alignment & (alignment - 1)) != 0 || 533c9e3880cSriastradh alignment > cachedpagesize()) { 534d6f78684Sriastradh errno = EINVAL; 535d6f78684Sriastradh return NULL; 536d6f78684Sriastradh } 5379e6ac21aSriastradh p = malloc(size < alignment ? alignment : size); 538*229bcb83Sriastradh if (__predict_true(p != NULL)) 539d6f78684Sriastradh ASSERT((uintptr_t)p % alignment == 0); 540d6f78684Sriastradh return p; 541d6f78684Sriastradh } 542d6f78684Sriastradh 543d6f78684Sriastradh void * 544d6f78684Sriastradh calloc(size_t nmemb, size_t size) 545d6f78684Sriastradh { 546d6f78684Sriastradh void *p; 547d6f78684Sriastradh size_t n; 548d6f78684Sriastradh 549d5888fcbSriastradh if (__builtin_mul_overflow(nmemb, size, &n)) { 550d6f78684Sriastradh errno = ENOMEM; 551d6f78684Sriastradh return NULL; 552d6f78684Sriastradh } 553d6f78684Sriastradh p = malloc(n); 554d6f78684Sriastradh if (__predict_false(p == NULL)) 555d6f78684Sriastradh return NULL; 556d6f78684Sriastradh memset(p, 0, n); 557d6f78684Sriastradh return p; 558d6f78684Sriastradh } 559d6f78684Sriastradh 560d6f78684Sriastradh int 561d6f78684Sriastradh posix_memalign(void **memptr, size_t alignment, size_t size) 562d6f78684Sriastradh { 563d6f78684Sriastradh char *p; 564d6f78684Sriastradh 565d6f78684Sriastradh if (alignment < sizeof(void *) || 566d6f78684Sriastradh (alignment & (alignment - 1)) != 0 || 567d6f78684Sriastradh alignment > cachedpagesize()) 568d6f78684Sriastradh return EINVAL; 569d6f78684Sriastradh p = malloc(size < alignment ? alignment : size); 570d6f78684Sriastradh if (__predict_false(p == NULL)) 571d6f78684Sriastradh return ENOMEM; 572d6f78684Sriastradh ASSERT((uintptr_t)p % alignment == 0); 573d6f78684Sriastradh *memptr = p; 574d6f78684Sriastradh return 0; 575d6f78684Sriastradh } 576d6f78684Sriastradh 577d6f78684Sriastradh /* 578d6f78684Sriastradh * libc hooks required by fork 579d6f78684Sriastradh */ 580d6f78684Sriastradh 581d6f78684Sriastradh #include "../libc/include/extern.h" 582d6f78684Sriastradh 583d6f78684Sriastradh void 584d6f78684Sriastradh _malloc_prefork(void) 585d6f78684Sriastradh { 586d6f78684Sriastradh 587d6f78684Sriastradh mutex_lock(&malloc_mutex); 588d6f78684Sriastradh } 589d6f78684Sriastradh 590d6f78684Sriastradh void 591d6f78684Sriastradh _malloc_postfork(void) 592d6f78684Sriastradh { 593d6f78684Sriastradh 594d6f78684Sriastradh mutex_unlock(&malloc_mutex); 595d6f78684Sriastradh } 596d6f78684Sriastradh 597d6f78684Sriastradh void 598d6f78684Sriastradh _malloc_postfork_child(void) 599d6f78684Sriastradh { 600d6f78684Sriastradh 601d6f78684Sriastradh mutex_unlock(&malloc_mutex); 602d6f78684Sriastradh } 603