StdLib: Modify the memory allocation routines to not be dependent upon the internal structure of the EDK II memory pool.

StdLib/LibC/StdLib/Malloc.c
Create a private data structure, CPOOL_HEAD, which contains housekeeping information for StdLib’s memory allocation functions.  An instance of this structure is prepended to every chunk of allocated memory.  The structure links the allocation into a doubly-linked list and keeps track of the size of each allocation unit.  This information is then available for use by the realloc function.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Daryl McDaniel <daryl.mcdaniel@intel.com>
Reviewed-by: Jaben Carsey <jaben.carsey@intel.com>
Reviewed-by: Erik Bjorge <erik.c.bjorge@intel.com>
Reviewed-by:  Rosenbaum, Lee G <lee.g.rosenbaum@intel.com>



git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15319 6f19259b-4bc3-4df7-8a09-765794883524
This commit is contained in:
Daryl McDaniel 2014-03-07 01:05:30 +00:00 committed by darylm503
parent de2eccc46a
commit 7292c69b2a
1 changed files with 86 additions and 33 deletions

View File

@ -14,20 +14,19 @@
either a null pointer or a unique pointer. The value of a pointer that either a null pointer or a unique pointer. The value of a pointer that
refers to freed space is indeterminate. refers to freed space is indeterminate.
Copyright (c) 2010, Intel Corporation. All rights reserved.<BR> Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/ */
#include <Base.h>
#include <Uefi.h> #include <Uefi.h>
#include <Library/MemoryAllocationLib.h> #include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h> #include <Library/UefiBootServicesTableLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h> #include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h> #include <Library/DebugLib.h>
@ -37,23 +36,31 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
#define CPOOL_HEAD_SIGNATURE SIGNATURE_32('C','p','h','d')
/** The UEFI functions do not provide a way to determine the size of an /** The UEFI functions do not provide a way to determine the size of an
allocated region of memory given just a pointer to the start of that allocated region of memory given just a pointer to the start of that
region. Since this is required for the implementation of realloc, region. Since this is required for the implementation of realloc,
the memory head structure from Core/Dxe/Mem/Pool.c has been reproduced the memory head structure, CPOOL_HEAD, containing the necessary
here. information is prepended to the requested space.
NOTE: If the UEFI implementation is changed, the realloc function may cease The order of members is important. This structure is 8-byte aligned,
to function properly. as per the UEFI specification for memory allocation functions. By
specifying Size as a 64-bit value and placing it immediately before
Data, it ensures that Data will always be 8-byte aligned.
On IA32 systems, this structure is 24 bytes long, excluding Data.
On X64 systems, this structure is 32 bytes long, excluding Data.
**/ **/
#define POOL_HEAD_SIGNATURE SIGNATURE_32('p','h','d','0')
typedef struct { typedef struct {
LIST_ENTRY List;
UINT32 Signature; UINT32 Signature;
UINT32 Size; UINT64 Size;
EFI_MEMORY_TYPE Type;
UINTN Reserved;
CHAR8 Data[1]; CHAR8 Data[1];
} POOL_HEAD; } CPOOL_HEAD;
// List of memory allocated by malloc/calloc/etc.
static LIST_ENTRY MemPoolHead = INITIALIZE_LIST_HEAD_VARIABLE(MemPoolHead);
/****************************/ /****************************/
@ -76,19 +83,41 @@ typedef struct {
void * void *
malloc(size_t Size) malloc(size_t Size)
{ {
CPOOL_HEAD *Head;
void *RetVal; void *RetVal;
EFI_STATUS Status; EFI_STATUS Status;
UINTN NodeSize;
if( Size == 0) { if( Size == 0) {
errno = EINVAL; // Make errno diffenent, just in case of a lingering ENOMEM. errno = EINVAL; // Make errno diffenent, just in case of a lingering ENOMEM.
DEBUG((DEBUG_ERROR, "ERROR malloc: Zero Size\n"));
return NULL; return NULL;
} }
Status = gBS->AllocatePool( EfiLoaderData, (UINTN)Size, &RetVal); NodeSize = (UINTN)(Size + sizeof(CPOOL_HEAD));
DEBUG((DEBUG_POOL, "malloc(%d): NodeSz: %d", Size, NodeSize));
Status = gBS->AllocatePool( EfiLoaderData, NodeSize, &Head);
if( Status != EFI_SUCCESS) { if( Status != EFI_SUCCESS) {
RetVal = NULL; RetVal = NULL;
errno = ENOMEM; errno = ENOMEM;
DEBUG((DEBUG_ERROR, "\nERROR malloc: AllocatePool returned %r\n", Status));
} }
else {
assert(Head != NULL);
// Fill out the pool header
Head->Signature = CPOOL_HEAD_SIGNATURE;
Head->Size = NodeSize;
// Add this node to the list
(void)InsertTailList(&MemPoolHead, (LIST_ENTRY *)Head);
// Return a pointer to the data
RetVal = (void*)Head->Data;
DEBUG((DEBUG_POOL, " Head: %p, Returns %p\n", Head, RetVal));
}
return RetVal; return RetVal;
} }
@ -113,13 +142,15 @@ calloc(size_t Num, size_t Size)
size_t NumSize; size_t NumSize;
NumSize = Num * Size; NumSize = Num * Size;
if (NumSize == 0) { RetVal = NULL;
return NULL; if (NumSize != 0) {
}
RetVal = malloc(NumSize); RetVal = malloc(NumSize);
if( RetVal != NULL) { if( RetVal != NULL) {
(VOID)ZeroMem( RetVal, NumSize); (VOID)ZeroMem( RetVal, NumSize);
} }
}
DEBUG((DEBUG_POOL, "0x%p = calloc(%d, %d)\n", RetVal, Num, Size));
return RetVal; return RetVal;
} }
@ -137,9 +168,24 @@ calloc(size_t Num, size_t Size)
void void
free(void *Ptr) free(void *Ptr)
{ {
CPOOL_HEAD *Head;
Head = BASE_CR(Ptr, CPOOL_HEAD, Data);
assert(Head != NULL);
DEBUG((DEBUG_POOL, "free(%p): Head: %p\n", Ptr, Head));
if(Ptr != NULL) { if(Ptr != NULL) {
(void) gBS->FreePool (Ptr); if (Head->Signature == CPOOL_HEAD_SIGNATURE) {
(void) RemoveEntryList((LIST_ENTRY *)Head); // Remove this node from the malloc pool
(void) gBS->FreePool (Head); // Now free the associated memory
}
else {
errno = EFAULT;
DEBUG((DEBUG_ERROR, "ERROR free(0x%p): Signature is 0x%8X, expected 0x%8X\n",
Ptr, Head->Signature, CPOOL_HEAD_SIGNATURE));
} }
}
DEBUG((DEBUG_POOL, "free Done\n"));
} }
/** The realloc function changes the size of the object pointed to by Ptr to /** The realloc function changes the size of the object pointed to by Ptr to
@ -185,27 +231,31 @@ free(void *Ptr)
NULL is returned and errno will be unchanged. NULL is returned and errno will be unchanged.
**/ **/
void * void *
realloc(void *Ptr, size_t NewSize) realloc(void *Ptr, size_t ReqSize)
{ {
void *RetVal = NULL; void *RetVal = NULL;
POOL_HEAD *Head; CPOOL_HEAD *Head = NULL;
UINTN OldSize = 0; size_t OldSize = 0;
UINTN NumCpy; size_t NewSize;
size_t NumCpy;
// Find out the size of the OLD memory region // Find out the size of the OLD memory region
if( Ptr != NULL) { if( Ptr != NULL) {
Head = BASE_CR (Ptr, POOL_HEAD, Data); Head = BASE_CR (Ptr, CPOOL_HEAD, Data);
assert(Head != NULL); assert(Head != NULL);
if (Head->Signature != POOL_HEAD_SIGNATURE) { if (Head->Signature != CPOOL_HEAD_SIGNATURE) {
errno = EFAULT; errno = EFAULT;
DEBUG((DEBUG_ERROR, "ERROR realloc(0x%p): Signature is 0x%8X, expected 0x%8X\n",
Ptr, Head->Signature, CPOOL_HEAD_SIGNATURE));
return NULL; return NULL;
} }
OldSize = Head->Size; OldSize = (size_t)Head->Size;
} }
// At this point, Ptr is either NULL or a valid pointer to an allocated space // At this point, Ptr is either NULL or a valid pointer to an allocated space
NewSize = (size_t)(ReqSize + (sizeof(CPOOL_HEAD)));
if( NewSize > 0) { if( ReqSize > 0) {
RetVal = malloc(NewSize); // Get the NEW memory region RetVal = malloc(NewSize); // Get the NEW memory region
if( Ptr != NULL) { // If there is an OLD region... if( Ptr != NULL) { // If there is an OLD region...
if( RetVal != NULL) { // and the NEW region was successfully allocated if( RetVal != NULL) { // and the NEW region was successfully allocated
@ -216,13 +266,16 @@ realloc(void *Ptr, size_t NewSize)
(VOID)CopyMem( RetVal, Ptr, NumCpy); // Copy old data to the new region. (VOID)CopyMem( RetVal, Ptr, NumCpy); // Copy old data to the new region.
free( Ptr); // and reclaim the old region. free( Ptr); // and reclaim the old region.
} }
else {
errno = ENOMEM;
}
} }
} }
else { else {
if( Ptr != NULL) {
free( Ptr); // Reclaim the old region. free( Ptr); // Reclaim the old region.
} }
} DEBUG((DEBUG_POOL, "0x%p = realloc(%p, %d): Head: %p NewSz: %d\n",
RetVal, Ptr, ReqSize, Head, NewSize));
return RetVal; return RetVal;
} }