/** * @file libsbrk.c * @brief Naive implementation of sbrk() * * QNX doesn't support sbrk(), and heap-allocated data can reside anywhere in * the virtual address space. However, Emacs depdends on sbrk() for its own * allocator implementation, as well as for dumping a pre-loaded image. * This implementation reserves a non-backed region in the virtual address * space. A call to sbrk() that moves the break point causes the sub range * between the old and new break points to be backed by virtual memory. */ #include #include #include #define SEGMENT_SIZE 1024 * 1024 * 1024 static void *base_addr = MAP_FAILED; static uintptr_t break_addr; static uintptr_t max_addr; /** * Moves the break point of the emulated data segment. * @param increment Amount of memory to add to the data segment * @return Previous break point, if successful, -1 on failure */ void * sbrk(intptr_t increment) { if (base_addr == MAP_FAILED) { // Allocate a non-backed region of the address space. base_addr = mmap(0, SEGMENT_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); if (base_addr == MAP_FAILED) { errno = ENOMEM; return (void *)-1; } break_addr = (uintptr_t)base_addr; max_addr = (uintptr_t)base_addr + SEGMENT_SIZE; } // Round up to a page size. increment = (increment + __PAGESIZE - 1) & ~(__PAGESIZE - 1); // Check for overflow. uintptr_t new_addr = break_addr + increment; if (new_addr >= max_addr) { errno = ENOMEM; return (void *)-1; } // Only support incrementing the break point (though it's not hard to use // mprotect(PROT_NONE) to handle decrementing as well). if (new_addr <= break_addr) { return (void *)break_addr; } // Back the new sub-range. if (mmap((void *)break_addr, increment, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0) == MAP_FAILED) { errno = ENOMEM; return (void *)-1; } void *result = (void *)break_addr; break_addr = new_addr; return result; }