Reactos
at master 638 lines 20 kB view raw
1/*** 2*align.c - Aligned allocation, reallocation or freeing of memory in the heap 3* 4* Copyright (c) Microsoft Corporation. All rights reserved. 5* 6*Purpose: 7* Defines the _aligned_malloc(), 8* _aligned_realloc(), 9* _aligned_recalloc(), 10* _aligned_offset_malloc(), 11* _aligned_offset_realloc(), 12* _aligned_offset_recalloc(), 13* _aligned_free(), 14* _aligned_msize() functions. 15* 16*******************************************************************************/ 17 18#include <corecrt_internal.h> 19#include <malloc.h> 20 21 22 23#define IS_2_POW_N(X) ((X) != 0 && ((X) & ((X) - 1)) == 0) 24#define PTR_SZ sizeof(void *) 25 26/*** 27* 28* |1|___6___|2|3|4|_________5__________|_6_| 29* 30* 1 -> Pointer to start of the block allocated by malloc. 31* 2 -> Value of 1. 32* 3 -> Gap used to get 1 aligned on sizeof(void *). 33* 4 -> Pointer to the start of data block. 34* 4+5 -> Data block. 35* 6 -> Wasted memory at rear of data block. 36* 6 -> Wasted memory. 37* 38*******************************************************************************/ 39 40static void _aligned_free_base( 41 _Pre_maybenull_ _Post_invalid_ void* block 42 ); 43 44_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(size) 45static void* _aligned_malloc_base( 46 _In_ size_t size, 47 _In_ size_t alignment 48 ); 49 50_Check_return_ 51static size_t _aligned_msize_base( 52 _Pre_notnull_ void* block, 53 _In_ size_t alignment, 54 _In_ size_t offset 55 ); 56 57_Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(size) 58static void* _aligned_offset_malloc_base( 59 _In_ size_t size, 60 _In_ size_t alignment, 61 _In_ size_t offset 62 ); 63 64_Success_(return != 0) _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(size) 65static void* _aligned_offset_realloc_base( 66 _Pre_maybenull_ _Post_invalid_ void* block, 67 _In_ size_t size, 68 _In_ size_t alignment, 69 _In_ size_t offset 70 ); 71 72_Success_(return != 0) _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(size * count) 73static void* _aligned_offset_recalloc_base( 74 _Pre_maybenull_ _Post_invalid_ void* block, 75 _In_ size_t count, 76 _In_ size_t size, 77 _In_ size_t alignment, 78 _In_ size_t offset 79 ); 80 81_Success_(return != 0) _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(size) 82static void* _aligned_realloc_base( 83 _Pre_maybenull_ _Post_invalid_ void* block, 84 _In_ size_t size, 85 _In_ size_t alignment 86 ); 87 88_Success_(return != 0) _Check_return_ _Ret_maybenull_ _Post_writable_byte_size_(size * count) 89static void* _aligned_recalloc_base( 90 _Pre_maybenull_ _Post_invalid_ void* block, 91 _In_ size_t count, 92 _In_ size_t size, 93 _In_ size_t alignment 94 ); 95 96/*** 97* void *_aligned_malloc_base(size_t size, size_t alignment) 98* - Get a block of aligned memory from the heap. 99* 100* Purpose: 101* Allocate of block of aligned memory aligned on the alignment of at least 102* size bytes from the heap and return a pointer to it. 103* 104* Entry: 105* size_t size - size of block requested 106* size_t alignment - alignment of memory (needs to be a power of 2) 107* 108* Exit: 109* Success: Pointer to memory block 110* Failure: Null, errno is set 111* 112* Exceptions: 113* Input parameters are validated. Refer to the validation section of the function. 114* 115*******************************************************************************/ 116 117static __forceinline void* __cdecl _aligned_malloc_base( 118 size_t const size, 119 size_t const alignment 120 ) 121{ 122 return _aligned_offset_malloc_base(size, alignment, 0); 123} 124/*** 125* void *_aligned_offset_malloc_base(size_t size, size_t alignment, int offset) 126* - Get a block of memory from the heap. 127* 128* Purpose: 129* Allocate a block of memory which is shifted by offset from alignment of 130* at least size bytes from the heap and return a pointer to it. 131* 132* Entry: 133* size_t size - size of block of memory 134* size_t alignment - alignment of memory (needs to be a power of 2) 135* size_t offset - offset of memory from the alignment 136* 137* Exit: 138* Success: Pointer to memory block 139* Failure: Null, errno is set 140* 141* Exceptions: 142* Input parameters are validated. Refer to the validation section of the function. 143* 144*******************************************************************************/ 145 146 147static __forceinline void* __cdecl _aligned_offset_malloc_base( 148 size_t size, 149 size_t align, 150 size_t offset 151 ) 152{ 153 uintptr_t ptr, retptr, gap; 154 size_t nonuser_size,block_size; 155 156 /* validation section */ 157 _VALIDATE_RETURN(IS_2_POW_N(align), EINVAL, nullptr); 158 _VALIDATE_RETURN(offset == 0 || offset < size, EINVAL, nullptr); 159 160 align = (align > PTR_SZ ? align : PTR_SZ) -1; 161 162 /* gap = number of bytes needed to round up offset to align with PTR_SZ*/ 163 gap = (0 - offset)&(PTR_SZ -1); 164 165 nonuser_size = PTR_SZ +gap +align; 166 block_size = nonuser_size + size; 167 _VALIDATE_RETURN_NOEXC(size <= block_size, ENOMEM, nullptr) 168 169 if ( (ptr =(uintptr_t)malloc(block_size)) == (uintptr_t)nullptr) 170 return nullptr; 171 172 retptr =((ptr +nonuser_size+offset)&~align)- offset; 173 ((uintptr_t *)(retptr - gap))[-1] = ptr; 174 175 return (void *)retptr; 176} 177 178/*** 179* 180* void *_aligned_realloc_base(void * memblock, size_t size, size_t alignment) 181* - Reallocate a block of aligned memory from the heap. 182* 183* Purpose: 184* Reallocates of block of aligned memory aligned on the alignment of at 185* least size bytes from the heap and return a pointer to it. Size can be 186* either greater or less than the original size of the block. 187* The reallocation may result in moving the block as well as changing the 188* size. 189* 190* Entry: 191* void *memblock - pointer to block in the heap previously allocated by 192* call to _aligned_malloc(), _aligned_offset_malloc(), 193* _aligned_realloc() or _aligned_offset_realloc(). 194* size_t size - size of block requested 195* size_t alignment - alignment of memory 196* 197* Exit: 198* Success: Pointer to re-allocated memory block 199* Failure: Null, errno is set 200* 201* Exceptions: 202* Input parameters are validated. Refer to the validation section of the function. 203* 204*******************************************************************************/ 205 206static __forceinline void* __cdecl _aligned_realloc_base( 207 void* const block, 208 size_t const size, 209 size_t const alignment 210 ) 211{ 212 return _aligned_offset_realloc_base(block, size, alignment, 0); 213} 214 215/*** 216* 217* void *_aligned_recalloc_base(void * memblock, size_t count, size_t size, size_t alignment) 218* - Reallocate a block of aligned memory from the heap. 219* 220* Purpose: 221* Reallocates of block of aligned memory aligned on the alignment of at 222* least size bytes from the heap and return a pointer to it. Size can be 223* either greater or less than the original size of the block. 224* The reallocation may result in moving the block as well as changing the 225* size. 226* 227* Entry: 228* void *memblock - pointer to block in the heap previously allocated by 229* call to _aligned_malloc(), _aligned_offset_malloc(), 230* _aligned_realloc() or _aligned_offset_realloc(). 231* size_t count - count of items 232* size_t size - size of item 233* size_t alignment - alignment of memory 234* 235* Exit: 236* Success: Pointer to re-allocated memory block 237* Failure: Null, errno is set 238* 239* Exceptions: 240* Input parameters are validated. Refer to the validation section of the function. 241* 242*******************************************************************************/ 243 244static __forceinline void* __cdecl _aligned_recalloc_base( 245 void* const block, 246 size_t const count, 247 size_t const size, 248 size_t const alignment 249 ) 250{ 251 return _aligned_offset_recalloc_base(block, count, size, alignment, 0); 252} 253 254/*** 255* 256* void *_aligned_offset_realloc_base (void * memblock, size_t size, 257* size_t alignment, int offset) 258* - Reallocate a block of memory from the heap. 259* 260* Purpose: 261* Reallocates a block of memory which is shifted by offset from 262* alignment of at least size bytes from the heap and return a pointer 263* to it. Size can be either greater or less than the original size of the 264* block. 265* 266* Entry: 267* void *memblock - pointer to block in the heap previously allocated by 268* call to _aligned_malloc(), _aligned_offset_malloc(), 269* _aligned_realloc() or _aligned_offset_realloc(). 270* size_t size - size of block of memory 271* size_t alignment - alignment of memory 272* size_t offset - offset of memory from the alignment 273* 274* Exit: 275* Success: Pointer to re-allocated memory block 276* Failure: Null, errno is set 277* 278* Exceptions: 279* Input parameters are validated. Refer to the validation section of the function. 280* 281*******************************************************************************/ 282 283static __forceinline void* __cdecl _aligned_offset_realloc_base( 284 void* block, 285 size_t size, 286 size_t align, 287 size_t offset 288 ) 289{ 290 uintptr_t ptr, retptr, gap, stptr, diff; 291 uintptr_t movsz, reqsz; 292 int bFree = 0; 293 294 /* special cases */ 295 if (block == nullptr) 296 { 297 return _aligned_offset_malloc_base(size, align, offset); 298 } 299 if (size == 0) 300 { 301 _aligned_free_base(block); 302 return nullptr; 303 } 304 305 /* validation section */ 306 _VALIDATE_RETURN(IS_2_POW_N(align), EINVAL, nullptr); 307 _VALIDATE_RETURN(offset == 0 || offset < size, EINVAL, nullptr); 308 309 stptr = (uintptr_t)block; 310 311 /* ptr points to the pointer to starting of the memory block */ 312 stptr = (stptr & ~(PTR_SZ -1)) - PTR_SZ; 313 314 /* ptr is the pointer to the start of memory block*/ 315 stptr = *((uintptr_t *)stptr); 316 317 align = (align > PTR_SZ ? align : PTR_SZ) -1; 318 /* gap = number of bytes needed to round up offset to align with PTR_SZ*/ 319 gap = (0 -offset)&(PTR_SZ -1); 320 321 diff = (uintptr_t)block - stptr; 322 /* Mov size is min of the size of data available and sizw requested. 323 */ 324 #pragma warning(push) 325 #pragma warning(disable: 22018) // Silence prefast about overflow/underflow 326 movsz = _msize_base((void *)stptr) - ((uintptr_t)block - stptr); 327 #pragma warning(pop) 328 329 movsz = movsz > size ? size : movsz; 330 reqsz = PTR_SZ + gap + align + size; 331 332 _VALIDATE_RETURN_NOEXC(size <= reqsz, ENOMEM, nullptr); 333 334 /* First check if we can expand(reducing or expanding using expand) data 335 * safely, ie no data is lost. eg, reducing alignment and keeping size 336 * same might result in loss of data at the tail of data block while 337 * expanding. 338 * 339 * If no, use malloc to allocate the new data and move data. 340 * 341 * If yes, expand and then check if we need to move the data. 342 */ 343 if ((stptr +align +PTR_SZ +gap)<(uintptr_t)block) 344 { 345 if ((ptr = (uintptr_t)malloc(reqsz)) == (uintptr_t) nullptr) 346 return nullptr; 347 bFree = 1; 348 } 349 else 350 { 351 /* we need to save errno, which can be modified by _expand */ 352 errno_t save_errno = errno; 353 if ((ptr = (uintptr_t)_expand((void *)stptr, reqsz)) == (uintptr_t)nullptr) 354 { 355 errno = save_errno; 356 if ((ptr = (uintptr_t)malloc(reqsz)) == (uintptr_t) nullptr) 357 return nullptr; 358 bFree = 1; 359 } 360 else 361 stptr = ptr; 362 } 363 364 365 if ( ptr == ((uintptr_t)block - diff) 366 && !( ((size_t)block + gap +offset) & ~(align) )) 367 { 368 return block; 369 } 370 371 retptr =((ptr +PTR_SZ +gap +align +offset)&~align)- offset; 372 memmove((void *)retptr, (void *)(stptr + diff), movsz); 373 if ( bFree) 374 free ((void *)stptr); 375 376 ((uintptr_t *)(retptr - gap))[-1] = ptr; 377 return (void *)retptr; 378} 379 380 381/*** 382* 383* size_t _aligned_msize_base(void *memblock, size_t align, size_t offset) 384* 385* Purpose: 386* Computes the size of an aligned block. 387* 388* Entry: 389* void * memblock - pointer to the aligned block of memory 390* 391* Exceptions: 392* None. If memblock == nullptr 0 is returned. 393* 394*******************************************************************************/ 395 396static __forceinline size_t __cdecl _aligned_msize_base( 397 void* block, 398 size_t align, 399 size_t offset 400 ) 401{ 402 size_t header_size = 0; /* Size of the header block */ 403 size_t footer_size = 0; /* Size of the footer block */ 404 size_t total_size = 0; /* total size of the allocated block */ 405 size_t user_size = 0; /* size of the user block*/ 406 uintptr_t gap = 0; /* keep the alignment of the data block */ 407 /* after the sizeof(void*) aligned pointer */ 408 /* to the beginning of the allocated block */ 409 uintptr_t ptr = 0; /* computes the beginning of the allocated block */ 410 411 _VALIDATE_RETURN(block != nullptr, EINVAL, static_cast<size_t>(-1)); 412 413 /* HEADER SIZE + FOOTER SIZE = GAP + ALIGN + SIZE OF A POINTER*/ 414 /* HEADER SIZE + USER SIZE + FOOTER SIZE = TOTAL SIZE */ 415 416 ptr = (uintptr_t)block; /* ptr points to the start of the aligned memory block */ 417 ptr = (ptr & ~(PTR_SZ - 1)) - PTR_SZ; /* ptr is one position behind memblock */ 418 /* the value in ptr is the start of the real allocated block */ 419 ptr = *((uintptr_t *)ptr); /* after dereference ptr points to the beginning of the allocated block */ 420 421 total_size = _msize_base((void*)ptr); 422 header_size = (uintptr_t) block - ptr; 423 gap = (0 - offset) & (PTR_SZ - 1); 424 /* Alignment cannot be less than sizeof(void*) */ 425 align = (align > PTR_SZ ? align : PTR_SZ) -1; 426 footer_size = gap + align + PTR_SZ - header_size; 427 user_size = total_size - header_size - footer_size; 428 429 return user_size; 430} 431 432/*** 433* 434* void *_aligned_offset_recalloc_base (void * memblock, size_t size, size_t count, size_t alignment, int offset) 435* - Reallocate a block of memory from the heap. 436* 437* Purpose: 438* Reallocates a block of memory which is shifted by offset from 439* alignment of at least size bytes from the heap and return a pointer 440* to it. Size can be either greater or less than the original size of the 441* block. 442* 443* Entry: 444* void *memblock - pointer to block in the heap previously allocated by 445* call to _aligned_malloc(), _aligned_offset_malloc(), 446* _aligned_realloc() or _aligned_offset_realloc(). 447* size_t count - count of items 448* size_t size - size of items 449* size_t alignment - alignment of memory 450* size_t offset - offset of memory from the alignment 451* 452* Exit: 453* Success: Pointer to re-allocated memory block 454* Failure: Null, errno is set 455* 456* Exceptions: 457* Input parameters are validated. Refer to the validation section of the function. 458* 459*******************************************************************************/ 460 461static __forceinline void* __cdecl _aligned_offset_recalloc_base( 462 void* block, 463 size_t count, 464 size_t size, 465 size_t align, 466 size_t offset 467 ) 468{ 469 size_t user_size = 0; /* wanted size, passed to aligned realoc */ 470 size_t start_fill = 0; /* location where aligned recalloc starts to fill with 0 */ 471 /* filling must start from the end of the previous user block */ 472 void * retptr = nullptr; /* result of aligned recalloc*/ 473 474 /* ensure that (size * num) does not overflow */ 475 if (count > 0) 476 { 477 _VALIDATE_RETURN_NOEXC((_HEAP_MAXREQ / count) >= size, ENOMEM, nullptr); 478 } 479 480 user_size = size * count; 481 482 if (block != nullptr) 483 { 484 start_fill = _aligned_msize_base(block, align, offset); 485 } 486 487 retptr = _aligned_offset_realloc_base(block, user_size, align, offset); 488 489 if (retptr != nullptr) 490 { 491 if (start_fill < user_size) 492 { 493 memset ((char*)retptr + start_fill, 0, user_size - start_fill); 494 } 495 } 496 return retptr; 497} 498 499/*** 500* 501* void *_aligned_free_base(void *memblock) 502* - Free the memory which was allocated using _aligned_malloc or 503* _aligned_offset_memory 504* 505* Purpose: 506* Frees the aligned memory block which was allocated using _aligned_malloc. 507* 508* Entry: 509* void * memblock - pointer to the block of memory 510* 511* Exceptions: 512* None. If memblock == nullptr we simply return without doing anything. 513* 514*******************************************************************************/ 515 516static __forceinline void __cdecl _aligned_free_base(void* const block) 517{ 518 uintptr_t ptr; 519 520 if (block == nullptr) 521 return; 522 523 ptr = (uintptr_t)block; 524 525 /* ptr points to the pointer to starting of the memory block */ 526 ptr = (ptr & ~(PTR_SZ -1)) - PTR_SZ; 527 528 /* ptr is the pointer to the start of memory block*/ 529 ptr = *((uintptr_t*)ptr); 530 free((void *)ptr); 531} 532 533// These functions are patchable and therefore must be marked noinline. 534// Their *_base implementations are marked __forceinline in order to 535// ensure each export is separated from each other and that there is 536// enough space in each export to host a patch. 537 538extern "C" __declspec(noinline) void __cdecl _aligned_free(void* const block) 539{ 540 #ifdef _DEBUG 541 _aligned_free_dbg(block); 542 #else 543 _aligned_free_base(block); 544 #endif 545} 546 547extern "C" __declspec(noinline) _CRTRESTRICT void* __cdecl _aligned_malloc( 548 size_t const size, 549 size_t const alignment 550 ) 551{ 552 #ifdef _DEBUG 553 return _aligned_offset_malloc_dbg(size, alignment, 0, nullptr, 0); 554 #else 555 return _aligned_malloc_base(size, alignment); 556 #endif 557} 558 559extern "C" __declspec(noinline) size_t __cdecl _aligned_msize( 560 void* const block, 561 size_t const alignment, 562 size_t const offset) 563{ 564 #ifdef _DEBUG 565 return _aligned_msize_dbg(block, alignment, offset); 566 #else 567 return _aligned_msize_base(block, alignment, offset); 568 #endif 569} 570 571extern "C" __declspec(noinline) _CRTRESTRICT void* __cdecl _aligned_offset_malloc( 572 size_t const size, 573 size_t const alignment, 574 size_t const offset 575 ) 576{ 577 #ifdef _DEBUG 578 return _aligned_offset_malloc_dbg(size, alignment, offset, nullptr, 0); 579 #else 580 return _aligned_offset_malloc_base(size, alignment, offset); 581 #endif 582} 583 584extern "C" __declspec(noinline) _CRTRESTRICT void* __cdecl _aligned_offset_realloc( 585 void* const block, 586 size_t const size, 587 size_t const alignment, 588 size_t const offset 589 ) 590{ 591 #ifdef _DEBUG 592 return _aligned_offset_realloc_dbg(block, size, alignment, offset, nullptr, 0); 593 #else 594 return _aligned_offset_realloc_base(block, size, alignment, offset); 595 #endif 596} 597 598extern "C" __declspec(noinline) _CRTRESTRICT void* __cdecl _aligned_offset_recalloc( 599 void* const block, 600 size_t const count, 601 size_t const size, 602 size_t const alignment, 603 size_t const offset 604 ) 605{ 606 #ifdef _DEBUG 607 return _aligned_offset_recalloc_dbg(block, count, size, alignment, offset, nullptr, 0); 608 #else 609 return _aligned_offset_recalloc_base(block, count, size, alignment, offset); 610 #endif 611} 612 613extern "C" __declspec(noinline) _CRTRESTRICT void* __cdecl _aligned_realloc( 614 void* const block, 615 size_t const size, 616 size_t const alignment 617 ) 618{ 619 #ifdef _DEBUG 620 return _aligned_offset_realloc_dbg(block, size, alignment, 0, nullptr, 0); 621 #else 622 return _aligned_realloc_base(block, size, alignment); 623 #endif 624} 625 626extern "C" __declspec(noinline) _CRTRESTRICT void* __cdecl _aligned_recalloc( 627 void* const block, 628 size_t const count, 629 size_t const size, 630 size_t const alignment 631 ) 632{ 633 #ifdef _DEBUG 634 return _aligned_offset_recalloc_dbg(block, count, size, alignment, 0, nullptr, 0); 635 #else 636 return _aligned_recalloc_base(block, count, size, alignment); 637 #endif 638}