Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include "primpl.h" |
michael@0 | 7 | |
michael@0 | 8 | /* |
michael@0 | 9 | ** We override malloc etc. on any platform which has preemption + |
michael@0 | 10 | ** nspr20 user level threads. When we're debugging, we can make our |
michael@0 | 11 | ** version of malloc fail occasionally. |
michael@0 | 12 | */ |
michael@0 | 13 | #ifdef _PR_OVERRIDE_MALLOC |
michael@0 | 14 | |
michael@0 | 15 | /* |
michael@0 | 16 | ** Thread safe version of malloc, calloc, realloc, free |
michael@0 | 17 | */ |
michael@0 | 18 | #include <stdarg.h> |
michael@0 | 19 | |
michael@0 | 20 | #ifdef DEBUG |
michael@0 | 21 | #define SANITY |
michael@0 | 22 | #define EXTRA_SANITY |
michael@0 | 23 | #else |
michael@0 | 24 | #undef SANITY |
michael@0 | 25 | #undef EXTRA_SANITY |
michael@0 | 26 | #endif |
michael@0 | 27 | |
michael@0 | 28 | /* Forward decls */ |
michael@0 | 29 | void *_PR_UnlockedMalloc(size_t size); |
michael@0 | 30 | void _PR_UnlockedFree(void *ptr); |
michael@0 | 31 | void *_PR_UnlockedRealloc(void *ptr, size_t size); |
michael@0 | 32 | void *_PR_UnlockedCalloc(size_t n, size_t elsize); |
michael@0 | 33 | |
michael@0 | 34 | /************************************************************************/ |
michael@0 | 35 | |
michael@0 | 36 | /* |
michael@0 | 37 | * ---------------------------------------------------------------------------- |
michael@0 | 38 | * "THE BEER-WARE LICENSE" (Revision 42): |
michael@0 | 39 | * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you |
michael@0 | 40 | * can do whatever you want with this stuff. If we meet some day, and you think |
michael@0 | 41 | * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp |
michael@0 | 42 | * ---------------------------------------------------------------------------- |
michael@0 | 43 | * |
michael@0 | 44 | */ |
michael@0 | 45 | |
michael@0 | 46 | /* |
michael@0 | 47 | * Defining SANITY will enable some checks which will tell you if the users |
michael@0 | 48 | * program did botch something |
michael@0 | 49 | */ |
michael@0 | 50 | |
michael@0 | 51 | /* |
michael@0 | 52 | * Defining EXTRA_SANITY will enable some checks which are mostly related |
michael@0 | 53 | * to internal conditions in malloc.c |
michael@0 | 54 | */ |
michael@0 | 55 | |
michael@0 | 56 | /* |
michael@0 | 57 | * Very verbose progress on stdout... |
michael@0 | 58 | */ |
michael@0 | 59 | #if 0 |
michael@0 | 60 | # define TRACE(foo) printf foo |
michael@0 | 61 | static int malloc_event; |
michael@0 | 62 | #else |
michael@0 | 63 | # define TRACE(foo) |
michael@0 | 64 | #endif |
michael@0 | 65 | |
michael@0 | 66 | /* XXX Pick a number, any number */ |
michael@0 | 67 | # define malloc_pagesize 4096UL |
michael@0 | 68 | # define malloc_pageshift 12UL |
michael@0 | 69 | |
michael@0 | 70 | #ifdef XP_UNIX |
michael@0 | 71 | #include <stdio.h> |
michael@0 | 72 | #include <stdlib.h> |
michael@0 | 73 | #include <unistd.h> |
michael@0 | 74 | #include <string.h> |
michael@0 | 75 | #include <errno.h> |
michael@0 | 76 | #include <sys/types.h> |
michael@0 | 77 | #include <sys/mman.h> |
michael@0 | 78 | #endif |
michael@0 | 79 | |
michael@0 | 80 | /* |
michael@0 | 81 | * This structure describes a page's worth of chunks. |
michael@0 | 82 | */ |
michael@0 | 83 | |
michael@0 | 84 | struct pginfo { |
michael@0 | 85 | struct pginfo *next; /* next on the free list */ |
michael@0 | 86 | char *page; /* Pointer to the page */ |
michael@0 | 87 | u_short size; /* size of this page's chunks */ |
michael@0 | 88 | u_short shift; /* How far to shift for this size chunks */ |
michael@0 | 89 | u_short free; /* How many free chunks */ |
michael@0 | 90 | u_short total; /* How many chunk */ |
michael@0 | 91 | u_long bits[1]; /* Which chunks are free */ |
michael@0 | 92 | }; |
michael@0 | 93 | |
michael@0 | 94 | struct pgfree { |
michael@0 | 95 | struct pgfree *next; /* next run of free pages */ |
michael@0 | 96 | struct pgfree *prev; /* prev run of free pages */ |
michael@0 | 97 | char *page; /* pointer to free pages */ |
michael@0 | 98 | char *end; /* pointer to end of free pages */ |
michael@0 | 99 | u_long size; /* number of bytes free */ |
michael@0 | 100 | }; |
michael@0 | 101 | |
michael@0 | 102 | /* |
michael@0 | 103 | * How many bits per u_long in the bitmap. |
michael@0 | 104 | * Change only if not 8 bits/byte |
michael@0 | 105 | */ |
michael@0 | 106 | #define MALLOC_BITS (8*sizeof(u_long)) |
michael@0 | 107 | |
michael@0 | 108 | /* |
michael@0 | 109 | * Magic values to put in the page_directory |
michael@0 | 110 | */ |
michael@0 | 111 | #define MALLOC_NOT_MINE ((struct pginfo*) 0) |
michael@0 | 112 | #define MALLOC_FREE ((struct pginfo*) 1) |
michael@0 | 113 | #define MALLOC_FIRST ((struct pginfo*) 2) |
michael@0 | 114 | #define MALLOC_FOLLOW ((struct pginfo*) 3) |
michael@0 | 115 | #define MALLOC_MAGIC ((struct pginfo*) 4) |
michael@0 | 116 | |
michael@0 | 117 | /* |
michael@0 | 118 | * Set to one when malloc_init has been called |
michael@0 | 119 | */ |
michael@0 | 120 | static unsigned initialized; |
michael@0 | 121 | |
michael@0 | 122 | /* |
michael@0 | 123 | * The size of a page. |
michael@0 | 124 | * Must be a integral multiplum of the granularity of mmap(2). |
michael@0 | 125 | * Your toes will curl if it isn't a power of two |
michael@0 | 126 | */ |
michael@0 | 127 | #define malloc_pagemask ((malloc_pagesize)-1) |
michael@0 | 128 | |
michael@0 | 129 | /* |
michael@0 | 130 | * The size of the largest chunk. |
michael@0 | 131 | * Half a page. |
michael@0 | 132 | */ |
michael@0 | 133 | #define malloc_maxsize ((malloc_pagesize)>>1) |
michael@0 | 134 | |
michael@0 | 135 | /* |
michael@0 | 136 | * malloc_pagesize == 1 << malloc_pageshift |
michael@0 | 137 | */ |
michael@0 | 138 | #ifndef malloc_pageshift |
michael@0 | 139 | static unsigned malloc_pageshift; |
michael@0 | 140 | #endif /* malloc_pageshift */ |
michael@0 | 141 | |
michael@0 | 142 | /* |
michael@0 | 143 | * The smallest allocation we bother about. |
michael@0 | 144 | * Must be power of two |
michael@0 | 145 | */ |
michael@0 | 146 | #ifndef malloc_minsize |
michael@0 | 147 | static unsigned malloc_minsize; |
michael@0 | 148 | #endif /* malloc_minsize */ |
michael@0 | 149 | |
michael@0 | 150 | /* |
michael@0 | 151 | * The largest chunk we care about. |
michael@0 | 152 | * Must be smaller than pagesize |
michael@0 | 153 | * Must be power of two |
michael@0 | 154 | */ |
michael@0 | 155 | #ifndef malloc_maxsize |
michael@0 | 156 | static unsigned malloc_maxsize; |
michael@0 | 157 | #endif /* malloc_maxsize */ |
michael@0 | 158 | |
michael@0 | 159 | #ifndef malloc_cache |
michael@0 | 160 | static unsigned malloc_cache; |
michael@0 | 161 | #endif /* malloc_cache */ |
michael@0 | 162 | |
michael@0 | 163 | /* |
michael@0 | 164 | * The offset from pagenumber to index into the page directory |
michael@0 | 165 | */ |
michael@0 | 166 | static u_long malloc_origo; |
michael@0 | 167 | |
michael@0 | 168 | /* |
michael@0 | 169 | * The last index in the page directory we care about |
michael@0 | 170 | */ |
michael@0 | 171 | static u_long last_index; |
michael@0 | 172 | |
michael@0 | 173 | /* |
michael@0 | 174 | * Pointer to page directory. |
michael@0 | 175 | * Allocated "as if with" malloc |
michael@0 | 176 | */ |
michael@0 | 177 | static struct pginfo **page_dir; |
michael@0 | 178 | |
michael@0 | 179 | /* |
michael@0 | 180 | * How many slots in the page directory |
michael@0 | 181 | */ |
michael@0 | 182 | static unsigned malloc_ninfo; |
michael@0 | 183 | |
michael@0 | 184 | /* |
michael@0 | 185 | * Free pages line up here |
michael@0 | 186 | */ |
michael@0 | 187 | static struct pgfree free_list; |
michael@0 | 188 | |
michael@0 | 189 | /* |
michael@0 | 190 | * Abort() if we fail to get VM ? |
michael@0 | 191 | */ |
michael@0 | 192 | static int malloc_abort; |
michael@0 | 193 | |
michael@0 | 194 | #ifdef SANITY |
michael@0 | 195 | /* |
michael@0 | 196 | * Are we trying to die ? |
michael@0 | 197 | */ |
michael@0 | 198 | static int suicide; |
michael@0 | 199 | #endif |
michael@0 | 200 | |
michael@0 | 201 | /* |
michael@0 | 202 | * dump statistics |
michael@0 | 203 | */ |
michael@0 | 204 | static int malloc_stats; |
michael@0 | 205 | |
michael@0 | 206 | /* |
michael@0 | 207 | * always realloc ? |
michael@0 | 208 | */ |
michael@0 | 209 | static int malloc_realloc; |
michael@0 | 210 | |
michael@0 | 211 | /* |
michael@0 | 212 | * my last break. |
michael@0 | 213 | */ |
michael@0 | 214 | static void *malloc_brk; |
michael@0 | 215 | |
michael@0 | 216 | /* |
michael@0 | 217 | * one location cache for free-list holders |
michael@0 | 218 | */ |
michael@0 | 219 | static struct pgfree *px; |
michael@0 | 220 | |
michael@0 | 221 | static int set_pgdir(void *ptr, struct pginfo *info); |
michael@0 | 222 | static int extend_page_directory(u_long index); |
michael@0 | 223 | |
michael@0 | 224 | #ifdef SANITY |
michael@0 | 225 | void |
michael@0 | 226 | malloc_dump(FILE *fd) |
michael@0 | 227 | { |
michael@0 | 228 | struct pginfo **pd; |
michael@0 | 229 | struct pgfree *pf; |
michael@0 | 230 | int j; |
michael@0 | 231 | |
michael@0 | 232 | pd = page_dir; |
michael@0 | 233 | |
michael@0 | 234 | /* print out all the pages */ |
michael@0 | 235 | for(j=0;j<=last_index;j++) { |
michael@0 | 236 | fprintf(fd,"%08lx %5d ",(j+malloc_origo) << malloc_pageshift,j); |
michael@0 | 237 | if (pd[j] == MALLOC_NOT_MINE) { |
michael@0 | 238 | for(j++;j<=last_index && pd[j] == MALLOC_NOT_MINE;j++) |
michael@0 | 239 | ; |
michael@0 | 240 | j--; |
michael@0 | 241 | fprintf(fd,".. %5d not mine\n", j); |
michael@0 | 242 | } else if (pd[j] == MALLOC_FREE) { |
michael@0 | 243 | for(j++;j<=last_index && pd[j] == MALLOC_FREE;j++) |
michael@0 | 244 | ; |
michael@0 | 245 | j--; |
michael@0 | 246 | fprintf(fd,".. %5d free\n", j); |
michael@0 | 247 | } else if (pd[j] == MALLOC_FIRST) { |
michael@0 | 248 | for(j++;j<=last_index && pd[j] == MALLOC_FOLLOW;j++) |
michael@0 | 249 | ; |
michael@0 | 250 | j--; |
michael@0 | 251 | fprintf(fd,".. %5d in use\n", j); |
michael@0 | 252 | } else if (pd[j] < MALLOC_MAGIC) { |
michael@0 | 253 | fprintf(fd,"(%p)\n", pd[j]); |
michael@0 | 254 | } else { |
michael@0 | 255 | fprintf(fd,"%p %d (of %d) x %d @ %p --> %p\n", |
michael@0 | 256 | pd[j],pd[j]->free, pd[j]->total, |
michael@0 | 257 | pd[j]->size, pd[j]->page, pd[j]->next); |
michael@0 | 258 | } |
michael@0 | 259 | } |
michael@0 | 260 | |
michael@0 | 261 | for(pf=free_list.next; pf; pf=pf->next) { |
michael@0 | 262 | fprintf(fd,"Free: @%p [%p...%p[ %ld ->%p <-%p\n", |
michael@0 | 263 | pf,pf->page,pf->end,pf->size,pf->prev,pf->next); |
michael@0 | 264 | if (pf == pf->next) { |
michael@0 | 265 | fprintf(fd,"Free_list loops.\n"); |
michael@0 | 266 | break; |
michael@0 | 267 | } |
michael@0 | 268 | } |
michael@0 | 269 | |
michael@0 | 270 | /* print out various info */ |
michael@0 | 271 | fprintf(fd,"Minsize\t%d\n",malloc_minsize); |
michael@0 | 272 | fprintf(fd,"Maxsize\t%ld\n",malloc_maxsize); |
michael@0 | 273 | fprintf(fd,"Pagesize\t%ld\n",malloc_pagesize); |
michael@0 | 274 | fprintf(fd,"Pageshift\t%ld\n",malloc_pageshift); |
michael@0 | 275 | fprintf(fd,"FirstPage\t%ld\n",malloc_origo); |
michael@0 | 276 | fprintf(fd,"LastPage\t%ld %lx\n",last_index+malloc_pageshift, |
michael@0 | 277 | (last_index + malloc_pageshift) << malloc_pageshift); |
michael@0 | 278 | fprintf(fd,"Break\t%ld\n",(u_long)sbrk(0) >> malloc_pageshift); |
michael@0 | 279 | } |
michael@0 | 280 | |
michael@0 | 281 | static void wrterror(char *fmt, ...) |
michael@0 | 282 | { |
michael@0 | 283 | char *q = "malloc() error: "; |
michael@0 | 284 | char buf[100]; |
michael@0 | 285 | va_list ap; |
michael@0 | 286 | |
michael@0 | 287 | suicide = 1; |
michael@0 | 288 | |
michael@0 | 289 | va_start(ap, fmt); |
michael@0 | 290 | PR_vsnprintf(buf, sizeof(buf), fmt, ap); |
michael@0 | 291 | va_end(ap); |
michael@0 | 292 | fputs(q, stderr); |
michael@0 | 293 | fputs(buf, stderr); |
michael@0 | 294 | |
michael@0 | 295 | malloc_dump(stderr); |
michael@0 | 296 | PR_Abort(); |
michael@0 | 297 | } |
michael@0 | 298 | |
michael@0 | 299 | static void wrtwarning(char *fmt, ...) |
michael@0 | 300 | { |
michael@0 | 301 | char *q = "malloc() warning: "; |
michael@0 | 302 | char buf[100]; |
michael@0 | 303 | va_list ap; |
michael@0 | 304 | |
michael@0 | 305 | va_start(ap, fmt); |
michael@0 | 306 | PR_vsnprintf(buf, sizeof(buf), fmt, ap); |
michael@0 | 307 | va_end(ap); |
michael@0 | 308 | fputs(q, stderr); |
michael@0 | 309 | fputs(buf, stderr); |
michael@0 | 310 | } |
michael@0 | 311 | #endif /* SANITY */ |
michael@0 | 312 | |
michael@0 | 313 | |
michael@0 | 314 | /* |
michael@0 | 315 | * Allocate a number of pages from the OS |
michael@0 | 316 | */ |
michael@0 | 317 | static caddr_t |
michael@0 | 318 | map_pages(int pages, int update) |
michael@0 | 319 | { |
michael@0 | 320 | caddr_t result,tail; |
michael@0 | 321 | |
michael@0 | 322 | result = ((caddr_t)sbrk(0)) + malloc_pagemask - 1; |
michael@0 | 323 | result = (caddr_t) ((u_long)result & ~malloc_pagemask); |
michael@0 | 324 | tail = result + (pages << malloc_pageshift); |
michael@0 | 325 | if (!brk(tail)) { |
michael@0 | 326 | last_index = ((u_long)tail >> malloc_pageshift) - malloc_origo -1; |
michael@0 | 327 | malloc_brk = tail; |
michael@0 | 328 | TRACE(("%6d S %p .. %p\n",malloc_event++, result, tail)); |
michael@0 | 329 | if (!update || last_index < malloc_ninfo || |
michael@0 | 330 | extend_page_directory(last_index)) |
michael@0 | 331 | return result; |
michael@0 | 332 | } |
michael@0 | 333 | TRACE(("%6d s %d %p %d\n",malloc_event++,pages,sbrk(0),errno)); |
michael@0 | 334 | #ifdef EXTRA_SANITY |
michael@0 | 335 | wrterror("map_pages fails\n"); |
michael@0 | 336 | #endif |
michael@0 | 337 | return 0; |
michael@0 | 338 | } |
michael@0 | 339 | |
michael@0 | 340 | #define set_bit(_pi,_bit) \ |
michael@0 | 341 | (_pi)->bits[(_bit)/MALLOC_BITS] |= 1L<<((_bit)%MALLOC_BITS) |
michael@0 | 342 | |
michael@0 | 343 | #define clr_bit(_pi,_bit) \ |
michael@0 | 344 | (_pi)->bits[(_bit)/MALLOC_BITS] &= ~(1L<<((_bit)%MALLOC_BITS)); |
michael@0 | 345 | |
michael@0 | 346 | #define tst_bit(_pi,_bit) \ |
michael@0 | 347 | ((_pi)->bits[(_bit)/MALLOC_BITS] & (1L<<((_bit)%MALLOC_BITS))) |
michael@0 | 348 | |
michael@0 | 349 | /* |
michael@0 | 350 | * Extend page directory |
michael@0 | 351 | */ |
michael@0 | 352 | static int |
michael@0 | 353 | extend_page_directory(u_long index) |
michael@0 | 354 | { |
michael@0 | 355 | struct pginfo **young, **old; |
michael@0 | 356 | int i; |
michael@0 | 357 | |
michael@0 | 358 | TRACE(("%6d E %lu\n",malloc_event++,index)); |
michael@0 | 359 | |
michael@0 | 360 | /* Make it this many pages */ |
michael@0 | 361 | i = index * sizeof *page_dir; |
michael@0 | 362 | i /= malloc_pagesize; |
michael@0 | 363 | i += 2; |
michael@0 | 364 | |
michael@0 | 365 | /* Get new pages, if you used this much mem you don't care :-) */ |
michael@0 | 366 | young = (struct pginfo**) map_pages(i,0); |
michael@0 | 367 | if (!young) |
michael@0 | 368 | return 0; |
michael@0 | 369 | |
michael@0 | 370 | /* Copy the old stuff */ |
michael@0 | 371 | memset(young, 0, i * malloc_pagesize); |
michael@0 | 372 | memcpy(young, page_dir, |
michael@0 | 373 | malloc_ninfo * sizeof *page_dir); |
michael@0 | 374 | |
michael@0 | 375 | /* register the new size */ |
michael@0 | 376 | malloc_ninfo = i * malloc_pagesize / sizeof *page_dir; |
michael@0 | 377 | |
michael@0 | 378 | /* swap the pointers */ |
michael@0 | 379 | old = page_dir; |
michael@0 | 380 | page_dir = young; |
michael@0 | 381 | |
michael@0 | 382 | /* Mark the pages */ |
michael@0 | 383 | index = ((u_long)young >> malloc_pageshift) - malloc_origo; |
michael@0 | 384 | page_dir[index] = MALLOC_FIRST; |
michael@0 | 385 | while (--i) { |
michael@0 | 386 | page_dir[++index] = MALLOC_FOLLOW; |
michael@0 | 387 | } |
michael@0 | 388 | |
michael@0 | 389 | /* Now free the old stuff */ |
michael@0 | 390 | _PR_UnlockedFree(old); |
michael@0 | 391 | return 1; |
michael@0 | 392 | } |
michael@0 | 393 | |
michael@0 | 394 | /* |
michael@0 | 395 | * Set entry in page directory. |
michael@0 | 396 | * Extend page directory if need be. |
michael@0 | 397 | */ |
michael@0 | 398 | static int |
michael@0 | 399 | set_pgdir(void *ptr, struct pginfo *info) |
michael@0 | 400 | { |
michael@0 | 401 | u_long index = ((u_long)ptr >> malloc_pageshift) - malloc_origo; |
michael@0 | 402 | |
michael@0 | 403 | if (index >= malloc_ninfo && !extend_page_directory(index)) |
michael@0 | 404 | return 0; |
michael@0 | 405 | page_dir[index] = info; |
michael@0 | 406 | return 1; |
michael@0 | 407 | } |
michael@0 | 408 | |
michael@0 | 409 | /* |
michael@0 | 410 | * Initialize the world |
michael@0 | 411 | */ |
michael@0 | 412 | static void |
michael@0 | 413 | malloc_init (void) |
michael@0 | 414 | { |
michael@0 | 415 | int i; |
michael@0 | 416 | char *p; |
michael@0 | 417 | |
michael@0 | 418 | TRACE(("%6d I\n",malloc_event++)); |
michael@0 | 419 | #ifdef DEBUG |
michael@0 | 420 | for (p=getenv("MALLOC_OPTIONS"); p && *p; p++) { |
michael@0 | 421 | switch (*p) { |
michael@0 | 422 | case 'a': malloc_abort = 0; break; |
michael@0 | 423 | case 'A': malloc_abort = 1; break; |
michael@0 | 424 | case 'd': malloc_stats = 0; break; |
michael@0 | 425 | case 'D': malloc_stats = 1; break; |
michael@0 | 426 | case 'r': malloc_realloc = 0; break; |
michael@0 | 427 | case 'R': malloc_realloc = 1; break; |
michael@0 | 428 | default: |
michael@0 | 429 | wrtwarning("Unknown chars in MALLOC_OPTIONS\n"); |
michael@0 | 430 | break; |
michael@0 | 431 | } |
michael@0 | 432 | } |
michael@0 | 433 | #endif |
michael@0 | 434 | |
michael@0 | 435 | #ifndef malloc_pagesize |
michael@0 | 436 | /* determine our pagesize */ |
michael@0 | 437 | malloc_pagesize = getpagesize(); |
michael@0 | 438 | #endif /* malloc_pagesize */ |
michael@0 | 439 | |
michael@0 | 440 | #ifndef malloc_pageshift |
michael@0 | 441 | /* determine how much we shift by to get there */ |
michael@0 | 442 | for (i = malloc_pagesize; i > 1; i >>= 1) |
michael@0 | 443 | malloc_pageshift++; |
michael@0 | 444 | #endif /* malloc_pageshift */ |
michael@0 | 445 | |
michael@0 | 446 | #ifndef malloc_cache |
michael@0 | 447 | malloc_cache = 50 << malloc_pageshift; |
michael@0 | 448 | #endif /* malloc_cache */ |
michael@0 | 449 | |
michael@0 | 450 | #ifndef malloc_minsize |
michael@0 | 451 | /* |
michael@0 | 452 | * find the smallest size allocation we will bother about. |
michael@0 | 453 | * this is determined as the smallest allocation that can hold |
michael@0 | 454 | * it's own pginfo; |
michael@0 | 455 | */ |
michael@0 | 456 | i = 2; |
michael@0 | 457 | for(;;) { |
michael@0 | 458 | int j; |
michael@0 | 459 | |
michael@0 | 460 | /* Figure out the size of the bits */ |
michael@0 | 461 | j = malloc_pagesize/i; |
michael@0 | 462 | j /= 8; |
michael@0 | 463 | if (j < sizeof(u_long)) |
michael@0 | 464 | j = sizeof (u_long); |
michael@0 | 465 | if (sizeof(struct pginfo) + j - sizeof (u_long) <= i) |
michael@0 | 466 | break; |
michael@0 | 467 | i += i; |
michael@0 | 468 | } |
michael@0 | 469 | malloc_minsize = i; |
michael@0 | 470 | #endif /* malloc_minsize */ |
michael@0 | 471 | |
michael@0 | 472 | |
michael@0 | 473 | /* Allocate one page for the page directory */ |
michael@0 | 474 | page_dir = (struct pginfo **) map_pages(1,0); |
michael@0 | 475 | #ifdef SANITY |
michael@0 | 476 | if (!page_dir) |
michael@0 | 477 | wrterror("fatal: my first mmap failed. (check limits ?)\n"); |
michael@0 | 478 | #endif |
michael@0 | 479 | |
michael@0 | 480 | /* |
michael@0 | 481 | * We need a maximum of malloc_pageshift buckets, steal these from the |
michael@0 | 482 | * front of the page_directory; |
michael@0 | 483 | */ |
michael@0 | 484 | malloc_origo = (u_long) page_dir >> malloc_pageshift; |
michael@0 | 485 | malloc_origo -= malloc_pageshift; |
michael@0 | 486 | |
michael@0 | 487 | /* Clear it */ |
michael@0 | 488 | memset(page_dir,0,malloc_pagesize); |
michael@0 | 489 | |
michael@0 | 490 | /* Find out how much it tells us */ |
michael@0 | 491 | malloc_ninfo = malloc_pagesize / sizeof *page_dir; |
michael@0 | 492 | |
michael@0 | 493 | /* Plug the page directory into itself */ |
michael@0 | 494 | i = set_pgdir(page_dir,MALLOC_FIRST); |
michael@0 | 495 | #ifdef SANITY |
michael@0 | 496 | if (!i) |
michael@0 | 497 | wrterror("fatal: couldn't set myself in the page directory\n"); |
michael@0 | 498 | #endif |
michael@0 | 499 | |
michael@0 | 500 | /* Been here, done that */ |
michael@0 | 501 | initialized++; |
michael@0 | 502 | } |
michael@0 | 503 | |
michael@0 | 504 | /* |
michael@0 | 505 | * Allocate a number of complete pages |
michael@0 | 506 | */ |
michael@0 | 507 | static void *malloc_pages(size_t size) |
michael@0 | 508 | { |
michael@0 | 509 | void *p,*delay_free = 0; |
michael@0 | 510 | int i; |
michael@0 | 511 | struct pgfree *pf; |
michael@0 | 512 | u_long index; |
michael@0 | 513 | |
michael@0 | 514 | /* How many pages ? */ |
michael@0 | 515 | size += (malloc_pagesize-1); |
michael@0 | 516 | size &= ~malloc_pagemask; |
michael@0 | 517 | |
michael@0 | 518 | p = 0; |
michael@0 | 519 | /* Look for free pages before asking for more */ |
michael@0 | 520 | for(pf = free_list.next; pf; pf = pf->next) { |
michael@0 | 521 | #ifdef EXTRA_SANITY |
michael@0 | 522 | if (pf->page == pf->end) |
michael@0 | 523 | wrterror("zero entry on free_list\n"); |
michael@0 | 524 | if (pf->page > pf->end) { |
michael@0 | 525 | TRACE(("%6d !s %p %p %p <%d>\n",malloc_event++, |
michael@0 | 526 | pf,pf->page,pf->end,__LINE__)); |
michael@0 | 527 | wrterror("sick entry on free_list\n"); |
michael@0 | 528 | } |
michael@0 | 529 | if ((void*)pf->page >= (void*)sbrk(0)) |
michael@0 | 530 | wrterror("entry on free_list past brk\n"); |
michael@0 | 531 | if (page_dir[((u_long)pf->page >> malloc_pageshift) - malloc_origo] |
michael@0 | 532 | != MALLOC_FREE) { |
michael@0 | 533 | TRACE(("%6d !f %p %p %p <%d>\n",malloc_event++, |
michael@0 | 534 | pf,pf->page,pf->end,__LINE__)); |
michael@0 | 535 | wrterror("non-free first page on free-list\n"); |
michael@0 | 536 | } |
michael@0 | 537 | if (page_dir[((u_long)pf->end >> malloc_pageshift) - 1 - malloc_origo] |
michael@0 | 538 | != MALLOC_FREE) |
michael@0 | 539 | wrterror("non-free last page on free-list\n"); |
michael@0 | 540 | #endif /* EXTRA_SANITY */ |
michael@0 | 541 | if (pf->size < size) |
michael@0 | 542 | continue; |
michael@0 | 543 | else if (pf->size == size) { |
michael@0 | 544 | p = pf->page; |
michael@0 | 545 | if (pf->next) |
michael@0 | 546 | pf->next->prev = pf->prev; |
michael@0 | 547 | pf->prev->next = pf->next; |
michael@0 | 548 | delay_free = pf; |
michael@0 | 549 | break; |
michael@0 | 550 | } else { |
michael@0 | 551 | p = pf->page; |
michael@0 | 552 | pf->page += size; |
michael@0 | 553 | pf->size -= size; |
michael@0 | 554 | break; |
michael@0 | 555 | } |
michael@0 | 556 | } |
michael@0 | 557 | #ifdef EXTRA_SANITY |
michael@0 | 558 | if (p && page_dir[((u_long)p >> malloc_pageshift) - malloc_origo] |
michael@0 | 559 | != MALLOC_FREE) { |
michael@0 | 560 | wrterror("allocated non-free page on free-list\n"); |
michael@0 | 561 | } |
michael@0 | 562 | #endif /* EXTRA_SANITY */ |
michael@0 | 563 | |
michael@0 | 564 | size >>= malloc_pageshift; |
michael@0 | 565 | |
michael@0 | 566 | /* Map new pages */ |
michael@0 | 567 | if (!p) |
michael@0 | 568 | p = map_pages(size,1); |
michael@0 | 569 | |
michael@0 | 570 | if (p) { |
michael@0 | 571 | /* Mark the pages in the directory */ |
michael@0 | 572 | index = ((u_long)p >> malloc_pageshift) - malloc_origo; |
michael@0 | 573 | page_dir[index] = MALLOC_FIRST; |
michael@0 | 574 | for (i=1;i<size;i++) |
michael@0 | 575 | page_dir[index+i] = MALLOC_FOLLOW; |
michael@0 | 576 | } |
michael@0 | 577 | if (delay_free) { |
michael@0 | 578 | if (!px) |
michael@0 | 579 | px = (struct pgfree*)delay_free; |
michael@0 | 580 | else |
michael@0 | 581 | _PR_UnlockedFree(delay_free); |
michael@0 | 582 | } |
michael@0 | 583 | return p; |
michael@0 | 584 | } |
michael@0 | 585 | |
michael@0 | 586 | /* |
michael@0 | 587 | * Allocate a page of fragments |
michael@0 | 588 | */ |
michael@0 | 589 | |
michael@0 | 590 | static int |
michael@0 | 591 | malloc_make_chunks(int bits) |
michael@0 | 592 | { |
michael@0 | 593 | struct pginfo *bp; |
michael@0 | 594 | void *pp; |
michael@0 | 595 | int i,k,l; |
michael@0 | 596 | |
michael@0 | 597 | /* Allocate a new bucket */ |
michael@0 | 598 | pp = malloc_pages(malloc_pagesize); |
michael@0 | 599 | if (!pp) |
michael@0 | 600 | return 0; |
michael@0 | 601 | l = sizeof *bp - sizeof(u_long); |
michael@0 | 602 | l += sizeof(u_long) * |
michael@0 | 603 | (((malloc_pagesize >> bits)+MALLOC_BITS-1) / MALLOC_BITS); |
michael@0 | 604 | if ((1<<(bits)) <= l+l) { |
michael@0 | 605 | bp = (struct pginfo *)pp; |
michael@0 | 606 | } else { |
michael@0 | 607 | bp = (struct pginfo *)_PR_UnlockedMalloc(l); |
michael@0 | 608 | } |
michael@0 | 609 | if (!bp) |
michael@0 | 610 | return 0; |
michael@0 | 611 | bp->size = (1<<bits); |
michael@0 | 612 | bp->shift = bits; |
michael@0 | 613 | bp->total = bp->free = malloc_pagesize >> bits; |
michael@0 | 614 | bp->next = page_dir[bits]; |
michael@0 | 615 | bp->page = (char*)pp; |
michael@0 | 616 | i = set_pgdir(pp,bp); |
michael@0 | 617 | if (!i) |
michael@0 | 618 | return 0; |
michael@0 | 619 | |
michael@0 | 620 | /* We can safely assume that there is nobody in this chain */ |
michael@0 | 621 | page_dir[bits] = bp; |
michael@0 | 622 | |
michael@0 | 623 | /* set all valid bits in the bits */ |
michael@0 | 624 | k = bp->total; |
michael@0 | 625 | i = 0; |
michael@0 | 626 | /* |
michael@0 | 627 | for(;k-i >= MALLOC_BITS; i += MALLOC_BITS) |
michael@0 | 628 | bp->bits[i / MALLOC_BITS] = ~0; |
michael@0 | 629 | */ |
michael@0 | 630 | for(; i < k; i++) |
michael@0 | 631 | set_bit(bp,i); |
michael@0 | 632 | |
michael@0 | 633 | if (bp != pp) |
michael@0 | 634 | return 1; |
michael@0 | 635 | |
michael@0 | 636 | /* We may have used the first ones already */ |
michael@0 | 637 | for(i=0;l > 0;i++) { |
michael@0 | 638 | clr_bit(bp,i); |
michael@0 | 639 | bp->free--; |
michael@0 | 640 | bp->total--; |
michael@0 | 641 | l -= (1 << bits); |
michael@0 | 642 | } |
michael@0 | 643 | return 1; |
michael@0 | 644 | } |
michael@0 | 645 | |
michael@0 | 646 | /* |
michael@0 | 647 | * Allocate a fragment |
michael@0 | 648 | */ |
michael@0 | 649 | static void *malloc_bytes(size_t size) |
michael@0 | 650 | { |
michael@0 | 651 | size_t s; |
michael@0 | 652 | int j; |
michael@0 | 653 | struct pginfo *bp; |
michael@0 | 654 | int k; |
michael@0 | 655 | u_long *lp, bf; |
michael@0 | 656 | |
michael@0 | 657 | /* Don't bother with anything less than this */ |
michael@0 | 658 | if (size < malloc_minsize) { |
michael@0 | 659 | size = malloc_minsize; |
michael@0 | 660 | } |
michael@0 | 661 | |
michael@0 | 662 | /* Find the right bucket */ |
michael@0 | 663 | j = 1; |
michael@0 | 664 | s = size - 1; |
michael@0 | 665 | while (s >>= 1) { |
michael@0 | 666 | j++; |
michael@0 | 667 | } |
michael@0 | 668 | |
michael@0 | 669 | /* If it's empty, make a page more of that size chunks */ |
michael@0 | 670 | if (!page_dir[j] && !malloc_make_chunks(j)) |
michael@0 | 671 | return 0; |
michael@0 | 672 | |
michael@0 | 673 | /* Find first word of bitmap which isn't empty */ |
michael@0 | 674 | bp = page_dir[j]; |
michael@0 | 675 | for (lp = bp->bits; !*lp; lp++) |
michael@0 | 676 | ; |
michael@0 | 677 | |
michael@0 | 678 | /* Find that bit */ |
michael@0 | 679 | bf = *lp; |
michael@0 | 680 | k = 0; |
michael@0 | 681 | while ((bf & 1) == 0) { |
michael@0 | 682 | bf >>= 1; |
michael@0 | 683 | k++; |
michael@0 | 684 | } |
michael@0 | 685 | |
michael@0 | 686 | *lp ^= 1L<<k; /* clear it */ |
michael@0 | 687 | bp->free--; |
michael@0 | 688 | if (!bp->free) { |
michael@0 | 689 | page_dir[j] = bp->next; |
michael@0 | 690 | bp->next = 0; |
michael@0 | 691 | } |
michael@0 | 692 | k += (lp - bp->bits)*MALLOC_BITS; |
michael@0 | 693 | return bp->page + (k << bp->shift); |
michael@0 | 694 | } |
michael@0 | 695 | |
michael@0 | 696 | void *_PR_UnlockedMalloc(size_t size) |
michael@0 | 697 | { |
michael@0 | 698 | void *result; |
michael@0 | 699 | |
michael@0 | 700 | /* Round up to a multiple of 8 bytes */ |
michael@0 | 701 | if (size & 7) { |
michael@0 | 702 | size = size + 8 - (size & 7); |
michael@0 | 703 | } |
michael@0 | 704 | |
michael@0 | 705 | if (!initialized) |
michael@0 | 706 | malloc_init(); |
michael@0 | 707 | |
michael@0 | 708 | #ifdef SANITY |
michael@0 | 709 | if (suicide) |
michael@0 | 710 | PR_Abort(); |
michael@0 | 711 | #endif |
michael@0 | 712 | |
michael@0 | 713 | if (size <= malloc_maxsize) |
michael@0 | 714 | result = malloc_bytes(size); |
michael@0 | 715 | else |
michael@0 | 716 | result = malloc_pages(size); |
michael@0 | 717 | #ifdef SANITY |
michael@0 | 718 | if (malloc_abort && !result) |
michael@0 | 719 | wrterror("malloc() returns NULL\n"); |
michael@0 | 720 | #endif |
michael@0 | 721 | TRACE(("%6d M %p %d\n",malloc_event++,result,size)); |
michael@0 | 722 | |
michael@0 | 723 | return result; |
michael@0 | 724 | } |
michael@0 | 725 | |
michael@0 | 726 | void *_PR_UnlockedMemalign(size_t alignment, size_t size) |
michael@0 | 727 | { |
michael@0 | 728 | void *result; |
michael@0 | 729 | |
michael@0 | 730 | /* |
michael@0 | 731 | * alignment has to be a power of 2 |
michael@0 | 732 | */ |
michael@0 | 733 | |
michael@0 | 734 | if ((size <= alignment) && (alignment <= malloc_maxsize)) |
michael@0 | 735 | size = alignment; |
michael@0 | 736 | else |
michael@0 | 737 | size += alignment - 1; |
michael@0 | 738 | |
michael@0 | 739 | /* Round up to a multiple of 8 bytes */ |
michael@0 | 740 | if (size & 7) { |
michael@0 | 741 | size = size + 8 - (size & 7); |
michael@0 | 742 | } |
michael@0 | 743 | |
michael@0 | 744 | if (!initialized) |
michael@0 | 745 | malloc_init(); |
michael@0 | 746 | |
michael@0 | 747 | #ifdef SANITY |
michael@0 | 748 | if (suicide) |
michael@0 | 749 | abort(); |
michael@0 | 750 | #endif |
michael@0 | 751 | |
michael@0 | 752 | if (size <= malloc_maxsize) |
michael@0 | 753 | result = malloc_bytes(size); |
michael@0 | 754 | else |
michael@0 | 755 | result = malloc_pages(size); |
michael@0 | 756 | #ifdef SANITY |
michael@0 | 757 | if (malloc_abort && !result) |
michael@0 | 758 | wrterror("malloc() returns NULL\n"); |
michael@0 | 759 | #endif |
michael@0 | 760 | TRACE(("%6d A %p %d\n",malloc_event++,result,size)); |
michael@0 | 761 | |
michael@0 | 762 | if ((u_long)result & (alignment - 1)) |
michael@0 | 763 | return ((void *)(((u_long)result + alignment) & ~(alignment - 1))); |
michael@0 | 764 | else |
michael@0 | 765 | return result; |
michael@0 | 766 | } |
michael@0 | 767 | |
michael@0 | 768 | void *_PR_UnlockedCalloc(size_t n, size_t nelem) |
michael@0 | 769 | { |
michael@0 | 770 | void *p; |
michael@0 | 771 | |
michael@0 | 772 | /* Compute total size and then round up to a double word amount */ |
michael@0 | 773 | n *= nelem; |
michael@0 | 774 | if (n & 7) { |
michael@0 | 775 | n = n + 8 - (n & 7); |
michael@0 | 776 | } |
michael@0 | 777 | |
michael@0 | 778 | /* Get the memory */ |
michael@0 | 779 | p = _PR_UnlockedMalloc(n); |
michael@0 | 780 | if (p) { |
michael@0 | 781 | /* Zero it */ |
michael@0 | 782 | memset(p, 0, n); |
michael@0 | 783 | } |
michael@0 | 784 | return p; |
michael@0 | 785 | } |
michael@0 | 786 | |
michael@0 | 787 | /* |
michael@0 | 788 | * Change an allocation's size |
michael@0 | 789 | */ |
michael@0 | 790 | void *_PR_UnlockedRealloc(void *ptr, size_t size) |
michael@0 | 791 | { |
michael@0 | 792 | void *p; |
michael@0 | 793 | u_long osize,page,index,tmp_index; |
michael@0 | 794 | struct pginfo **mp; |
michael@0 | 795 | |
michael@0 | 796 | if (!initialized) |
michael@0 | 797 | malloc_init(); |
michael@0 | 798 | |
michael@0 | 799 | #ifdef SANITY |
michael@0 | 800 | if (suicide) |
michael@0 | 801 | PR_Abort(); |
michael@0 | 802 | #endif |
michael@0 | 803 | |
michael@0 | 804 | /* used as free() */ |
michael@0 | 805 | TRACE(("%6d R %p %d\n",malloc_event++, ptr, size)); |
michael@0 | 806 | if (ptr && !size) { |
michael@0 | 807 | _PR_UnlockedFree(ptr); |
michael@0 | 808 | return _PR_UnlockedMalloc (1); |
michael@0 | 809 | } |
michael@0 | 810 | |
michael@0 | 811 | /* used as malloc() */ |
michael@0 | 812 | if (!ptr) { |
michael@0 | 813 | p = _PR_UnlockedMalloc(size); |
michael@0 | 814 | return p; |
michael@0 | 815 | } |
michael@0 | 816 | |
michael@0 | 817 | /* Find the page directory entry for the page in question */ |
michael@0 | 818 | page = (u_long)ptr >> malloc_pageshift; |
michael@0 | 819 | index = page - malloc_origo; |
michael@0 | 820 | |
michael@0 | 821 | /* |
michael@0 | 822 | * check if memory was allocated by memalign |
michael@0 | 823 | */ |
michael@0 | 824 | tmp_index = index; |
michael@0 | 825 | while (page_dir[tmp_index] == MALLOC_FOLLOW) |
michael@0 | 826 | tmp_index--; |
michael@0 | 827 | if (tmp_index != index) { |
michael@0 | 828 | /* |
michael@0 | 829 | * memalign-allocated memory |
michael@0 | 830 | */ |
michael@0 | 831 | index = tmp_index; |
michael@0 | 832 | page = index + malloc_origo; |
michael@0 | 833 | ptr = (void *) (page << malloc_pageshift); |
michael@0 | 834 | } |
michael@0 | 835 | TRACE(("%6d R2 %p %d\n",malloc_event++, ptr, size)); |
michael@0 | 836 | |
michael@0 | 837 | /* make sure it makes sense in some fashion */ |
michael@0 | 838 | if (index < malloc_pageshift || index > last_index) { |
michael@0 | 839 | #ifdef SANITY |
michael@0 | 840 | wrtwarning("junk pointer passed to realloc()\n"); |
michael@0 | 841 | #endif |
michael@0 | 842 | return 0; |
michael@0 | 843 | } |
michael@0 | 844 | |
michael@0 | 845 | /* find the size of that allocation, and see if we need to relocate */ |
michael@0 | 846 | mp = &page_dir[index]; |
michael@0 | 847 | if (*mp == MALLOC_FIRST) { |
michael@0 | 848 | osize = malloc_pagesize; |
michael@0 | 849 | while (mp[1] == MALLOC_FOLLOW) { |
michael@0 | 850 | osize += malloc_pagesize; |
michael@0 | 851 | mp++; |
michael@0 | 852 | } |
michael@0 | 853 | if (!malloc_realloc && |
michael@0 | 854 | size < osize && |
michael@0 | 855 | size > malloc_maxsize && |
michael@0 | 856 | size > (osize - malloc_pagesize)) { |
michael@0 | 857 | return ptr; |
michael@0 | 858 | } |
michael@0 | 859 | } else if (*mp >= MALLOC_MAGIC) { |
michael@0 | 860 | osize = (*mp)->size; |
michael@0 | 861 | if (!malloc_realloc && |
michael@0 | 862 | size < osize && |
michael@0 | 863 | (size > (*mp)->size/2 || (*mp)->size == malloc_minsize)) { |
michael@0 | 864 | return ptr; |
michael@0 | 865 | } |
michael@0 | 866 | } else { |
michael@0 | 867 | #ifdef SANITY |
michael@0 | 868 | wrterror("realloc() of wrong page.\n"); |
michael@0 | 869 | #endif |
michael@0 | 870 | } |
michael@0 | 871 | |
michael@0 | 872 | /* try to reallocate */ |
michael@0 | 873 | p = _PR_UnlockedMalloc(size); |
michael@0 | 874 | |
michael@0 | 875 | if (p) { |
michael@0 | 876 | /* copy the lesser of the two sizes */ |
michael@0 | 877 | if (osize < size) |
michael@0 | 878 | memcpy(p,ptr,osize); |
michael@0 | 879 | else |
michael@0 | 880 | memcpy(p,ptr,size); |
michael@0 | 881 | _PR_UnlockedFree(ptr); |
michael@0 | 882 | } |
michael@0 | 883 | #ifdef DEBUG |
michael@0 | 884 | else if (malloc_abort) |
michael@0 | 885 | wrterror("realloc() returns NULL\n"); |
michael@0 | 886 | #endif |
michael@0 | 887 | |
michael@0 | 888 | return p; |
michael@0 | 889 | } |
michael@0 | 890 | |
michael@0 | 891 | /* |
michael@0 | 892 | * Free a sequence of pages |
michael@0 | 893 | */ |
michael@0 | 894 | |
michael@0 | 895 | static void |
michael@0 | 896 | free_pages(char *ptr, u_long page, int index, struct pginfo *info) |
michael@0 | 897 | { |
michael@0 | 898 | int i; |
michael@0 | 899 | struct pgfree *pf,*pt; |
michael@0 | 900 | u_long l; |
michael@0 | 901 | char *tail; |
michael@0 | 902 | |
michael@0 | 903 | TRACE(("%6d FP %p %d\n",malloc_event++, ptr, page)); |
michael@0 | 904 | /* Is it free already ? */ |
michael@0 | 905 | if (info == MALLOC_FREE) { |
michael@0 | 906 | #ifdef SANITY |
michael@0 | 907 | wrtwarning("freeing free page at %p.\n", ptr); |
michael@0 | 908 | #endif |
michael@0 | 909 | return; |
michael@0 | 910 | } |
michael@0 | 911 | |
michael@0 | 912 | #ifdef SANITY |
michael@0 | 913 | /* Is it not the right place to begin ? */ |
michael@0 | 914 | if (info != MALLOC_FIRST) |
michael@0 | 915 | wrterror("freeing wrong page.\n"); |
michael@0 | 916 | |
michael@0 | 917 | /* Is this really a pointer to a page ? */ |
michael@0 | 918 | if ((u_long)ptr & malloc_pagemask) |
michael@0 | 919 | wrterror("freeing messed up page pointer.\n"); |
michael@0 | 920 | #endif |
michael@0 | 921 | |
michael@0 | 922 | /* Count how many pages it is anyway */ |
michael@0 | 923 | page_dir[index] = MALLOC_FREE; |
michael@0 | 924 | for (i = 1; page_dir[index+i] == MALLOC_FOLLOW; i++) |
michael@0 | 925 | page_dir[index + i] = MALLOC_FREE; |
michael@0 | 926 | |
michael@0 | 927 | l = i << malloc_pageshift; |
michael@0 | 928 | |
michael@0 | 929 | tail = ptr+l; |
michael@0 | 930 | |
michael@0 | 931 | /* add to free-list */ |
michael@0 | 932 | if (!px) |
michael@0 | 933 | px = (struct pgfree*)_PR_UnlockedMalloc(sizeof *pt); |
michael@0 | 934 | /* XXX check success */ |
michael@0 | 935 | px->page = ptr; |
michael@0 | 936 | px->end = tail; |
michael@0 | 937 | px->size = l; |
michael@0 | 938 | if (!free_list.next) { |
michael@0 | 939 | px->next = free_list.next; |
michael@0 | 940 | px->prev = &free_list; |
michael@0 | 941 | free_list.next = px; |
michael@0 | 942 | pf = px; |
michael@0 | 943 | px = 0; |
michael@0 | 944 | } else { |
michael@0 | 945 | tail = ptr+l; |
michael@0 | 946 | for(pf = free_list.next; pf->next && pf->end < ptr; pf = pf->next) |
michael@0 | 947 | ; |
michael@0 | 948 | for(; pf; pf = pf->next) { |
michael@0 | 949 | if (pf->end == ptr ) { |
michael@0 | 950 | /* append to entry */ |
michael@0 | 951 | pf->end += l; |
michael@0 | 952 | pf->size += l; |
michael@0 | 953 | if (pf->next && pf->end == pf->next->page ) { |
michael@0 | 954 | pt = pf->next; |
michael@0 | 955 | pf->end = pt->end; |
michael@0 | 956 | pf->size += pt->size; |
michael@0 | 957 | pf->next = pt->next; |
michael@0 | 958 | if (pf->next) |
michael@0 | 959 | pf->next->prev = pf; |
michael@0 | 960 | _PR_UnlockedFree(pt); |
michael@0 | 961 | } |
michael@0 | 962 | } else if (pf->page == tail) { |
michael@0 | 963 | /* prepend to entry */ |
michael@0 | 964 | pf->size += l; |
michael@0 | 965 | pf->page = ptr; |
michael@0 | 966 | } else if (pf->page > ptr) { |
michael@0 | 967 | px->next = pf; |
michael@0 | 968 | px->prev = pf->prev; |
michael@0 | 969 | pf->prev = px; |
michael@0 | 970 | px->prev->next = px; |
michael@0 | 971 | pf = px; |
michael@0 | 972 | px = 0; |
michael@0 | 973 | } else if (!pf->next) { |
michael@0 | 974 | px->next = 0; |
michael@0 | 975 | px->prev = pf; |
michael@0 | 976 | pf->next = px; |
michael@0 | 977 | pf = px; |
michael@0 | 978 | px = 0; |
michael@0 | 979 | } else { |
michael@0 | 980 | continue; |
michael@0 | 981 | } |
michael@0 | 982 | break; |
michael@0 | 983 | } |
michael@0 | 984 | } |
michael@0 | 985 | if (!pf->next && |
michael@0 | 986 | pf->size > malloc_cache && |
michael@0 | 987 | pf->end == malloc_brk && |
michael@0 | 988 | malloc_brk == (void*)sbrk(0)) { |
michael@0 | 989 | pf->end = pf->page + malloc_cache; |
michael@0 | 990 | pf->size = malloc_cache; |
michael@0 | 991 | TRACE(("%6d U %p %d\n",malloc_event++,pf->end,pf->end - pf->page)); |
michael@0 | 992 | brk(pf->end); |
michael@0 | 993 | malloc_brk = pf->end; |
michael@0 | 994 | /* Find the page directory entry for the page in question */ |
michael@0 | 995 | page = (u_long)pf->end >> malloc_pageshift; |
michael@0 | 996 | index = page - malloc_origo; |
michael@0 | 997 | /* Now update the directory */ |
michael@0 | 998 | for(i=index;i <= last_index;) |
michael@0 | 999 | page_dir[i++] = MALLOC_NOT_MINE; |
michael@0 | 1000 | last_index = index - 1; |
michael@0 | 1001 | } |
michael@0 | 1002 | } |
michael@0 | 1003 | |
michael@0 | 1004 | /* |
michael@0 | 1005 | * Free a chunk, and possibly the page it's on, if the page becomes empty. |
michael@0 | 1006 | */ |
michael@0 | 1007 | |
michael@0 | 1008 | static void |
michael@0 | 1009 | free_bytes(void *ptr, u_long page, int index, struct pginfo *info) |
michael@0 | 1010 | { |
michael@0 | 1011 | int i; |
michael@0 | 1012 | struct pginfo **mp; |
michael@0 | 1013 | void *vp; |
michael@0 | 1014 | |
michael@0 | 1015 | /* Make sure that pointer is multiplum of chunk-size */ |
michael@0 | 1016 | #ifdef SANITY |
michael@0 | 1017 | if ((u_long)ptr & (info->size - 1)) |
michael@0 | 1018 | wrterror("freeing messed up chunk pointer\n"); |
michael@0 | 1019 | #endif |
michael@0 | 1020 | |
michael@0 | 1021 | /* Find the chunk number on the page */ |
michael@0 | 1022 | i = ((u_long)ptr & malloc_pagemask) >> info->shift; |
michael@0 | 1023 | |
michael@0 | 1024 | /* See if it's free already */ |
michael@0 | 1025 | if (tst_bit(info,i)) { |
michael@0 | 1026 | #ifdef SANITY |
michael@0 | 1027 | wrtwarning("freeing free chunk at %p\n", ptr); |
michael@0 | 1028 | #endif |
michael@0 | 1029 | return; |
michael@0 | 1030 | } |
michael@0 | 1031 | |
michael@0 | 1032 | /* Mark it free */ |
michael@0 | 1033 | set_bit(info,i); |
michael@0 | 1034 | info->free++; |
michael@0 | 1035 | |
michael@0 | 1036 | /* If the page was full before, we need to put it on the queue now */ |
michael@0 | 1037 | if (info->free == 1) { |
michael@0 | 1038 | mp = page_dir + info->shift; |
michael@0 | 1039 | while (*mp && (*mp)->next && (*mp)->next->page < info->page) |
michael@0 | 1040 | mp = &(*mp)->next; |
michael@0 | 1041 | info->next = *mp; |
michael@0 | 1042 | *mp = info; |
michael@0 | 1043 | return; |
michael@0 | 1044 | } |
michael@0 | 1045 | |
michael@0 | 1046 | /* If this page isn't empty, don't do anything. */ |
michael@0 | 1047 | if (info->free != info->total) |
michael@0 | 1048 | return; |
michael@0 | 1049 | |
michael@0 | 1050 | /* We may want to keep at least one page of each size chunks around. */ |
michael@0 | 1051 | mp = page_dir + info->shift; |
michael@0 | 1052 | if (0 && (*mp == info) && !info->next) |
michael@0 | 1053 | return; |
michael@0 | 1054 | |
michael@0 | 1055 | /* Find & remove this page in the queue */ |
michael@0 | 1056 | while (*mp != info) { |
michael@0 | 1057 | mp = &((*mp)->next); |
michael@0 | 1058 | #ifdef EXTRA_SANITY |
michael@0 | 1059 | if (!*mp) { |
michael@0 | 1060 | TRACE(("%6d !q %p\n",malloc_event++,info)); |
michael@0 | 1061 | wrterror("Not on queue\n"); |
michael@0 | 1062 | } |
michael@0 | 1063 | #endif |
michael@0 | 1064 | } |
michael@0 | 1065 | *mp = info->next; |
michael@0 | 1066 | |
michael@0 | 1067 | /* Free the page & the info structure if need be */ |
michael@0 | 1068 | set_pgdir(info->page,MALLOC_FIRST); |
michael@0 | 1069 | if((void*)info->page == (void*)info) { |
michael@0 | 1070 | _PR_UnlockedFree(info->page); |
michael@0 | 1071 | } else { |
michael@0 | 1072 | vp = info->page; |
michael@0 | 1073 | _PR_UnlockedFree(info); |
michael@0 | 1074 | _PR_UnlockedFree(vp); |
michael@0 | 1075 | } |
michael@0 | 1076 | } |
michael@0 | 1077 | |
michael@0 | 1078 | void _PR_UnlockedFree(void *ptr) |
michael@0 | 1079 | { |
michael@0 | 1080 | u_long page; |
michael@0 | 1081 | struct pginfo *info; |
michael@0 | 1082 | int index, tmp_index; |
michael@0 | 1083 | |
michael@0 | 1084 | TRACE(("%6d F %p\n",malloc_event++,ptr)); |
michael@0 | 1085 | /* This is legal */ |
michael@0 | 1086 | if (!ptr) |
michael@0 | 1087 | return; |
michael@0 | 1088 | |
michael@0 | 1089 | #ifdef SANITY |
michael@0 | 1090 | /* There wouldn't be anything to free */ |
michael@0 | 1091 | if (!initialized) { |
michael@0 | 1092 | wrtwarning("free() called before malloc() ever got called\n"); |
michael@0 | 1093 | return; |
michael@0 | 1094 | } |
michael@0 | 1095 | #endif |
michael@0 | 1096 | |
michael@0 | 1097 | #ifdef SANITY |
michael@0 | 1098 | if (suicide) |
michael@0 | 1099 | PR_Abort(); |
michael@0 | 1100 | #endif |
michael@0 | 1101 | |
michael@0 | 1102 | /* Find the page directory entry for the page in question */ |
michael@0 | 1103 | page = (u_long)ptr >> malloc_pageshift; |
michael@0 | 1104 | index = page - malloc_origo; |
michael@0 | 1105 | |
michael@0 | 1106 | /* |
michael@0 | 1107 | * check if memory was allocated by memalign |
michael@0 | 1108 | */ |
michael@0 | 1109 | tmp_index = index; |
michael@0 | 1110 | while (page_dir[tmp_index] == MALLOC_FOLLOW) |
michael@0 | 1111 | tmp_index--; |
michael@0 | 1112 | if (tmp_index != index) { |
michael@0 | 1113 | /* |
michael@0 | 1114 | * memalign-allocated memory |
michael@0 | 1115 | */ |
michael@0 | 1116 | index = tmp_index; |
michael@0 | 1117 | page = index + malloc_origo; |
michael@0 | 1118 | ptr = (void *) (page << malloc_pageshift); |
michael@0 | 1119 | } |
michael@0 | 1120 | /* make sure it makes sense in some fashion */ |
michael@0 | 1121 | if (index < malloc_pageshift) { |
michael@0 | 1122 | #ifdef SANITY |
michael@0 | 1123 | wrtwarning("junk pointer %p (low) passed to free()\n", ptr); |
michael@0 | 1124 | #endif |
michael@0 | 1125 | return; |
michael@0 | 1126 | } |
michael@0 | 1127 | if (index > last_index) { |
michael@0 | 1128 | #ifdef SANITY |
michael@0 | 1129 | wrtwarning("junk pointer %p (high) passed to free()\n", ptr); |
michael@0 | 1130 | #endif |
michael@0 | 1131 | return; |
michael@0 | 1132 | } |
michael@0 | 1133 | |
michael@0 | 1134 | /* handle as page-allocation or chunk allocation */ |
michael@0 | 1135 | info = page_dir[index]; |
michael@0 | 1136 | if (info < MALLOC_MAGIC) |
michael@0 | 1137 | free_pages((char*)ptr, page, index, info); |
michael@0 | 1138 | else |
michael@0 | 1139 | free_bytes(ptr,page,index,info); |
michael@0 | 1140 | return; |
michael@0 | 1141 | } |
michael@0 | 1142 | #endif /* _PR_OVERRIDE_MALLOC */ |