1 /* 2 * msvcrt.dll heap functions 3 * 4 * Copyright 2000 Jon Griffiths 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 * 20 * Note: Win32 heap operations are MT safe. We only lock the new 21 * handler and non atomic heap operations 22 */ 23 24 #include <precomp.h> 25 #include <malloc.h> 26 27 /* MT */ 28 #define LOCK_HEAP _mlock( _HEAP_LOCK ) 29 #define UNLOCK_HEAP _munlock( _HEAP_LOCK ) 30 31 /* _aligned */ 32 #define SAVED_PTR(x) ((void *)((DWORD_PTR)((char *)x - sizeof(void *)) & \ 33 ~(sizeof(void *) - 1))) 34 #define ALIGN_PTR(ptr, alignment, offset) ((void *) \ 35 ((((DWORD_PTR)((char *)ptr + alignment + sizeof(void *) + offset)) & \ 36 ~(alignment - 1)) - offset)) 37 38 39 typedef int (CDECL *MSVCRT_new_handler_func)(size_t size); 40 41 static MSVCRT_new_handler_func MSVCRT_new_handler; 42 static int MSVCRT_new_mode; 43 44 /* FIXME - According to documentation it should be 8*1024, at runtime it returns 16 */ 45 static unsigned int MSVCRT_amblksiz = 16; 46 /* FIXME - According to documentation it should be 480 bytes, at runtime default is 0 */ 47 static size_t MSVCRT_sbh_threshold = 0; 48 49 /********************************************************************* 50 * ??2@YAPAXI@Z (MSVCRT.@) 51 */ 52 void* CDECL MSVCRT_operator_new(size_t size) 53 { 54 void *retval; 55 int freed; 56 57 do 58 { 59 retval = HeapAlloc(GetProcessHeap(), 0, size); 60 if(retval) 61 { 62 TRACE("(%ld) returning %p\n", size, retval); 63 return retval; 64 } 65 66 LOCK_HEAP; 67 if(MSVCRT_new_handler) 68 freed = (*MSVCRT_new_handler)(size); 69 else 70 freed = 0; 71 UNLOCK_HEAP; 72 } while(freed); 73 74 TRACE("(%ld) out of memory\n", size); 75 return NULL; 76 } 77 78 79 /********************************************************************* 80 * ??2@YAPAXIHPBDH@Z (MSVCRT.@) 81 */ 82 void* CDECL MSVCRT_operator_new_dbg(size_t size, int type, const char *file, int line) 83 { 84 return MSVCRT_operator_new( size ); 85 } 86 87 88 /********************************************************************* 89 * ??3@YAXPAX@Z (MSVCRT.@) 90 */ 91 void CDECL MSVCRT_operator_delete(void *mem) 92 { 93 TRACE("(%p)\n", mem); 94 HeapFree(GetProcessHeap(), 0, mem); 95 } 96 97 98 /********************************************************************* 99 * ?_query_new_handler@@YAP6AHI@ZXZ (MSVCRT.@) 100 */ 101 MSVCRT_new_handler_func CDECL MSVCRT__query_new_handler(void) 102 { 103 return MSVCRT_new_handler; 104 } 105 106 107 /********************************************************************* 108 * ?_query_new_mode@@YAHXZ (MSVCRT.@) 109 */ 110 int CDECL MSVCRT__query_new_mode(void) 111 { 112 return MSVCRT_new_mode; 113 } 114 115 /********************************************************************* 116 * ?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z (MSVCRT.@) 117 */ 118 MSVCRT_new_handler_func CDECL MSVCRT__set_new_handler(MSVCRT_new_handler_func func) 119 { 120 MSVCRT_new_handler_func old_handler; 121 LOCK_HEAP; 122 old_handler = MSVCRT_new_handler; 123 MSVCRT_new_handler = func; 124 UNLOCK_HEAP; 125 return old_handler; 126 } 127 128 /********************************************************************* 129 * ?set_new_handler@@YAP6AXXZP6AXXZ@Z (MSVCRT.@) 130 */ 131 MSVCRT_new_handler_func CDECL MSVCRT_set_new_handler(void *func) 132 { 133 TRACE("(%p)\n",func); 134 MSVCRT__set_new_handler(NULL); 135 return NULL; 136 } 137 138 /********************************************************************* 139 * ?_set_new_mode@@YAHH@Z (MSVCRT.@) 140 */ 141 int CDECL MSVCRT__set_new_mode(int mode) 142 { 143 int old_mode; 144 LOCK_HEAP; 145 old_mode = MSVCRT_new_mode; 146 MSVCRT_new_mode = mode; 147 UNLOCK_HEAP; 148 return old_mode; 149 } 150 151 /********************************************************************* 152 * _callnewh (MSVCRT.@) 153 */ 154 int CDECL _callnewh(size_t size) 155 { 156 if(MSVCRT_new_handler) 157 (*MSVCRT_new_handler)(size); 158 return 0; 159 } 160 161 /********************************************************************* 162 * _expand (MSVCRT.@) 163 */ 164 void* CDECL _expand(void* mem, size_t size) 165 { 166 return HeapReAlloc(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, mem, size); 167 } 168 169 /********************************************************************* 170 * _heapchk (MSVCRT.@) 171 */ 172 int CDECL _heapchk(void) 173 { 174 if (!HeapValidate( GetProcessHeap(), 0, NULL)) 175 { 176 _dosmaperr(GetLastError()); 177 return _HEAPBADNODE; 178 } 179 return _HEAPOK; 180 } 181 182 /********************************************************************* 183 * _heapmin (MSVCRT.@) 184 */ 185 int CDECL _heapmin(void) 186 { 187 if (!HeapCompact( GetProcessHeap(), 0 )) 188 { 189 if (GetLastError() != ERROR_CALL_NOT_IMPLEMENTED) 190 _dosmaperr(GetLastError()); 191 return -1; 192 } 193 return 0; 194 } 195 196 /********************************************************************* 197 * _heapwalk (MSVCRT.@) 198 */ 199 int CDECL _heapwalk(_HEAPINFO* next) 200 { 201 PROCESS_HEAP_ENTRY phe; 202 203 LOCK_HEAP; 204 phe.lpData = next->_pentry; 205 phe.cbData = (DWORD)next->_size; 206 phe.wFlags = next->_useflag == _USEDENTRY ? PROCESS_HEAP_ENTRY_BUSY : 0; 207 208 if (phe.lpData && phe.wFlags & PROCESS_HEAP_ENTRY_BUSY && 209 !HeapValidate( GetProcessHeap(), 0, phe.lpData )) 210 { 211 UNLOCK_HEAP; 212 _dosmaperr(GetLastError()); 213 return _HEAPBADNODE; 214 } 215 216 do 217 { 218 if (!HeapWalk( GetProcessHeap(), &phe )) 219 { 220 UNLOCK_HEAP; 221 if (GetLastError() == ERROR_NO_MORE_ITEMS) 222 return _HEAPEND; 223 _dosmaperr(GetLastError()); 224 if (!phe.lpData) 225 return _HEAPBADBEGIN; 226 return _HEAPBADNODE; 227 } 228 } while (phe.wFlags & (PROCESS_HEAP_REGION|PROCESS_HEAP_UNCOMMITTED_RANGE)); 229 230 UNLOCK_HEAP; 231 next->_pentry = phe.lpData; 232 next->_size = phe.cbData; 233 next->_useflag = phe.wFlags & PROCESS_HEAP_ENTRY_BUSY ? _USEDENTRY : _FREEENTRY; 234 return _HEAPOK; 235 } 236 237 /********************************************************************* 238 * _heapset (MSVCRT.@) 239 */ 240 int CDECL _heapset(unsigned int value) 241 { 242 int retval; 243 _HEAPINFO heap; 244 245 memset( &heap, 0, sizeof(heap) ); 246 LOCK_HEAP; 247 while ((retval = _heapwalk(&heap)) == _HEAPOK) 248 { 249 if (heap._useflag == _FREEENTRY) 250 memset(heap._pentry, value, heap._size); 251 } 252 UNLOCK_HEAP; 253 return retval == _HEAPEND? _HEAPOK : retval; 254 } 255 256 /********************************************************************* 257 * _heapadd (MSVCRT.@) 258 */ 259 int CDECL _heapadd(void* mem, size_t size) 260 { 261 TRACE("(%p,%ld) unsupported in Win32\n", mem,size); 262 *_errno() = ENOSYS; 263 return -1; 264 } 265 266 /********************************************************************* 267 * _heapadd (MSVCRT.@) 268 */ 269 intptr_t CDECL _get_heap_handle(void) 270 { 271 return (intptr_t)GetProcessHeap(); 272 } 273 274 /********************************************************************* 275 * _msize (MSVCRT.@) 276 */ 277 size_t CDECL _msize(void* mem) 278 { 279 size_t size = HeapSize(GetProcessHeap(),0,mem); 280 if (size == ~(size_t)0) 281 { 282 WARN(":Probably called with non wine-allocated memory, ret = -1\n"); 283 /* At least the Win32 crtdll/msvcrt also return -1 in this case */ 284 } 285 return size; 286 } 287 288 /********************************************************************* 289 * calloc (MSVCRT.@) 290 */ 291 void* CDECL calloc(size_t count, size_t size) 292 { 293 return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, count * size ); 294 } 295 296 /********************************************************************* 297 * free (MSVCRT.@) 298 */ 299 void CDECL free(void* ptr) 300 { 301 if(ptr == NULL) return; 302 HeapFree(GetProcessHeap(),0,ptr); 303 } 304 305 /********************************************************************* 306 * malloc (MSVCRT.@) 307 */ 308 void* CDECL malloc(size_t size) 309 { 310 void *ret = HeapAlloc(GetProcessHeap(),0,size); 311 if (!ret) 312 *_errno() = ENOMEM; 313 return ret; 314 } 315 316 /********************************************************************* 317 * realloc (MSVCRT.@) 318 */ 319 void* CDECL realloc(void* ptr, size_t size) 320 { 321 if (!ptr) return malloc(size); 322 if (size) return HeapReAlloc(GetProcessHeap(), 0, ptr, size); 323 free(ptr); 324 return NULL; 325 } 326 327 /********************************************************************* 328 * __p__amblksiz (MSVCRT.@) 329 */ 330 unsigned int* CDECL __p__amblksiz(void) 331 { 332 return &MSVCRT_amblksiz; 333 } 334 335 /********************************************************************* 336 * _get_sbh_threshold (MSVCRT.@) 337 */ 338 size_t CDECL _get_sbh_threshold(void) 339 { 340 return MSVCRT_sbh_threshold; 341 } 342 343 /********************************************************************* 344 * _set_sbh_threshold (MSVCRT.@) 345 */ 346 int CDECL _set_sbh_threshold(size_t threshold) 347 { 348 if(threshold > 1016) 349 return 0; 350 else 351 MSVCRT_sbh_threshold = threshold; 352 return 1; 353 } 354 355 /********************************************************************* 356 * _aligned_free (MSVCRT.@) 357 */ 358 void CDECL _aligned_free(void *memblock) 359 { 360 TRACE("(%p)\n", memblock); 361 362 if (memblock) 363 { 364 void **saved = SAVED_PTR(memblock); 365 free(*saved); 366 } 367 } 368 369 /********************************************************************* 370 * _aligned_offset_malloc (MSVCRT.@) 371 */ 372 void * CDECL _aligned_offset_malloc(size_t size, size_t alignment, size_t offset) 373 { 374 void *memblock, *temp, **saved; 375 TRACE("(%lu, %lu, %lu)\n", size, alignment, offset); 376 377 /* alignment must be a power of 2 */ 378 if ((alignment & (alignment - 1)) != 0) 379 { 380 *_errno() = EINVAL; 381 return NULL; 382 } 383 384 /* offset must be less than size */ 385 if (offset >= size) 386 { 387 *_errno() = EINVAL; 388 return NULL; 389 } 390 391 /* don't align to less than void pointer size */ 392 if (alignment < sizeof(void *)) 393 alignment = sizeof(void *); 394 395 /* allocate enough space for void pointer and alignment */ 396 temp = malloc(size + alignment + sizeof(void *)); 397 398 if (!temp) 399 return NULL; 400 401 /* adjust pointer for proper alignment and offset */ 402 memblock = ALIGN_PTR(temp, alignment, offset); 403 404 /* Save the real allocation address below returned address */ 405 /* so it can be found later to free. */ 406 saved = SAVED_PTR(memblock); 407 *saved = temp; 408 409 return memblock; 410 } 411 412 /********************************************************************* 413 * _aligned_malloc (MSVCRT.@) 414 */ 415 void * CDECL _aligned_malloc(size_t size, size_t alignment) 416 { 417 TRACE("(%lu, %lu)\n", size, alignment); 418 return _aligned_offset_malloc(size, alignment, 0); 419 } 420 421 /********************************************************************* 422 * _aligned_offset_realloc (MSVCRT.@) 423 */ 424 void * CDECL _aligned_offset_realloc(void *memblock, size_t size, 425 size_t alignment, size_t offset) 426 { 427 void * temp, **saved; 428 size_t old_padding, new_padding, old_size; 429 TRACE("(%p, %lu, %lu, %lu)\n", memblock, size, alignment, offset); 430 431 if (!memblock) 432 return _aligned_offset_malloc(size, alignment, offset); 433 434 /* alignment must be a power of 2 */ 435 if ((alignment & (alignment - 1)) != 0) 436 { 437 *_errno() = EINVAL; 438 return NULL; 439 } 440 441 /* offset must be less than size */ 442 if (offset >= size) 443 { 444 *_errno() = EINVAL; 445 return NULL; 446 } 447 448 if (size == 0) 449 { 450 _aligned_free(memblock); 451 return NULL; 452 } 453 454 /* don't align to less than void pointer size */ 455 if (alignment < sizeof(void *)) 456 alignment = sizeof(void *); 457 458 /* make sure alignment and offset didn't change */ 459 saved = SAVED_PTR(memblock); 460 if (memblock != ALIGN_PTR(*saved, alignment, offset)) 461 { 462 *_errno() = EINVAL; 463 return NULL; 464 } 465 466 old_padding = (char *)memblock - (char *)*saved; 467 468 /* Get previous size of block */ 469 old_size = _msize(*saved); 470 if (old_size == -1) 471 { 472 /* It seems this function was called with an invalid pointer. Bail out. */ 473 return NULL; 474 } 475 476 /* Adjust old_size to get amount of actual data in old block. */ 477 if (old_size < old_padding) 478 { 479 /* Shouldn't happen. Something's weird, so bail out. */ 480 return NULL; 481 } 482 old_size -= old_padding; 483 484 temp = realloc(*saved, size + alignment + sizeof(void *)); 485 486 if (!temp) 487 return NULL; 488 489 /* adjust pointer for proper alignment and offset */ 490 memblock = ALIGN_PTR(temp, alignment, offset); 491 492 /* Save the real allocation address below returned address */ 493 /* so it can be found later to free. */ 494 saved = SAVED_PTR(memblock); 495 496 new_padding = (char *)memblock - (char *)temp; 497 498 /* 499 Memory layout of old block is as follows: 500 +-------+---------------------+-+--------------------------+-----------+ 501 | ... | "old_padding" bytes | | ... "old_size" bytes ... | ... | 502 +-------+---------------------+-+--------------------------+-----------+ 503 ^ ^ ^ 504 | | | 505 *saved saved memblock 506 507 Memory layout of new block is as follows: 508 +-------+-----------------------------+-+----------------------+-------+ 509 | ... | "new_padding" bytes | | ... "size" bytes ... | ... | 510 +-------+-----------------------------+-+----------------------+-------+ 511 ^ ^ ^ 512 | | | 513 temp saved memblock 514 515 However, in the new block, actual data is still written as follows 516 (because it was copied by MSVCRT_realloc): 517 +-------+---------------------+--------------------------------+-------+ 518 | ... | "old_padding" bytes | ... "old_size" bytes ... | ... | 519 +-------+---------------------+--------------------------------+-------+ 520 ^ ^ ^ 521 | | | 522 temp saved memblock 523 524 Therefore, min(old_size,size) bytes of actual data have to be moved 525 from the offset they were at in the old block (temp + old_padding), 526 to the offset they have to be in the new block (temp + new_padding == memblock). 527 */ 528 if (new_padding != old_padding) 529 memmove((char *)memblock, (char *)temp + old_padding, (old_size < size) ? old_size : size); 530 531 *saved = temp; 532 533 return memblock; 534 } 535 536 /********************************************************************* 537 * _aligned_realloc (MSVCRT.@) 538 */ 539 void * CDECL _aligned_realloc(void *memblock, size_t size, size_t alignment) 540 { 541 TRACE("(%p, %lu, %lu)\n", memblock, size, alignment); 542 return _aligned_offset_realloc(memblock, size, alignment, 0); 543 } 544 545 /********************************************************************* 546 * memmove_s (MSVCRT.@) 547 */ 548 int CDECL memmove_s(void *dest, size_t numberOfElements, const void *src, size_t count) 549 { 550 TRACE("(%p %lu %p %lu)\n", dest, numberOfElements, src, count); 551 552 if(!count) 553 return 0; 554 555 if(!dest || !src) { 556 if(dest) 557 memset(dest, 0, numberOfElements); 558 559 *_errno() = EINVAL; 560 return EINVAL; 561 } 562 563 if(count > numberOfElements) { 564 memset(dest, 0, numberOfElements); 565 566 *_errno() = ERANGE; 567 return ERANGE; 568 } 569 570 memmove(dest, src, count); 571 return 0; 572 } 573 574 /********************************************************************* 575 * strncpy_s (MSVCRT.@) 576 */ 577 int CDECL strncpy_s(char *dest, size_t numberOfElements, 578 const char *src, size_t count) 579 { 580 size_t i, end; 581 582 TRACE("(%s %lu %s %lu)\n", dest, numberOfElements, src, count); 583 584 if(!count) 585 return 0; 586 587 if (!MSVCRT_CHECK_PMT(dest != NULL) || !MSVCRT_CHECK_PMT(src != NULL) || 588 !MSVCRT_CHECK_PMT(numberOfElements != 0)) { 589 *_errno() = EINVAL; 590 return EINVAL; 591 } 592 593 if(count!=_TRUNCATE && count<numberOfElements) 594 end = count; 595 else 596 end = numberOfElements-1; 597 598 for(i=0; i<end && src[i]; i++) 599 dest[i] = src[i]; 600 601 if(!src[i] || end==count || count==_TRUNCATE) { 602 dest[i] = '\0'; 603 return 0; 604 } 605 606 MSVCRT_INVALID_PMT("dest[numberOfElements] is too small", EINVAL); 607 dest[0] = '\0'; 608 return EINVAL; 609 } 610