* Switched to the boot loader's heap implementation.

* This sane heap revealed a bunch of bugs (like sLoadedImageCount not being
  maintained that was used for various allocations).
* If the allocation of the image fails, opening the app will now just fail
  instead of crashing of overwriting random data (IOW VLC will now load
  okay, but cannot load some of its add-ons anymore).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19045 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2006-10-11 16:02:43 +00:00
parent 3862cbfeee
commit 6f0994d460
6 changed files with 364 additions and 79 deletions

View File

@ -59,7 +59,7 @@ Objects
runtime_loader.c
elf.c
export.c
heap.c
heap.cpp
utility.cpp
arch_relocate.c
;

View File

@ -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);
}

View File

@ -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 <string.h>
#include <syscalls.h>
#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);
}

View File

@ -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 <syscalls.h>
#include <util/kernel_cpp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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();
}

View File

@ -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();

View File

@ -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);