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