diff --git a/.gitignore b/.gitignore index 7d45161..e8bcbb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ a.out +*~ +*.swp *.o *.a +*.so +*.class +*.log +*.core diff --git a/malloc/Makefile b/malloc/Makefile new file mode 100644 index 0000000..6b16c91 --- /dev/null +++ b/malloc/Makefile @@ -0,0 +1,29 @@ +CC=gcc +CFLAGS=-std=c99 -pedantic -Werror -Wall -Wextra -Wvla -fvisibility=hidden -fPIC +CPPFLAGS= -D_DEFAULT_SOURCE +LDFLAGS= -shared -Wl,--no-undefined +LDLIBS= + +SRCS=src/malloc.c src/helpers/helpers.c src/helpers/allocate.c +OBJS=${SRCS:.c=.o} +TST_OBJS = tests/test.o +TARGET=libmalloc.so + +library: $(OBJS) + $(CC) -o $(TARGET) $^ $(LDFLAGS) + +debug: CFLAGS += -g +debug: $(OBJS) + $(CC) -o $(TARGET) $^ $(LDFLAGS) + +# test: LDLIBS += -lcriterion +test: CFLAGS = -g -L. -lmalloc -fPIC -fvisibility=hidden +test: library $(TST_OBJS) + $(CC) -o $(TST_TARGET) $^ $(LDLIBS) + +check: + dash tests/test-cmd.sh + +clean: + $(RM) $(OBJS) + $(RM) $(TARGET) diff --git a/malloc/src/helpers/allocate.c b/malloc/src/helpers/allocate.c new file mode 100644 index 0000000..350a38b --- /dev/null +++ b/malloc/src/helpers/allocate.c @@ -0,0 +1,94 @@ +#include "allocate.h" + +#include +#include + +#include "helpers.h" +// c'est dangereux de pas s'i3lock +// coucou +void *allocate_block(struct bucket *biquette, int block_number) +{ + if (block_number == -1) + return NULL; + + // Update header + unsigned char byte_mask = 1 << (block_number % 8); + biquette->alloc_blk_cnt++; + biquette->free_map[block_number / 8] |= byte_mask; + + // Compute block address + return get_block(biquette, block_number); +} + +struct bucket *create_bucket(size_t min_alloc_size) +{ + size_t page_alloc_size; + size_t block_alloc_size; + + if (min_alloc_size > PAGE_SIZE - sizeof(struct bucket)) + { + // Huge page + page_alloc_size = min_alloc_size + sizeof(struct bucket); + block_alloc_size = page_alloc_size; + } + else + { + // Regular page + page_alloc_size = PAGE_SIZE; + block_alloc_size = s2p(min_alloc_size); + } + + // Get page + void *page = mmap(NULL, page_alloc_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + if (page == NULL) + return NULL; + + // Init bucket + struct bucket *header = page; + header->block_size = block_alloc_size; + header->alloc_blk_cnt = 0; + header->alloc_size = page_alloc_size; + memset(header->free_map, 0, FREE_LIST_SIZE); + + return header; +} + +void free_block(void *ptr, struct bucket **list) +{ + // Compute header location and block number + struct bucket *header = get_page_begin(ptr); + + // Verify that header belongs to list + struct bucket *elt = *list; + int found = 0; + while (elt != NULL) + { + if (elt == header) + { + found = 1; + break; + } + elt = elt->next; + } + if (!found) + return; + + // free in map and update size + int block_number = get_block_number(header, ptr); + if (block_number == -1) + return; + + unsigned char byte_mask = 1 << ((block_number % 8)); + header->free_map[block_number / 8] &= ~byte_mask; + header->alloc_blk_cnt--; + + // Huge page + // free bucket if necessary + if (header->alloc_blk_cnt == 0) + { + bucket_list_remove(list, header); + munmap(header, header->alloc_size); + } +} diff --git a/malloc/src/helpers/allocate.h b/malloc/src/helpers/allocate.h new file mode 100644 index 0000000..fdb723b --- /dev/null +++ b/malloc/src/helpers/allocate.h @@ -0,0 +1,13 @@ +#ifndef ALLOCATE_H +#define ALLOCATE_H + +#include + +#include "helpers.h" + +void *allocate_block(struct bucket *biquette, int block_number); +struct bucket *create_bucket(size_t min_alloc_size); +void bucket_append(struct bucket **head, struct bucket *elt); +void free_block(void *ptr, struct bucket **list); + +#endif // ! ALLOCATE_H diff --git a/malloc/src/helpers/helpers.c b/malloc/src/helpers/helpers.c new file mode 100644 index 0000000..bde2907 --- /dev/null +++ b/malloc/src/helpers/helpers.c @@ -0,0 +1,170 @@ +#include "helpers.h" + +// Returns the smallest power of 2 greater or equal to s +size_t s2p(size_t s) +{ + // Trick I found on the internet but I don't completely understand it + // Quite amazing + // n--; // 1101 1101 --> 1101 1100 + // n |= n >> 1; // 1101 1100 | 0110 1110 = 1111 1110 + // n |= n >> 2; // 1111 1110 | 0011 1111 = 1111 1111 + // n |= n >> 4; // ... + // n |= n >> 8; + // n |= n >> 16; // 1111 1111 | 1111 1111 = 1111 1111 + // n++; // 1111 1111 --> 1 0000 0000 + + // There is also an x86 assembly instruction to do that in a single cycle: + // lzcnt + // Yeah... This one is definetely wild intel + // Now since asm{} is prohibited in the coding style it won't be useful + // Or maybe there is a builtin to do that ? + // (found __builtin_clzll but not sure of what it does) + + // So here's the good old way + size_t n = MIN_BLOCK_SIZE; + while (n < s) + { + n <<= 1; + } + + return n; +} + +int find_free_block(struct bucket *buck) +{ + // Huge pages + if (buck->block_size > BUCKET_SIZE) + { + // Check block 0 + if (!(buck->free_map[0] & (1 << 0))) + return 0; + return -1; + } + + size_t num_blocks_in_page = + (PAGE_SIZE - sizeof(struct bucket)) / buck->block_size; + size_t maplength = (num_blocks_in_page + 7) + / 8; // +7 for ceiling division (oui c'est moche mais ça marche) + + // Foreach byte + for (unsigned i = 0; i < maplength; i++) + { + unsigned char map_byte = buck->free_map[i]; + if (map_byte == 0xFF) // Skip if used + continue; + + for (unsigned j = 0; j < 8; j++) + { + if ((i * 8 + j) < num_blocks_in_page + && !(map_byte & (1 << j))) // Check if free + return i * 8 + j; + } + } + + return -1; +} + +// Computes the block address based on its position in the freemap +void *get_block(struct bucket *biquette, int block_number) +{ + if (block_number == -1) + return NULL; + + char *data_begin = get_data_begin(biquette); + return data_begin + (biquette->block_size * (block_number)); +} + +// Gets block position in the freemap based on its address +int get_block_number(struct bucket *biquette, void *block) +{ + if (block == NULL || biquette == NULL) + return -1; + + char *block_nul = block; + char *bibiche = + get_data_begin(biquette); // Célèbre bar Capcinois, venez à l'occasion + + return (block_nul - bibiche) / biquette->block_size; + + // Au revoir mes belles + // Vous manquerez à papa + // (mais fallait marcher, là ça marche pas) + + // void *biquetteounette = biquette; + // char *biquette_cherie = biquetteounette; + + // return (block_nul - biquette_cherie - sizeof(struct bucket)) + // / biquette->block_size; +} + +// Gets a bucket with at least a block_size of size +// Returns NULL if nothing was found +struct bucket *get_bucket(struct bucket *head, size_t size) +{ + while (head != NULL) + { + if (head->block_size >= size) + return head; + + head = head->next; + } + + return NULL; +} + +// Computes the buckets first block address +void *get_data_begin(struct bucket *biquette) +{ + void *maisqueltrucdemerde = biquette; + char *biquettas = maisqueltrucdemerde; + return biquettas + sizeof(struct bucket); +} + +// Computes the beginning address of the page containing ptr +void *get_page_begin(void *ptr) +{ + size_t quelenfer = ((size_t)ptr) & ~(PAGE_SIZE - 1); + char *pointeur_alacon = NULL; + return pointeur_alacon + quelenfer; +} + +// Appends a bucket address to the list pointed by head +void bucket_list_append(struct bucket **head, struct bucket *elt) +{ + struct bucket *list_elt = *head; + if (list_elt == NULL) + { + *head = elt; + return; + } + + while (list_elt->next != NULL) + { + list_elt = list_elt->next; + } + list_elt->next = elt; +} + +// Removes a bucket from the list pointed by head +void bucket_list_remove(struct bucket **head, struct bucket *elt) +{ + struct bucket *list_elt = *head; + if (list_elt == NULL) + return; + + if (*head == elt) + { + *head = elt->next; + return; + } + + while (list_elt->next != NULL) + { + if (list_elt->next == elt) + { + list_elt->next = list_elt->next->next; + return; + } + list_elt = list_elt->next; + } +} diff --git a/malloc/src/helpers/helpers.h b/malloc/src/helpers/helpers.h new file mode 100644 index 0000000..8c64cfe --- /dev/null +++ b/malloc/src/helpers/helpers.h @@ -0,0 +1,40 @@ +#ifndef HELPERS_H +#define HELPERS_H + +// #define PAGE_SIZE sysconf(_SC_PAGE_SIZE) +#define PAGE_SIZE 4096 +#define MIN_BLOCK_SIZE 16 +#define FREE_LIST_SIZE PAGE_SIZE / MIN_BLOCK_SIZE / sizeof(unsigned char) + +#include + +// Header +// Contains all informations about the concerned bucket +struct bucket +{ + unsigned block_size; + unsigned char free_map[FREE_LIST_SIZE]; // Keeps track of allocations + // If block_size > MIN_BLOCK_SIZE + // It will use a consecutive + // representation + unsigned short alloc_blk_cnt; // Counts the number of allocated blocks + struct bucket *next; + size_t alloc_size; + + unsigned short space1; // + unsigned int space2; // -> Both are used to align data on 16 bytes +}; + +#define BUCKET_SIZE PAGE_SIZE - sizeof(struct bucket) + +size_t s2p(size_t n); +int find_free_block(struct bucket *buck); +struct bucket *get_bucket(struct bucket *head, size_t size); +void *get_data_begin(struct bucket *biquette); +void *get_page_begin(void *ptr); +void *get_block(struct bucket *biquette, int block_number); +int get_block_number(struct bucket *biquette, void *block); +void bucket_list_append(struct bucket **head, struct bucket *elt); +void bucket_list_remove(struct bucket **head, struct bucket *elt); + +#endif // ! HELPERS_H diff --git a/malloc/src/malloc.c b/malloc/src/malloc.c new file mode 100644 index 0000000..535c23e --- /dev/null +++ b/malloc/src/malloc.c @@ -0,0 +1,82 @@ +#include +#include + +#include "helpers/allocate.h" +#include "helpers/helpers.h" + +struct bucket *head = NULL; + +__attribute__((visibility("default"))) void *malloc(size_t size) +{ + if (size == 0) + return NULL; + + // Find adequate bucket + struct bucket *buck = get_bucket(head, size); + while (buck != NULL) + { + // Find adequate block + int block_number = find_free_block(buck); + if (block_number != -1) + return allocate_block(buck, block_number); + else + { + // No free space -> loop + buck = get_bucket(buck->next, size); + } + } + + buck = create_bucket(size); + bucket_list_append(&head, buck); + int first_free_buche = find_free_block(buck); // De noel (pas la mienne) + return allocate_block(buck, first_free_buche); +} + +__attribute__((visibility("default"))) void free(void *ptr) +{ + if (ptr == NULL) + return; + free_block(ptr, &head); +} + +__attribute__((visibility("default"))) void *realloc(void *ptr, size_t size) +{ + if (size == 0) + return NULL; + if (ptr == NULL) + return malloc(size); + + struct bucket *header = get_page_begin(ptr); + if (header == NULL) + return NULL; // Shouldn't get here + + if (header->block_size >= size) + { + return ptr; + } + else + { + void *new_ptr = malloc(size); + if (new_ptr == NULL) + return NULL; + memcpy(new_ptr, ptr, header->block_size); + free(ptr); + return new_ptr; + } + + return NULL; +} + +__attribute__((visibility("default"))) void *calloc(size_t nmemb, size_t size) +{ + // Check overflow + size_t c; + if (__builtin_mul_overflow(nmemb, size, &c)) + return NULL; + + char *res = malloc(c); + if (res == NULL) + return NULL; + memset(res, 0, nmemb * size); + return res; +} diff --git a/malloc/tests/corruptionproof b/malloc/tests/corruptionproof new file mode 100755 index 0000000..26ebc81 Binary files /dev/null and b/malloc/tests/corruptionproof differ diff --git a/malloc/tests/memoryfootprint b/malloc/tests/memoryfootprint new file mode 100755 index 0000000..1718375 Binary files /dev/null and b/malloc/tests/memoryfootprint differ diff --git a/malloc/tests/speed b/malloc/tests/speed new file mode 100755 index 0000000..a1026c8 Binary files /dev/null and b/malloc/tests/speed differ diff --git a/malloc/tests/test-cmd.sh b/malloc/tests/test-cmd.sh new file mode 100755 index 0000000..4ff60e6 --- /dev/null +++ b/malloc/tests/test-cmd.sh @@ -0,0 +1,24 @@ +#!/bi/sh + +RED='\033[0;31m' +NC='\033[0m' # No color + +echo Hello world! + +check() +{ + if [ $? -ne 0 ]; then + echo $RED ERROR $NC + fi +} +LD_PRELOAD=./libmalloc.so ls 1> /dev/null +check +LD_PRELOAD=./libmalloc.so ip a 1> /dev/null +check +LD_PRELOAD=./libmalloc.so grep --help 1> /dev/null +check +LD_PRELOAD=./libmalloc.so find 1> /dev/null +check +LD_PRELOAD=./libmalloc.so tree 1> /dev/null +check +LD_PRELOAD=./libmalloc.so git status 1> /dev/null diff --git a/malloc/tests/test.c b/malloc/tests/test.c new file mode 100644 index 0000000..51a4f94 --- /dev/null +++ b/malloc/tests/test.c @@ -0,0 +1,25 @@ +// #include +// #include + +// TestSuite(Main); + +// Test(Main, test1) +// { +// int *i = malloc(16); +// cr_assert(i != NULL); +// } + +#include + +int main(void) +{ + int *test = malloc(8); + void *tes2 = malloc(8); + void *tes3 = malloc(32); + void *tes4 = malloc(64); + void *tes5 = malloc(128); + void *tes6 = malloc(256); + void *tes7 = malloc(512); + void *tes8 = malloc(4096); + void *tes9 = malloc(95956); +}