1 /* $NetBSD: malloc.c,v 1.29 2000/01/31 22:56:14 thorpej Exp $ */ 2 3 /* 4 * ---------------------------------------------------------------------------- 5 * "THE BEER-WARE LICENSE" (Revision 42): 6 * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 7 * can do whatever you want with this stuff. If we meet some day, and you think 8 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 9 * ---------------------------------------------------------------------------- 10 * 11 * From FreeBSD: malloc.c,v 1.43 1998/09/30 06:13:59 jb 12 * 13 */ 14 15 /* 16 * Defining MALLOC_EXTRA_SANITY will enable extra checks which are related 17 * to internal conditions and consistency in malloc.c. This has a 18 * noticeable runtime performance hit, and generally will not do you 19 * any good unless you fiddle with the internals of malloc or want 20 * to catch random pointer corruption as early as possible. 21 */ 22 #ifndef MALLOC_EXTRA_SANITY 23 #undef MALLOC_EXTRA_SANITY 24 #endif 25 26 /* 27 * What to use for Junk. This is the byte value we use to fill with 28 * when the 'J' option is enabled. 29 */ 30 #define SOME_JUNK 0xd0 /* as in "Duh" :-) */ 31 32 /* 33 * The basic parameters you can tweak. 34 * 35 * malloc_minsize minimum size of an allocation in bytes. 36 * If this is too small it's too much work 37 * to manage them. This is also the smallest 38 * unit of alignment used for the storage 39 * returned by malloc/realloc. 40 * 41 */ 42 43 #if defined(__FreeBSD__) 44 # if defined(__i386__) 45 # define malloc_minsize 16U 46 # endif 47 # if defined(__alpha__) 48 # define malloc_minsize 16U 49 # endif 50 # if !defined(__NETBSD_SYSCALLS) 51 # define HAS_UTRACE 52 # endif 53 /* 54 * Make malloc/free/realloc thread-safe in libc for use with 55 * kernel threads. 56 */ 57 # include "libc_private.h" 58 # include "spinlock.h" 59 static spinlock_t thread_lock = _SPINLOCK_INITIALIZER; 60 # define THREAD_LOCK() if (__isthreaded) _SPINLOCK(&thread_lock); 61 # define THREAD_UNLOCK() if (__isthreaded) _SPINUNLOCK(&thread_lock); 62 #endif /* __FreeBSD__ */ 63 64 #if defined(__NetBSD__) 65 # define malloc_minsize 16U 66 #endif /* __NetBSD__ */ 67 68 #if defined(__sparc__) && defined(sun) 69 # define malloc_minsize 16U 70 # define MAP_ANON (0) 71 static int fdzero; 72 # define MMAP_FD fdzero 73 # define INIT_MMAP() \ 74 { if ((fdzero=open("/dev/zero", O_RDWR, 0000)) == -1) \ 75 wrterror("open of /dev/zero"); } 76 #endif /* __sparc__ */ 77 78 /* Insert your combination here... */ 79 #if defined(__FOOCPU__) && defined(__BAROS__) 80 # define malloc_minsize 16U 81 #endif /* __FOOCPU__ && __BAROS__ */ 82 83 84 /* 85 * No user serviceable parts behind this point. 86 */ 87 #include "namespace.h" 88 #include <sys/types.h> 89 #include <sys/mman.h> 90 #include <errno.h> 91 #include <fcntl.h> 92 #include <stddef.h> 93 #include <stdio.h> 94 #include <stdlib.h> 95 #include <string.h> 96 #include <unistd.h> 97 98 /* 99 * This structure describes a page worth of chunks. 100 */ 101 102 struct pginfo { 103 struct pginfo *next; /* next on the free list */ 104 void *page; /* Pointer to the page */ 105 u_short size; /* size of this page's chunks */ 106 u_short shift; /* How far to shift for this size chunks */ 107 u_short free; /* How many free chunks */ 108 u_short total; /* How many chunk */ 109 u_int bits[1]; /* Which chunks are free */ 110 }; 111 112 /* 113 * This structure describes a number of free pages. 114 */ 115 116 struct pgfree { 117 struct pgfree *next; /* next run of free pages */ 118 struct pgfree *prev; /* prev run of free pages */ 119 void *page; /* pointer to free pages */ 120 void *end; /* pointer to end of free pages */ 121 size_t size; /* number of bytes free */ 122 }; 123 124 /* 125 * How many bits per u_int in the bitmap. 126 * Change only if not 8 bits/byte 127 */ 128 #define MALLOC_BITS (8*sizeof(u_int)) 129 130 /* 131 * Magic values to put in the page_directory 132 */ 133 #define MALLOC_NOT_MINE ((struct pginfo*) 0) 134 #define MALLOC_FREE ((struct pginfo*) 1) 135 #define MALLOC_FIRST ((struct pginfo*) 2) 136 #define MALLOC_FOLLOW ((struct pginfo*) 3) 137 #define MALLOC_MAGIC ((struct pginfo*) 4) 138 139 /* 140 * Page size related parameters, computed at run-time. 141 */ 142 static size_t malloc_pagesize; 143 static size_t malloc_pageshift; 144 static size_t malloc_pagemask; 145 146 #ifndef malloc_minsize 147 #define malloc_minsize 16U 148 #endif 149 150 #ifndef malloc_maxsize 151 #define malloc_maxsize ((malloc_pagesize)>>1) 152 #endif 153 154 #define pageround(foo) (((foo) + (malloc_pagemask))&(~(malloc_pagemask))) 155 #define ptr2idx(foo) (((u_long)(foo) >> malloc_pageshift)-malloc_origo) 156 157 #ifndef THREAD_LOCK 158 #define THREAD_LOCK() 159 #endif 160 161 #ifndef THREAD_UNLOCK 162 #define THREAD_UNLOCK() 163 #endif 164 165 #ifndef MMAP_FD 166 #define MMAP_FD (-1) 167 #endif 168 169 #ifndef INIT_MMAP 170 #define INIT_MMAP() 171 #endif 172 173 #ifndef MADV_FREE 174 #define MADV_FREE MADV_DONTNEED 175 #endif 176 177 /* Set when initialization has been done */ 178 static unsigned malloc_started; 179 180 /* Recusion flag for public interface. */ 181 static int malloc_active; 182 183 /* Number of free pages we cache */ 184 static unsigned malloc_cache = 16; 185 186 /* The offset from pagenumber to index into the page directory */ 187 static u_long malloc_origo; 188 189 /* The last index in the page directory we care about */ 190 static u_long last_idx; 191 192 /* Pointer to page directory. Allocated "as if with" malloc */ 193 static struct pginfo **page_dir; 194 195 /* How many slots in the page directory */ 196 static unsigned malloc_ninfo; 197 198 /* Free pages line up here */ 199 static struct pgfree free_list; 200 201 /* Abort(), user doesn't handle problems. */ 202 static int malloc_abort; 203 204 /* Are we trying to die ? */ 205 static int suicide; 206 207 /* always realloc ? */ 208 static int malloc_realloc; 209 210 /* pass the kernel a hint on free pages ? */ 211 static int malloc_hint = 1; 212 213 /* xmalloc behaviour ? */ 214 static int malloc_xmalloc; 215 216 /* sysv behaviour for malloc(0) ? */ 217 static int malloc_sysv; 218 219 /* zero fill ? */ 220 static int malloc_zero; 221 222 /* junk fill ? */ 223 static int malloc_junk; 224 225 #ifdef HAS_UTRACE 226 227 /* utrace ? */ 228 static int malloc_utrace; 229 230 struct ut { void *p; size_t s; void *r; }; 231 232 void utrace __P((struct ut *, int)); 233 234 #define UTRACE(a, b, c) \ 235 if (malloc_utrace) \ 236 {struct ut u; u.p=a; u.s = b; u.r=c; utrace(&u, sizeof u);} 237 #else /* !HAS_UTRACE */ 238 #define UTRACE(a,b,c) 239 #endif /* HAS_UTRACE */ 240 241 /* my last break. */ 242 static void *malloc_brk; 243 244 /* one location cache for free-list holders */ 245 static struct pgfree *px; 246 247 /* compile-time options */ 248 char *malloc_options; 249 250 /* Name of the current public function */ 251 static char *malloc_func; 252 253 /* Macro for mmap */ 254 #define MMAP(size) \ 255 mmap(0, (size), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, \ 256 MMAP_FD, 0); 257 258 /* 259 * Necessary function declarations 260 */ 261 static int extend_pgdir(u_long idx); 262 static void *imalloc(size_t size); 263 static void ifree(void *ptr); 264 static void *irealloc(void *ptr, size_t size); 265 266 extern char *__progname; 267 268 static void 269 wrterror(char *p) 270 { 271 char *q = " error: "; 272 write(STDERR_FILENO, __progname, strlen(__progname)); 273 write(STDERR_FILENO, malloc_func, strlen(malloc_func)); 274 write(STDERR_FILENO, q, strlen(q)); 275 write(STDERR_FILENO, p, strlen(p)); 276 suicide = 1; 277 abort(); 278 } 279 280 static void 281 wrtwarning(char *p) 282 { 283 char *q = " warning: "; 284 if (malloc_abort) 285 wrterror(p); 286 write(STDERR_FILENO, __progname, strlen(__progname)); 287 write(STDERR_FILENO, malloc_func, strlen(malloc_func)); 288 write(STDERR_FILENO, q, strlen(q)); 289 write(STDERR_FILENO, p, strlen(p)); 290 } 291 292 293 /* 294 * Allocate a number of pages from the OS 295 */ 296 static void * 297 map_pages(int pages) 298 { 299 caddr_t result, tail; 300 301 result = (caddr_t)pageround((u_long)sbrk(0)); 302 tail = result + (pages << malloc_pageshift); 303 304 if (brk(tail)) { 305 #ifdef MALLOC_EXTRA_SANITY 306 wrterror("(ES): map_pages fails\n"); 307 #endif /* MALLOC_EXTRA_SANITY */ 308 return 0; 309 } 310 311 last_idx = ptr2idx(tail) - 1; 312 malloc_brk = tail; 313 314 if ((last_idx+1) >= malloc_ninfo && !extend_pgdir(last_idx)) 315 return 0;; 316 317 return result; 318 } 319 320 /* 321 * Extend page directory 322 */ 323 static int 324 extend_pgdir(u_long idx) 325 { 326 struct pginfo **new, **old; 327 int i, oldlen; 328 329 /* Make it this many pages */ 330 i = idx * sizeof *page_dir; 331 i /= malloc_pagesize; 332 i += 2; 333 334 /* remember the old mapping size */ 335 oldlen = malloc_ninfo * sizeof *page_dir; 336 337 /* 338 * NOTE: we allocate new pages and copy the directory rather than tempt 339 * fate by trying to "grow" the region.. There is nothing to prevent 340 * us from accidently re-mapping space that's been allocated by our caller 341 * via dlopen() or other mmap(). 342 * 343 * The copy problem is not too bad, as there is 4K of page index per 344 * 4MB of malloc arena. 345 * 346 * We can totally avoid the copy if we open a file descriptor to associate 347 * the anon mappings with. Then, when we remap the pages at the new 348 * address, the old pages will be "magically" remapped.. But this means 349 * keeping open a "secret" file descriptor..... 350 */ 351 352 /* Get new pages */ 353 new = (struct pginfo**) MMAP(i * malloc_pagesize); 354 if (new == (struct pginfo **)-1) 355 return 0; 356 357 /* Copy the old stuff */ 358 memcpy(new, page_dir, 359 malloc_ninfo * sizeof *page_dir); 360 361 /* register the new size */ 362 malloc_ninfo = i * malloc_pagesize / sizeof *page_dir; 363 364 /* swap the pointers */ 365 old = page_dir; 366 page_dir = new; 367 368 /* Now free the old stuff */ 369 munmap(old, oldlen); 370 return 1; 371 } 372 373 /* 374 * Initialize the world 375 */ 376 static void 377 malloc_init (void) 378 { 379 char *p, b[64]; 380 int i, j; 381 int errnosave; 382 383 /* 384 * Compute page-size related variables. 385 */ 386 malloc_pagesize = sysconf(_SC_PAGESIZE); 387 malloc_pagemask = malloc_pagesize - 1; 388 for (malloc_pageshift = 0; 389 (1UL << malloc_pageshift) != malloc_pagesize; 390 malloc_pageshift++) 391 /* nothing */ ; 392 393 INIT_MMAP(); 394 395 #ifdef MALLOC_EXTRA_SANITY 396 malloc_junk = 1; 397 #endif /* MALLOC_EXTRA_SANITY */ 398 399 for (i = 0; i < 3; i++) { 400 if (i == 0) { 401 errnosave = errno; 402 j = readlink("/etc/malloc.conf", b, sizeof b - 1); 403 errno = errnosave; 404 if (j <= 0) 405 continue; 406 b[j] = '\0'; 407 p = b; 408 } else if (i == 1) { 409 p = getenv("MALLOC_OPTIONS"); 410 } else { 411 p = malloc_options; 412 } 413 for (; p && *p; p++) { 414 switch (*p) { 415 case '>': malloc_cache <<= 1; break; 416 case '<': malloc_cache >>= 1; break; 417 case 'a': malloc_abort = 0; break; 418 case 'A': malloc_abort = 1; break; 419 case 'h': malloc_hint = 0; break; 420 case 'H': malloc_hint = 1; break; 421 case 'r': malloc_realloc = 0; break; 422 case 'R': malloc_realloc = 1; break; 423 case 'j': malloc_junk = 0; break; 424 case 'J': malloc_junk = 1; break; 425 #ifdef HAS_UTRACE 426 case 'u': malloc_utrace = 0; break; 427 case 'U': malloc_utrace = 1; break; 428 #endif 429 case 'v': malloc_sysv = 0; break; 430 case 'V': malloc_sysv = 1; break; 431 case 'x': malloc_xmalloc = 0; break; 432 case 'X': malloc_xmalloc = 1; break; 433 case 'z': malloc_zero = 0; break; 434 case 'Z': malloc_zero = 1; break; 435 default: 436 j = malloc_abort; 437 malloc_abort = 0; 438 wrtwarning("unknown char in MALLOC_OPTIONS\n"); 439 malloc_abort = j; 440 break; 441 } 442 } 443 } 444 445 UTRACE(0, 0, 0); 446 447 /* 448 * We want junk in the entire allocation, and zero only in the part 449 * the user asked for. 450 */ 451 if (malloc_zero) 452 malloc_junk=1; 453 454 /* 455 * If we run with junk (or implicitly from above: zero), we want to 456 * force realloc() to get new storage, so we can DTRT with it. 457 */ 458 if (malloc_junk) 459 malloc_realloc=1; 460 461 /* Allocate one page for the page directory */ 462 page_dir = (struct pginfo **) MMAP(malloc_pagesize); 463 464 if (page_dir == (struct pginfo **) -1) 465 wrterror("mmap(2) failed, check limits.\n"); 466 467 /* 468 * We need a maximum of malloc_pageshift buckets, steal these from the 469 * front of the page_directory; 470 */ 471 malloc_origo = ((u_long)pageround((u_long)sbrk(0))) >> malloc_pageshift; 472 malloc_origo -= malloc_pageshift; 473 474 malloc_ninfo = malloc_pagesize / sizeof *page_dir; 475 476 /* Recalculate the cache size in bytes, and make sure it's nonzero */ 477 478 if (!malloc_cache) 479 malloc_cache++; 480 481 malloc_cache <<= malloc_pageshift; 482 483 /* 484 * This is a nice hack from Kaleb Keithly (kaleb@x.org). 485 * We can sbrk(2) further back when we keep this on a low address. 486 */ 487 px = (struct pgfree *) imalloc (sizeof *px); 488 489 /* Been here, done that */ 490 malloc_started++; 491 } 492 493 /* 494 * Allocate a number of complete pages 495 */ 496 static void * 497 malloc_pages(size_t size) 498 { 499 void *p, *delay_free = 0; 500 int i; 501 struct pgfree *pf; 502 u_long idx; 503 504 size = pageround(size); 505 506 p = 0; 507 508 /* Look for free pages before asking for more */ 509 for(pf = free_list.next; pf; pf = pf->next) { 510 511 #ifdef MALLOC_EXTRA_SANITY 512 if (pf->size & malloc_pagemask) 513 wrterror("(ES): junk length entry on free_list\n"); 514 if (!pf->size) 515 wrterror("(ES): zero length entry on free_list\n"); 516 if (pf->page == pf->end) 517 wrterror("(ES): zero entry on free_list\n"); 518 if (pf->page > pf->end) 519 wrterror("(ES): sick entry on free_list\n"); 520 if ((void*)pf->page >= (void*)sbrk(0)) 521 wrterror("(ES): entry on free_list past brk\n"); 522 if (page_dir[ptr2idx(pf->page)] != MALLOC_FREE) 523 wrterror("(ES): non-free first page on free-list\n"); 524 if (page_dir[ptr2idx(pf->end)-1] != MALLOC_FREE) 525 wrterror("(ES): non-free last page on free-list\n"); 526 #endif /* MALLOC_EXTRA_SANITY */ 527 528 if (pf->size < size) 529 continue; 530 531 if (pf->size == size) { 532 p = pf->page; 533 if (pf->next) 534 pf->next->prev = pf->prev; 535 pf->prev->next = pf->next; 536 delay_free = pf; 537 break; 538 } 539 540 p = pf->page; 541 pf->page = (char *)pf->page + size; 542 pf->size -= size; 543 break; 544 } 545 546 #ifdef MALLOC_EXTRA_SANITY 547 if (p && page_dir[ptr2idx(p)] != MALLOC_FREE) 548 wrterror("(ES): allocated non-free page on free-list\n"); 549 #endif /* MALLOC_EXTRA_SANITY */ 550 551 size >>= malloc_pageshift; 552 553 /* Map new pages */ 554 if (!p) 555 p = map_pages(size); 556 557 if (p) { 558 559 idx = ptr2idx(p); 560 page_dir[idx] = MALLOC_FIRST; 561 for (i=1;i<size;i++) 562 page_dir[idx+i] = MALLOC_FOLLOW; 563 564 if (malloc_junk) 565 memset(p, SOME_JUNK, size << malloc_pageshift); 566 } 567 568 if (delay_free) { 569 if (!px) 570 px = delay_free; 571 else 572 ifree(delay_free); 573 } 574 575 return p; 576 } 577 578 /* 579 * Allocate a page of fragments 580 */ 581 582 static __inline__ int 583 malloc_make_chunks(int bits) 584 { 585 struct pginfo *bp; 586 void *pp; 587 int i, k, l; 588 589 /* Allocate a new bucket */ 590 pp = malloc_pages(malloc_pagesize); 591 if (!pp) 592 return 0; 593 594 /* Find length of admin structure */ 595 l = offsetof(struct pginfo, bits[0]); 596 l += sizeof bp->bits[0] * 597 (((malloc_pagesize >> bits)+MALLOC_BITS-1) / MALLOC_BITS); 598 599 /* Don't waste more than two chunks on this */ 600 if ((1<<(bits)) <= l+l) { 601 bp = (struct pginfo *)pp; 602 } else { 603 bp = (struct pginfo *)imalloc(l); 604 if (!bp) { 605 ifree(pp); 606 return 0; 607 } 608 } 609 610 bp->size = (1<<bits); 611 bp->shift = bits; 612 bp->total = bp->free = malloc_pagesize >> bits; 613 bp->page = pp; 614 615 /* set all valid bits in the bitmap */ 616 k = bp->total; 617 i = 0; 618 619 /* Do a bunch at a time */ 620 for(;k-i >= MALLOC_BITS; i += MALLOC_BITS) 621 bp->bits[i / MALLOC_BITS] = ~0; 622 623 for(; i < k; i++) 624 bp->bits[i/MALLOC_BITS] |= 1<<(i%MALLOC_BITS); 625 626 if (bp == bp->page) { 627 /* Mark the ones we stole for ourselves */ 628 for(i=0;l > 0;i++) { 629 bp->bits[i/MALLOC_BITS] &= ~(1<<(i%MALLOC_BITS)); 630 bp->free--; 631 bp->total--; 632 l -= (1 << bits); 633 } 634 } 635 636 /* MALLOC_LOCK */ 637 638 page_dir[ptr2idx(pp)] = bp; 639 640 bp->next = page_dir[bits]; 641 page_dir[bits] = bp; 642 643 /* MALLOC_UNLOCK */ 644 645 return 1; 646 } 647 648 /* 649 * Allocate a fragment 650 */ 651 static void * 652 malloc_bytes(size_t size) 653 { 654 int i,j; 655 u_int u; 656 struct pginfo *bp; 657 int k; 658 u_int *lp; 659 660 /* Don't bother with anything less than this */ 661 if (size < malloc_minsize) 662 size = malloc_minsize; 663 664 /* Find the right bucket */ 665 j = 1; 666 i = size-1; 667 while (i >>= 1) 668 j++; 669 670 /* If it's empty, make a page more of that size chunks */ 671 if (!page_dir[j] && !malloc_make_chunks(j)) 672 return 0; 673 674 bp = page_dir[j]; 675 676 /* Find first word of bitmap which isn't empty */ 677 for (lp = bp->bits; !*lp; lp++) 678 ; 679 680 /* Find that bit, and tweak it */ 681 u = 1; 682 k = 0; 683 while (!(*lp & u)) { 684 u += u; 685 k++; 686 } 687 *lp ^= u; 688 689 /* If there are no more free, remove from free-list */ 690 if (!--bp->free) { 691 page_dir[j] = bp->next; 692 bp->next = 0; 693 } 694 695 /* Adjust to the real offset of that chunk */ 696 k += (lp-bp->bits)*MALLOC_BITS; 697 k <<= bp->shift; 698 699 if (malloc_junk) 700 memset((u_char*)bp->page + k, SOME_JUNK, bp->size); 701 702 return (u_char *)bp->page + k; 703 } 704 705 /* 706 * Allocate a piece of memory 707 */ 708 static void * 709 imalloc(size_t size) 710 { 711 void *result; 712 713 if (suicide) 714 abort(); 715 716 if ((size + malloc_pagesize) < size) /* Check for overflow */ 717 result = 0; 718 else if (size <= malloc_maxsize) 719 result = malloc_bytes(size); 720 else 721 result = malloc_pages(size); 722 723 if (malloc_abort && !result) 724 wrterror("allocation failed.\n"); 725 726 if (malloc_zero && result) 727 memset(result, 0, size); 728 729 return result; 730 } 731 732 /* 733 * Change the size of an allocation. 734 */ 735 static void * 736 irealloc(void *ptr, size_t size) 737 { 738 void *p; 739 u_long osize, idx; 740 struct pginfo **mp; 741 int i; 742 743 if (suicide) 744 abort(); 745 746 idx = ptr2idx(ptr); 747 748 if (idx < malloc_pageshift) { 749 wrtwarning("junk pointer, too low to make sense.\n"); 750 return 0; 751 } 752 753 if (idx > last_idx) { 754 wrtwarning("junk pointer, too high to make sense.\n"); 755 return 0; 756 } 757 758 mp = &page_dir[idx]; 759 760 if (*mp == MALLOC_FIRST) { /* Page allocation */ 761 762 /* Check the pointer */ 763 if ((u_long)ptr & malloc_pagemask) { 764 wrtwarning("modified (page-) pointer.\n"); 765 return 0; 766 } 767 768 /* Find the size in bytes */ 769 for (osize = malloc_pagesize; *++mp == MALLOC_FOLLOW;) 770 osize += malloc_pagesize; 771 772 if (!malloc_realloc && /* unless we have to, */ 773 size <= osize && /* .. or are too small, */ 774 size > (osize - malloc_pagesize)) { /* .. or can free a page, */ 775 return ptr; /* don't do anything. */ 776 } 777 778 } else if (*mp >= MALLOC_MAGIC) { /* Chunk allocation */ 779 780 /* Check the pointer for sane values */ 781 if (((u_long)ptr & ((*mp)->size-1))) { 782 wrtwarning("modified (chunk-) pointer.\n"); 783 return 0; 784 } 785 786 /* Find the chunk index in the page */ 787 i = ((u_long)ptr & malloc_pagemask) >> (*mp)->shift; 788 789 /* Verify that it isn't a free chunk already */ 790 if ((*mp)->bits[i/MALLOC_BITS] & (1<<(i%MALLOC_BITS))) { 791 wrtwarning("chunk is already free.\n"); 792 return 0; 793 } 794 795 osize = (*mp)->size; 796 797 if (!malloc_realloc && /* Unless we have to, */ 798 size < osize && /* ..or are too small, */ 799 (size > osize/2 || /* ..or could use a smaller size, */ 800 osize == malloc_minsize)) { /* ..(if there is one) */ 801 return ptr; /* ..Don't do anything */ 802 } 803 804 } else { 805 wrtwarning("pointer to wrong page.\n"); 806 return 0; 807 } 808 809 p = imalloc(size); 810 811 if (p) { 812 /* copy the lesser of the two sizes, and free the old one */ 813 if (!size || !osize) 814 ; 815 else if (osize < size) 816 memcpy(p, ptr, osize); 817 else 818 memcpy(p, ptr, size); 819 ifree(ptr); 820 } 821 return p; 822 } 823 824 /* 825 * Free a sequence of pages 826 */ 827 828 static __inline__ void 829 free_pages(void *ptr, int idx, struct pginfo *info) 830 { 831 int i; 832 struct pgfree *pf, *pt=0; 833 u_long l; 834 void *tail; 835 836 if (info == MALLOC_FREE) { 837 wrtwarning("page is already free.\n"); 838 return; 839 } 840 841 if (info != MALLOC_FIRST) { 842 wrtwarning("pointer to wrong page.\n"); 843 return; 844 } 845 846 if ((u_long)ptr & malloc_pagemask) { 847 wrtwarning("modified (page-) pointer.\n"); 848 return; 849 } 850 851 /* Count how many pages and mark them free at the same time */ 852 page_dir[idx] = MALLOC_FREE; 853 for (i = 1; page_dir[idx+i] == MALLOC_FOLLOW; i++) 854 page_dir[idx + i] = MALLOC_FREE; 855 856 l = i << malloc_pageshift; 857 858 if (malloc_junk) 859 memset(ptr, SOME_JUNK, l); 860 861 if (malloc_hint) 862 madvise(ptr, l, MADV_FREE); 863 864 tail = (char *)ptr+l; 865 866 /* add to free-list */ 867 if (!px) 868 px = imalloc(sizeof *pt); /* This cannot fail... */ 869 px->page = ptr; 870 px->end = tail; 871 px->size = l; 872 if (!free_list.next) { 873 874 /* Nothing on free list, put this at head */ 875 px->next = free_list.next; 876 px->prev = &free_list; 877 free_list.next = px; 878 pf = px; 879 px = 0; 880 881 } else { 882 883 /* Find the right spot, leave pf pointing to the modified entry. */ 884 tail = (char *)ptr+l; 885 886 for(pf = free_list.next; pf->end < ptr && pf->next; pf = pf->next) 887 ; /* Race ahead here */ 888 889 if (pf->page > tail) { 890 /* Insert before entry */ 891 px->next = pf; 892 px->prev = pf->prev; 893 pf->prev = px; 894 px->prev->next = px; 895 pf = px; 896 px = 0; 897 } else if (pf->end == ptr ) { 898 /* Append to the previous entry */ 899 pf->end = (char *)pf->end + l; 900 pf->size += l; 901 if (pf->next && pf->end == pf->next->page ) { 902 /* And collapse the next too. */ 903 pt = pf->next; 904 pf->end = pt->end; 905 pf->size += pt->size; 906 pf->next = pt->next; 907 if (pf->next) 908 pf->next->prev = pf; 909 } 910 } else if (pf->page == tail) { 911 /* Prepend to entry */ 912 pf->size += l; 913 pf->page = ptr; 914 } else if (!pf->next) { 915 /* Append at tail of chain */ 916 px->next = 0; 917 px->prev = pf; 918 pf->next = px; 919 pf = px; 920 px = 0; 921 } else { 922 wrterror("freelist is destroyed.\n"); 923 } 924 } 925 926 /* Return something to OS ? */ 927 if (!pf->next && /* If we're the last one, */ 928 pf->size > malloc_cache && /* ..and the cache is full, */ 929 pf->end == malloc_brk && /* ..and none behind us, */ 930 malloc_brk == sbrk(0)) { /* ..and it's OK to do... */ 931 932 /* 933 * Keep the cache intact. Notice that the '>' above guarantees that 934 * the pf will always have at least one page afterwards. 935 */ 936 pf->end = (char *)pf->page + malloc_cache; 937 pf->size = malloc_cache; 938 939 brk(pf->end); 940 malloc_brk = pf->end; 941 942 idx = ptr2idx(pf->end); 943 last_idx = idx - 1; 944 945 for(i=idx;i <= last_idx;) 946 page_dir[i++] = MALLOC_NOT_MINE; 947 948 /* XXX: We could realloc/shrink the pagedir here I guess. */ 949 } 950 if (pt) 951 ifree(pt); 952 } 953 954 /* 955 * Free a chunk, and possibly the page it's on, if the page becomes empty. 956 */ 957 958 static __inline__ void 959 free_bytes(void *ptr, int idx, struct pginfo *info) 960 { 961 int i; 962 struct pginfo **mp; 963 void *vp; 964 965 /* Find the chunk number on the page */ 966 i = ((u_long)ptr & malloc_pagemask) >> info->shift; 967 968 if (((u_long)ptr & (info->size-1))) { 969 wrtwarning("modified (chunk-) pointer.\n"); 970 return; 971 } 972 973 if (info->bits[i/MALLOC_BITS] & (1<<(i%MALLOC_BITS))) { 974 wrtwarning("chunk is already free.\n"); 975 return; 976 } 977 978 if (malloc_junk) 979 memset(ptr, SOME_JUNK, info->size); 980 981 info->bits[i/MALLOC_BITS] |= 1<<(i%MALLOC_BITS); 982 info->free++; 983 984 mp = page_dir + info->shift; 985 986 if (info->free == 1) { 987 988 /* Page became non-full */ 989 990 mp = page_dir + info->shift; 991 /* Insert in address order */ 992 while (*mp && (*mp)->next && (*mp)->next->page < info->page) 993 mp = &(*mp)->next; 994 info->next = *mp; 995 *mp = info; 996 return; 997 } 998 999 if (info->free != info->total) 1000 return; 1001 1002 /* Find & remove this page in the queue */ 1003 while (*mp != info) { 1004 mp = &((*mp)->next); 1005 #ifdef MALLOC_EXTRA_SANITY 1006 if (!*mp) 1007 wrterror("(ES): Not on queue\n"); 1008 #endif /* MALLOC_EXTRA_SANITY */ 1009 } 1010 *mp = info->next; 1011 1012 /* Free the page & the info structure if need be */ 1013 page_dir[ptr2idx(info->page)] = MALLOC_FIRST; 1014 vp = info->page; /* Order is important ! */ 1015 if(vp != (void*)info) 1016 ifree(info); 1017 ifree(vp); 1018 } 1019 1020 static void 1021 ifree(void *ptr) 1022 { 1023 struct pginfo *info; 1024 int idx; 1025 1026 /* This is legal */ 1027 if (!ptr) 1028 return; 1029 1030 if (!malloc_started) { 1031 wrtwarning("malloc() has never been called.\n"); 1032 return; 1033 } 1034 1035 /* If we're already sinking, don't make matters any worse. */ 1036 if (suicide) 1037 return; 1038 1039 idx = ptr2idx(ptr); 1040 1041 if (idx < malloc_pageshift) { 1042 wrtwarning("junk pointer, too low to make sense.\n"); 1043 return; 1044 } 1045 1046 if (idx > last_idx) { 1047 wrtwarning("junk pointer, too high to make sense.\n"); 1048 return; 1049 } 1050 1051 info = page_dir[idx]; 1052 1053 if (info < MALLOC_MAGIC) 1054 free_pages(ptr, idx, info); 1055 else 1056 free_bytes(ptr, idx, info); 1057 return; 1058 } 1059 1060 /* 1061 * These are the public exported interface routines. 1062 */ 1063 1064 1065 void * 1066 malloc(size_t size) 1067 { 1068 register void *r; 1069 1070 THREAD_LOCK(); 1071 malloc_func = " in malloc():"; 1072 if (malloc_active++) { 1073 wrtwarning("recursive call.\n"); 1074 malloc_active--; 1075 return (0); 1076 } 1077 if (!malloc_started) 1078 malloc_init(); 1079 if (malloc_sysv && !size) 1080 r = 0; 1081 else 1082 r = imalloc(size); 1083 UTRACE(0, size, r); 1084 malloc_active--; 1085 THREAD_UNLOCK(); 1086 if (r == NULL && (size != 0 || !malloc_sysv)) { 1087 if (malloc_xmalloc) 1088 wrterror("out of memory.\n"); 1089 errno = ENOMEM; 1090 } 1091 return (r); 1092 } 1093 1094 void 1095 free(void *ptr) 1096 { 1097 THREAD_LOCK(); 1098 malloc_func = " in free():"; 1099 if (malloc_active++) { 1100 wrtwarning("recursive call.\n"); 1101 malloc_active--; 1102 return; 1103 } else { 1104 ifree(ptr); 1105 UTRACE(ptr, 0, 0); 1106 } 1107 malloc_active--; 1108 THREAD_UNLOCK(); 1109 return; 1110 } 1111 1112 void * 1113 realloc(void *ptr, size_t size) 1114 { 1115 register void *r; 1116 1117 THREAD_LOCK(); 1118 malloc_func = " in realloc():"; 1119 if (malloc_active++) { 1120 wrtwarning("recursive call.\n"); 1121 malloc_active--; 1122 return (0); 1123 } 1124 if (ptr && !malloc_started) { 1125 wrtwarning("malloc() has never been called.\n"); 1126 ptr = 0; 1127 } 1128 if (!malloc_started) 1129 malloc_init(); 1130 if (malloc_sysv && !size) { 1131 ifree(ptr); 1132 r = 0; 1133 } else if (!ptr) { 1134 r = imalloc(size); 1135 } else { 1136 r = irealloc(ptr, size); 1137 } 1138 UTRACE(ptr, size, r); 1139 malloc_active--; 1140 THREAD_UNLOCK(); 1141 if (r == NULL && (size != 0 || !malloc_sysv)) { 1142 if (malloc_xmalloc) 1143 wrterror("out of memory.\n"); 1144 errno = ENOMEM; 1145 } 1146 return (r); 1147 } 1148