/* Copyright (C) 2008 Emmanuel Varoquaux
This file is part of XOS.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
/* Allocateur de memoire minimaliste pour ld.so.
* Caracteristiques :
* - malloc() ne doit plus etre appele une fois que du code du programme
* principal a ete execute. En effet, le programme peut lui-meme modifier la
* taille du segment de donnees, ce qui invalide les structures de gestion de
* la memoire de ld.so.
* - free() ne libere pas de memoire, ni n'accede au systeme pour modifier la
* taille du segment de donnees. Il peut donc etre appele sans risque meme si
* du code utilisateur a ete execute.
* Par consequent la consommation memoire ne fait qu'augmenter, ce qui n'est
* pas genant vu la consommation tres limitee de ld.so. */
#include "stdlib.h"
#include "unistd.h"
#include
#include
#define BITS (sizeof (size_t) * 8)
#define MEMORY_GROW 131072 /* 128K */
/* bloc de memoire */
struct block_struct {
size_t size; /* taille du bloc */
void *data[1];
};
#define BLOCK_HEADER_SIZE (offsetof(struct block_struct, data))
/* taille minimale d'un bloc */
static size_t min_block_size;
static void *memory_end;
/* tas */
static void *heap_end = NULL;
static inline size_t nextpow2(size_t n)
{
register unsigned int k;
n--;
for (k = 1; k < BITS; k <<= 1)
n = n | n >> k;
return n + 1;
}
/* Allocation de memoire aupres du systeme */
static int init_memory(void **memory_start)
{
void *addr;
if ((addr = __sbrk(0)) == (void *)-1)
return -1;
*memory_start = memory_end = addr;
return 0;
}
static int resize_memory(void *end)
{
size_t incr;
void *addr;
if (end > memory_end) {
incr = (end - memory_end + (MEMORY_GROW - 1)) / MEMORY_GROW * MEMORY_GROW;
if ((addr = __sbrk(incr)) == (void *)-1)
return -1;
memory_end = addr + incr;
}
return 0;
}
/* Fonctions sur le tas */
static int setup_heap()
{
min_block_size = nextpow2(sizeof (struct block_struct));
if (init_memory(&heap_end) == -1)
return -1;
/* alignement de heap_end de sorte que le pointeur sur les donnees des
blocs soit correctement aligne pour n'importe quel type d'objet (ANSI C)
*/
heap_end += BLOCK_HEADER_SIZE;
heap_end = (void *)(((intptr_t)heap_end | (sizeof (union {long l; double d;}) - 1)) + 1);
heap_end -= BLOCK_HEADER_SIZE;
return 0;
}
/* size doit etre un diviseur de heap_size. */
static int grow_heap(size_t incr)
{
if (resize_memory(heap_end + incr) == -1)
return -1;
heap_end += incr;
return 0;
}
/* Fonctions sur les blocs */
static struct block_struct *alloc_block(size_t size)
{
struct block_struct *block;
block = heap_end;
if (grow_heap(size) == -1)
return NULL;
block->size = size;
return block;
}
/* Redimensionne un bloc alloue, si possible. */
static int resize_block(struct block_struct *block, size_t size)
{
if (size == block->size)
return 1;
else if (size > block->size)
return 0;
else { /* size < block->size */
block->size = size;
return 1;
}
}
/* D'apres le standard, "If the size of the space requested is 0, the behavior
* is implementation-defined: the value returned shall be either a null pointer
* or a unique pointer.". Ici, nous renvoyons un pointeur unique. */
void __attribute__ ((weak, visibility ("default"))) *malloc(size_t size)
{
struct block_struct *block;
if (!heap_end)
if (setup_heap() == -1)
return NULL;
size += BLOCK_HEADER_SIZE;
size = (size + (min_block_size - 1)) & ~(min_block_size - 1);
if (!(block = alloc_block(size)))
return NULL;
return block->data;
}
typeof (malloc) __malloc __attribute__ ((alias ("malloc")));
void __attribute__ ((weak, visibility ("default"))) *calloc(size_t nmemb, size_t size)
{
void *ptr;
if (!(ptr = __malloc(nmemb * size)))
return NULL;
memset(ptr, 0, nmemb * size);
return ptr;
}
typeof (calloc) __calloc __attribute__ ((alias ("calloc")));
void __attribute__ ((weak, visibility ("default"))) free(void *ptr) {}
typeof (free) __free __attribute__ ((alias ("free")));
void __attribute__ ((weak, visibility ("default"))) *realloc(void *ptr, size_t size)
{
struct block_struct *block, *new_block;
if (ptr == NULL)
return __malloc(size);
if (size == 0) {
__free(ptr);
return NULL;
}
size += BLOCK_HEADER_SIZE;
size = (size + (min_block_size - 1)) & ~(min_block_size - 1);
block = ptr - BLOCK_HEADER_SIZE;
/* On essaie d'abord de redimensionner le bloc. */
if (resize_block(block, size))
return ptr;
/* S'il n'est pas possible de redimensionner le bloc, on en alloue un nouveau
et on copie les donnees dedans. */
if (!(new_block = alloc_block(size)))
return NULL;
memcpy(new_block->data, block->data, (size < block->size ? size : block->size) - BLOCK_HEADER_SIZE);
return new_block->data;
}
typeof (realloc) __realloc __attribute__ ((alias ("realloc")));