* 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:
parent
3862cbfeee
commit
6f0994d460
@ -59,7 +59,7 @@ Objects
|
||||
runtime_loader.c
|
||||
elf.c
|
||||
export.c
|
||||
heap.c
|
||||
heap.cpp
|
||||
utility.cpp
|
||||
arch_relocate.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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
328
src/system/runtime_loader/heap.cpp
Normal file
328
src/system/runtime_loader/heap.cpp
Normal 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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user