#include #include #include #include "libc.h" #include "lock.h" #include "fork_impl.h" #define malloc __libc_malloc #define calloc __libc_calloc #define realloc undef #define free undef static struct fl { struct fl *next; struct fl_pair { void (*f)(void *); void *a; } pairs[]; } *head; static int slot; static size_t slot_max; static volatile int lock[1]; volatile int *const __atexit_lockptr = lock; void __funcs_on_exit() { void (*func)(void *), *arg; LOCK(lock); for (; head; head=head->next, slot=slot_max) { struct fl *next = head->next; while(slot-->0) { func = head->pairs[slot].f; arg = head->pairs[slot].a; UNLOCK(lock); func(arg); LOCK(lock); } munmap(head, PAGE_SIZE); head = next; } } void __cxa_finalize(void *dso) { } int __cxa_atexit(void (*func)(void *), void *arg, void *dso) { struct fl *cursor; LOCK(lock); /* Figure out how many slots we can have per page: page size minus one pointer then divided by two for function-argument pairs. */ slot_max = (PAGE_SIZE - sizeof(void *)) / sizeof(struct fl_pair); /* Determine if a new allocation is necessary. */ if (!head || slot == slot_max) cursor = 0; else cursor = head; /* If a new allocation is necessary, allocate it read-write, otherwise make the current atexit list read-write. */ if (!cursor) { cursor = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0); if (cursor == MAP_FAILED) { UNLOCK(lock); return -1; } cursor->next = head; head = cursor; slot = 0; } else if (mprotect(cursor, PAGE_SIZE, PROT_READ | PROT_WRITE)) { UNLOCK(lock); return -1; } /* Append function to the list. */ cursor->pairs[slot].f = func; cursor->pairs[slot].a = arg; slot++; /* Mark the atexit list read-only to avoid its abuse. */ if (mprotect(cursor, PAGE_SIZE, PROT_READ)) { UNLOCK(lock); return -1; } UNLOCK(lock); return 0; } static void call(void *p) { ((void (*)(void))(uintptr_t)p)(); } int atexit(void (*func)(void)) { return __cxa_atexit(call, (void *)(uintptr_t)func, 0); }