diff --git a/src/system/runtime_loader/Jamfile b/src/system/runtime_loader/Jamfile index 38057f2cd0..6a1d97e35d 100644 --- a/src/system/runtime_loader/Jamfile +++ b/src/system/runtime_loader/Jamfile @@ -59,7 +59,7 @@ Objects runtime_loader.c elf.c export.c - heap.c + heap.cpp utility.cpp arch_relocate.c ; diff --git a/src/system/runtime_loader/elf.c b/src/system/runtime_loader/elf.c index 59ca39aec4..212d99af28 100644 --- a/src/system/runtime_loader/elf.c +++ b/src/system/runtime_loader/elf.c @@ -310,13 +310,15 @@ count_regions(char const *buff, int phnum, int phentsize) static image_t * create_image(const char *name, const char *path, int num_regions) { - size_t allocSize; - image_t *image; + size_t allocSize = sizeof(image_t) + (num_regions - 1) * sizeof(elf_region_t); const char *lastSlash; - allocSize = sizeof(image_t) + (num_regions - 1) * sizeof(elf_region_t); + image_t *image = malloc(allocSize); + if (image == NULL) { + FATAL("no memory for image %s\n", path); + return NULL; + } - image = rldalloc(allocSize); memset(image, 0, allocSize); strlcpy(image->path, path, sizeof(image->path)); @@ -342,12 +344,12 @@ delete_image_struct(image_t *image) size_t size = sizeof(image_t) + (image->num_regions - 1) * sizeof(elf_region_t); memset(image->needed, 0xa5, sizeof(image->needed[0]) * image->num_needed); #endif - rldfree(image->needed); + free(image->needed); #ifdef DEBUG memset(image, 0xa5, size); #endif - rldfree(image); + free(image); } @@ -990,6 +992,7 @@ load_container(char const *name, image_type type, const char *rpath, image_t **_ _kern_close(fd); enqueue_image(&sLoadedImages, image); + sLoadedImageCount++; *_image = image; return B_OK; @@ -1032,15 +1035,19 @@ load_dependencies(image_t *image) return B_OK; image->flags |= RFLAG_DEPENDENCIES_LOADED; + if (image->num_needed == 0) { + image->needed = NULL; + return B_OK; + } - rpath = find_dt_rpath(image); - - image->needed = rldalloc(image->num_needed * sizeof(image_t *)); + image->needed = malloc(image->num_needed * sizeof(image_t *)); if (image->needed == NULL) { FATAL("failed to allocate needed struct\n"); return B_NO_MEMORY; } + memset(image->needed, 0, image->num_needed * sizeof(image_t *)); + rpath = find_dt_rpath(image); for (i = 0, j = 0; d[i].d_tag != DT_NULL; i++) { switch (d[i].d_tag) { @@ -1088,16 +1095,18 @@ topological_sort(image_t *image, uint32 slot, image_t **initList, } -static uint32 +static ssize_t get_sorted_image_list(image_t *image, image_t ***_list, uint32 sortFlag) { image_t **list; - list = rldalloc(sLoadedImageCount * sizeof(image_t *)); + list = malloc(sLoadedImageCount * sizeof(image_t *)); if (list == NULL) { FATAL("memory shortage in get_sorted_image_list()"); - return 0; // B_NO_MEMORY + *_list = NULL; + return B_NO_MEMORY; } + memset(list, 0, sLoadedImageCount * sizeof(image_t *)); *_list = list; @@ -1108,10 +1117,12 @@ get_sorted_image_list(image_t *image, image_t ***_list, uint32 sortFlag) static status_t relocate_dependencies(image_t *image) { - uint32 count, i; + ssize_t count, i; image_t **list; count = get_sorted_image_list(image, &list, RFLAG_RELOCATED); + if (count < B_OK) + return count; for (i = 0; i < count; i++) { status_t status = relocate_image(list[i]); @@ -1119,7 +1130,7 @@ relocate_dependencies(image_t *image) return status; } - rldfree(list); + free(list); return B_OK; } @@ -1128,10 +1139,10 @@ static void init_dependencies(image_t *image, bool initHead) { image_t **initList; - uint32 count, i; + ssize_t count, i; count = get_sorted_image_list(image, &initList, RFLAG_INITIALIZED); - if (count == 0) + if (count <= 0) return; if (!initHead) { @@ -1151,7 +1162,7 @@ init_dependencies(image_t *image, bool initHead) } TRACE(("%ld: init done.\n", find_thread(NULL))); - rldfree(initList); + free(initList); } @@ -1475,10 +1486,10 @@ void terminate_program(void) { image_t **termList; - uint32 count, i; + ssize_t count, i; count = get_sorted_image_list(sProgramImage, &termList, RFLAG_TERMINATED); - if (count == 0) + if (count < B_OK) return; TRACE(("%ld: terminate dependencies\n", find_thread(NULL))); @@ -1492,8 +1503,7 @@ terminate_program(void) } TRACE(("%ld: term done.\n", find_thread(NULL))); - rldfree(termList); - + free(termList); } diff --git a/src/system/runtime_loader/heap.c b/src/system/runtime_loader/heap.c deleted file mode 100644 index 10b9877f90..0000000000 --- a/src/system/runtime_loader/heap.c +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2002, Manuel J. Petit. All rights reserved. - * Distributed under the terms of the NewOS License. - */ - - -#include "runtime_loader_private.h" - -#include -#include - - -#define RLD_SCRATCH_SIZE 65536 - - -static area_id rld_region; -static char *rld_base; -static char *rld_ptr; - - -void -rldheap_init(void) -{ - rld_region = _kern_create_area("rld scratch", - (void **)&rld_base, B_ANY_ADDRESS, RLD_SCRATCH_SIZE, - B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); - - rld_ptr = rld_base; -} - - -void * -rldalloc(size_t s) -{ - void *retval; - - s = (s + 15) & ~15; - // multiple of 16 bytes - - retval = rld_ptr; - rld_ptr += s; - - return retval; -} - - -void -rldfree(void *p) -{ - (void)(p); -} - diff --git a/src/system/runtime_loader/heap.cpp b/src/system/runtime_loader/heap.cpp new file mode 100644 index 0000000000..201c64b38f --- /dev/null +++ b/src/system/runtime_loader/heap.cpp @@ -0,0 +1,328 @@ +/* + * Copyright 2003-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "runtime_loader_private.h" + +#include +#include + +#include +#include +#include + + +static const size_t kInitialHeapSize = 65536; + + +/* This is a very simple malloc()/free() implementation - it only + * manages a free list. + * After heap_init() is called, all free memory is contained in one + * big chunk, the only entry in the free link list (which is a single + * linked list). + * When memory is allocated, the smallest free chunk that contains + * the requested size is split (or taken as a whole if it can't be + * splitted anymore), and it's lower half will be removed from the + * free list. + * The free list is ordered by size, starting with the smallest + * free chunk available. When a chunk is freed, it will be joint + * with its predecessor or successor, if possible. + * To ease list handling, the list anchor itself is a free chunk with + * size 0 that can't be allocated. + */ + + +struct free_chunk { + uint32 size; + free_chunk *next; + + uint32 Size() const; + free_chunk *Split(uint32 splitSize); + bool IsTouching(free_chunk *link); + free_chunk *Join(free_chunk *link); + void Remove(free_chunk *previous = NULL); + void Enqueue(); + + void *AllocatedAddress() const; + static free_chunk *SetToAllocated(void *allocated); +}; + + +static void *sHeapBase; +static uint32 /*sHeapSize,*/ sMaxHeapSize, sAvailable; +static free_chunk sFreeAnchor; + + +/** Returns the amount of bytes that can be allocated + * in this chunk. + */ + +uint32 +free_chunk::Size() const +{ + return size - sizeof(uint32); +} + + +/** Splits the upper half at the requested location + * and returns it. + */ + +free_chunk * +free_chunk::Split(uint32 splitSize) +{ + free_chunk *chunk = (free_chunk *)((uint8 *)this + sizeof(uint32) + splitSize); + chunk->size = size - splitSize - sizeof(uint32); + chunk->next = next; + + size = splitSize + sizeof(uint32); + + return chunk; +} + + +/** Checks if the specified chunk touches this chunk, so + * that they could be joined. + */ + +bool +free_chunk::IsTouching(free_chunk *chunk) +{ + return chunk + && (((uint8 *)this + size == (uint8 *)chunk) + || (uint8 *)chunk + chunk->size == (uint8 *)this); +} + + +/** Joins the chunk to this chunk and returns the pointer + * to the new chunk - which will either be one of the + * two chunks. + * Note, the chunks must be joinable, or else this method + * doesn't work correctly. Use free_chunk::IsTouching() + * to check if this method can be applied. + */ + +free_chunk * +free_chunk::Join(free_chunk *chunk) +{ + if (chunk < this) { + chunk->size += size; + chunk->next = next; + + return chunk; + } + + size += chunk->size; + next = chunk->next; + + return this; +} + + +void +free_chunk::Remove(free_chunk *previous) +{ + if (previous == NULL) { + // find the previous chunk in the list + free_chunk *chunk = sFreeAnchor.next; + + while (chunk != NULL && chunk != this) { + previous = chunk; + chunk = chunk->next; + } + + if (chunk == NULL) { + printf("runtime_loader: try to remove chunk that's not in list"); + return; + } + } + + previous->next = this->next; + this->next = NULL; +} + + +void +free_chunk::Enqueue() +{ + free_chunk *chunk = sFreeAnchor.next, *last = &sFreeAnchor; + while (chunk && chunk->Size() < size) { + last = chunk; + chunk = chunk->next; + } + + this->next = chunk; + last->next = this; +} + + +void * +free_chunk::AllocatedAddress() const +{ + return (void *)&next; +} + + +free_chunk * +free_chunk::SetToAllocated(void *allocated) +{ + return (free_chunk *)((uint8 *)allocated - sizeof(uint32)); +} + + +// #pragma mark - + + +status_t +heap_init(void) +{ + area_id area = _kern_create_area("rld heap", + &sHeapBase, B_ANY_ADDRESS, kInitialHeapSize, + B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); + if (area < B_OK) + return area; + + sMaxHeapSize = kInitialHeapSize; + sAvailable = sMaxHeapSize - sizeof(uint32); + + // declare the whole heap as one chunk, and add it + // to the free list + + free_chunk *chunk = (free_chunk *)sHeapBase; + chunk->size = sMaxHeapSize; + chunk->next = NULL; + + sFreeAnchor.size = 0; + sFreeAnchor.next = chunk; + + return B_OK; +} + + +#if 0 +char * +grow_heap(uint32 bytes) +{ + char *start; + + if (sHeapSize + bytes > sMaxHeapSize) + return NULL; + + start = (char *)sHeapBase + sHeapSize; + memset(start, 0, bytes); + sHeapSize += bytes; + + return start; +} +#endif + +#ifdef HEAP_TEST +void +dump_chunks(void) +{ + free_chunk *chunk = sFreeAnchor.next, *last = &sFreeAnchor; + while (chunk != NULL) { + last = chunk; + + printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk, chunk->size, (uint8 *)chunk + chunk->size, chunk->next); + chunk = chunk->next; + } +} + + +uint32 +heap_available(void) +{ + return sAvailable; +} +#endif + + +void * +malloc(size_t size) +{ + if (sHeapBase == NULL || size == 0) + return NULL; + + // align the size requirement to an 8 bytes boundary + size = (size + 7) & ~7; + + if (size > sAvailable) + return NULL; + + free_chunk *chunk = sFreeAnchor.next, *last = &sFreeAnchor; + while (chunk && chunk->Size() < size) { + last = chunk; + chunk = chunk->next; + } + + if (chunk == NULL) { + // could not find a free chunk as large as needed + return NULL; + } + + if (chunk->Size() > size + sizeof(free_chunk) + 4) { + // if this chunk is bigger than the requested size, + // we split it to form two chunks (with a minimal + // size of 4 allocatable bytes). + + free_chunk *freeChunk = chunk->Split(size); + last->next = freeChunk; + + // re-enqueue the free chunk at the correct position + freeChunk->Remove(last); + freeChunk->Enqueue(); + } else { + // remove the chunk from the free list + + last->next = chunk->next; + } + + sAvailable -= size + sizeof(uint32); + + return chunk->AllocatedAddress(); +} + + +void +free(void *allocated) +{ + if (allocated == NULL) + return; + + free_chunk *freedChunk = free_chunk::SetToAllocated(allocated); + sAvailable += freedChunk->size; + + // try to join the new free chunk with an existing one + // it may be joined with up to two chunks + + free_chunk *chunk = sFreeAnchor.next, *last = &sFreeAnchor; + int32 joinCount = 0; + + while (chunk) { + if (chunk->IsTouching(freedChunk)) { + // almost "insert" it into the list before joining + // because the next pointer is inherited by the chunk + freedChunk->next = chunk->next; + freedChunk = chunk->Join(freedChunk); + + // remove the joined chunk from the list + last->next = freedChunk->next; + chunk = last; + + if (++joinCount == 2) + break; + } + + last = chunk; + chunk = chunk->next; + } + + // enqueue the link at the right position; the + // free link queue is ordered by size + + freedChunk->Enqueue(); +} + diff --git a/src/system/runtime_loader/runtime_loader.c b/src/system/runtime_loader/runtime_loader.c index 7fc6448b07..339c9c6467 100644 --- a/src/system/runtime_loader/runtime_loader.c +++ b/src/system/runtime_loader/runtime_loader.c @@ -352,7 +352,9 @@ runtime_loader(void *_args) close(2); open("/dev/console", 0); /* stderr */ #endif - rldheap_init(); + if (heap_init() < B_OK) + return 1; + rldexport_init(); rldelf_init(); diff --git a/src/system/runtime_loader/runtime_loader_private.h b/src/system/runtime_loader/runtime_loader_private.h index e668c43cd2..0d01cd556f 100644 --- a/src/system/runtime_loader/runtime_loader_private.h +++ b/src/system/runtime_loader/runtime_loader_private.h @@ -42,10 +42,7 @@ status_t elf_verify_header(void *header, int32 length); void rldelf_init(void); void rldexport_init(void); -// RLD heap -void rldheap_init(void); -void *rldalloc(size_t); -void rldfree(void *p); +status_t heap_init(void); // arch dependent prototypes status_t arch_relocate_image(image_t *image);