geode/loader/dobby/source/MemoryAllocator/NearMemoryArena.cc
2022-07-30 19:24:03 +03:00

275 lines
9.2 KiB
C++

#include "./NearMemoryArena.h"
#include "dobby_internal.h"
#include "UserMode/PlatformUtil/ProcessRuntimeUtility.h"
#include <iostream>
#include <vector>
using namespace zz;
#define KB (1024uLL)
#define MB (1024uLL * KB)
#define GB (1024uLL * MB)
LiteMutableArray *NearMemoryArena::page_chunks;
#if defined(WIN32)
static const void *memmem(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
if (!haystack || !needle) {
return haystack;
} else {
const char *h = (const char *)haystack;
const char *n = (const char *)needle;
size_t l = needlelen;
const char *r = h;
while (l && (l <= haystacklen)) {
if (*n++ != *h++) {
r = h;
n = (const char *)needle;
l = needlelen;
} else {
--l;
}
--haystacklen;
}
return l ? NULL : r;
}
}
#endif
static addr_t addr_max(addr_t a, addr_t b) {
return a > b ? a : b;
}
static addr_t addr_sub(addr_t a, addr_t b) {
return a > a - b ? a - b : 0;
}
static addr_t addr_add(addr_t a, addr_t b) {
return a < a + b ? a + b : (addr_t)-1;
}
static addr_t prev_page(addr_t cur, int pagesize) {
addr_t aligned_addr = ALIGN(cur, pagesize);
addr_t ret = aligned_addr - pagesize;
return ret <= aligned_addr ? ret : aligned_addr;
}
static addr_t next_page(addr_t cur, int pagesize) {
addr_t aligned_addr = ALIGN(cur, pagesize);
addr_t ret = aligned_addr + pagesize;
return ret >= aligned_addr ? ret : aligned_addr;
}
#if 1
static addr_t search_near_blank_page(addr_t pos, size_t alloc_range) {
addr_t min_page_addr, max_page_addr;
min_page_addr = next_page(addr_sub(pos, alloc_range), OSMemory::AllocPageSize());
max_page_addr = prev_page(addr_add(pos, alloc_range), OSMemory::AllocPageSize());
// region.start sorted
std::vector<MemoryRegion> process_memory_layout = ProcessRuntimeUtility::GetProcessMemoryLayout();
assert(process_memory_layout.size() > 0);
/*
* min_page_addr/--special-blank--/==region==/--right-blank--/max_page_addr
*/
addr_t resultPageAddr = 0, assumePageAddr = min_page_addr;
// check first region
addr_t first_region_start = (addr_t)process_memory_layout[0].address;
if (min_page_addr < first_region_start) {
resultPageAddr = prev_page(first_region_start, OSMemory::AllocPageSize());
resultPageAddr =
(addr_t)OSMemory::Allocate((void *)assumePageAddr, OSMemory::AllocPageSize(), MemoryPermission::kReadExecute);
if (resultPageAddr)
return resultPageAddr;
}
// check last region
MemoryRegion last_region = process_memory_layout[process_memory_layout.size() - 1];
addr_t last_region_end = (addr_t)last_region.address + last_region.length;
if (max_page_addr < last_region_end) {
resultPageAddr = next_page(last_region_end, OSMemory::AllocPageSize());
resultPageAddr =
(addr_t)OSMemory::Allocate((void *)assumePageAddr, OSMemory::AllocPageSize(), MemoryPermission::kReadExecute);
if (resultPageAddr)
return resultPageAddr;
}
for (int i = 0; i < static_cast<int>(process_memory_layout.size()); ++i) {
MemoryRegion region = process_memory_layout[i];
// check if assume-page-addr in memory-layout
addr_t region_end = (addr_t)region.address + region.length;
addr_t region_start = (addr_t)region.address;
if (region_end < max_page_addr) {
if (region_start >= min_page_addr) {
// find the region locate in the [min_page_addr, max_page_addr]
if (i >= 1 && assumePageAddr == min_page_addr) {
MemoryRegion prev_region;
prev_region = process_memory_layout[i - 1];
addr_t prev_region_end =
next_page((addr_t)prev_region.address + prev_region.length, OSMemory::AllocPageSize());
// check if have blank cave page
if (region_start > prev_region_end) {
assumePageAddr = addr_max(min_page_addr, prev_region_end);
resultPageAddr = (addr_t)OSMemory::Allocate((void *)assumePageAddr, OSMemory::AllocPageSize(),
MemoryPermission::kReadExecute);
if (resultPageAddr)
break;
}
}
if (i <= static_cast<int>(process_memory_layout.size() - 2)) {
// right-blank
MemoryRegion next_region = process_memory_layout[i + 1];
// check if have blank cave page
if (region_end < (addr_t)next_region.address) {
assumePageAddr = next_page((addr_t)region.address + region.length, OSMemory::AllocPageSize());
resultPageAddr = (addr_t)OSMemory::Allocate((void *)assumePageAddr, OSMemory::AllocPageSize(),
MemoryPermission::kReadExecute);
if (resultPageAddr)
break;
}
}
}
}
}
return resultPageAddr;
}
NearMemoryArena::NearMemoryArena() {
}
static addr_t search_near_blank_memory_chunk(addr_t pos, size_t alloc_range, int alloc_size) {
addr_t min_page_addr, max_page_addr;
min_page_addr = next_page(addr_sub(pos, alloc_range), OSMemory::AllocPageSize());
max_page_addr = prev_page(addr_add(pos, alloc_range), OSMemory::AllocPageSize());
std::vector<MemoryRegion> process_memory_layout = ProcessRuntimeUtility::GetProcessMemoryLayout();
uint8_t *blank_chunk_addr = NULL;
for (auto region : process_memory_layout) {
// check if assume-page-addr in memory-layout
if (region.permission == kReadExecute || region.permission == kRead) {
if (((addr_t)region.address + region.length) <= max_page_addr) {
if ((addr_t)region.address >= min_page_addr) {
#if defined(__APPLE__)
if (*(uint32_t *)region.address == 0xfeedfacf)
continue;
#endif
char *blank_memory = (char *)malloc(alloc_size);
memset(blank_memory, 0, alloc_size);
#if defined(__arm__) || defined(__aarch64__)
alloc_size += (4 - 1);
blank_chunk_addr = (uint8_t *)memmem(region.address, region.length, blank_memory, alloc_size);
if (blank_chunk_addr) {
int off = 4 - ((addr_t)blank_chunk_addr % 4);
blank_chunk_addr += off;
}
#else
blank_chunk_addr = (uint8_t *)memmem(region.address, region.length, blank_memory, alloc_size);
#endif
if (blank_chunk_addr)
break;
}
}
}
}
return (addr_t)blank_chunk_addr;
}
#endif
#define NEAR_PAGE_ARRAYLEN 8
int NearMemoryArena::PushPage(addr_t page_addr, MemoryPermission permission) {
PageChunk *newPage = new PageChunk;
newPage->page.address = (void *)page_addr;
newPage->page.length = OSMemory::PageSize();
newPage->page_cursor = page_addr;
newPage->permission = permission;
newPage->chunks = new LiteMutableArray(NEAR_PAGE_ARRAYLEN);
NearMemoryArena::page_chunks->pushObject(reinterpret_cast<LiteObject *>(newPage));
return RT_SUCCESS;
}
WritableDataChunk *NearMemoryArena::AllocateDataChunk(addr_t position, size_t alloc_range, int alloc_size) {
return NearMemoryArena::AllocateChunk(position, alloc_range, alloc_size, kReadWrite);
}
AssemblyCodeChunk *NearMemoryArena::AllocateCodeChunk(addr_t position, size_t alloc_range, int alloc_size) {
return NearMemoryArena::AllocateChunk(position, alloc_range, alloc_size, kReadExecute);
}
MemoryChunk *NearMemoryArena::AllocateChunk(addr_t position, size_t alloc_range, int alloc_size,
MemoryPermission permission) {
DLOG(0, "AllocateChunk 1");
if (page_chunks == NULL) {
page_chunks = new LiteMutableArray(NEAR_PAGE_ARRAYLEN);
}
MemoryChunk *result = NULL;
search_once_more:
DLOG(0, "AllocateChunk 2");
LiteCollectionIterator iter(NearMemoryArena::page_chunks);
PageChunk *page = NULL;
while ((page = reinterpret_cast<PageChunk *>(iter.getNextObject())) != NULL) {
if (page->permission == permission) {
if (llabs((intptr_t)(page->page_cursor - position)) < alloc_range) {
if ((page->page_cursor + alloc_size) < ((addr_t)page->page.address + page->page.length)) {
break;
}
}
}
}
DLOG(0, "AllocateChunk 3");
MemoryChunk *chunk = NULL;
if (page) {
chunk = new MemoryChunk;
chunk->address = (void *)page->page_cursor;
chunk->length = alloc_size;
// update page cursor
page->chunks->pushObject(reinterpret_cast<LiteObject *>(chunk));
page->page_cursor += alloc_size;
return chunk;
}
DLOG(0, "AllocateChunk 4");
addr_t blank_page_addr = 0;
blank_page_addr = search_near_blank_page(position, alloc_range);
if (blank_page_addr) {
DLOG(0, "AllocateChunk 5");
OSMemory::SetPermission((void *)blank_page_addr, OSMemory::PageSize(), permission);
DLOG(0, "AllocateChunk 6");
NearMemoryArena::PushPage(blank_page_addr, permission);
DLOG(0, "AllocateChunk 7");
goto search_once_more;
}
// TODO: fix up
if (permission == kReadWrite) {
DLOG(0, "AllocateChunk 8");
return NULL;
}
addr_t blank_chunk_addr = 0;
DLOG(0, "AllocateChunk 9");
blank_chunk_addr = search_near_blank_memory_chunk(position, alloc_range, alloc_size);
if (blank_chunk_addr) {
DLOG(0, "AllocateChunk 10");
MemoryChunk *chunk = NULL;
chunk = new MemoryChunk;
chunk->address = (void *)blank_chunk_addr;
chunk->length = alloc_size;
return chunk;
}
DLOG(0, "AllocateChunk 11");
return NULL;
}