#include #include #include #include static size_t page_size() { return sysconf(_SC_PAGESIZE); } typedef char* address; typedef int bool; const int false = 0; const int true = 1; const size_t K = 1024; static char* anon_mmap(char* requested_addr, size_t bytes, bool fixed) { char * addr; int flags; flags = MAP_PRIVATE | MAP_NORESERVE | MAP_ANONYMOUS; if (fixed) { // assert((uintptr_t)requested_addr % os::Linux::page_size() == 0, "unaligned address"); flags |= MAP_FIXED; } // Map reserved/uncommitted pages PROT_NONE so we fail early if we // touch an uncommitted page. Otherwise, the read/write might // succeed if we have enough swap space to back the physical page. addr = (char*)mmap(requested_addr, bytes, PROT_NONE, flags, -1, 0); return addr == MAP_FAILED ? NULL : addr; } static address get_stack_mapped_bottom(address bottom, size_t size, bool committed_only /* must have backing pages */) { address ntop = bottom + size; address test_addr; size_t page_sz = page_size(); unsigned pages = size / page_sz; unsigned char vec[1]; unsigned imin = 0, imax = pages, imid; int mincore_return_value = 0; // assert(imin <= imax, "Unexpected page size"); while (imin < imax) { imid = (imax + imin) / 2; test_addr = bottom + (imid * page_sz); // Use a trick with mincore to check whether the page is mapped or not. // mincore sets vec to 1 if page resides in memory and to 0 if page // is swapped output but if page we are asking for is unmapped // it returns -1,ENOMEM mincore_return_value = mincore(test_addr, page_sz, vec); if (mincore_return_value == -1 || (committed_only && (vec[0] & 0x01) == 0)) { // Page is not mapped go up // to find first mapped page if ((mincore_return_value == -1 && errno != EAGAIN) || (committed_only && (vec[0] & 0x01) == 0)) { // assert(mincore_return_value != -1 || errno == ENOMEM, "Unexpected mincore errno"); // go higher for mapped/committed page imin = imid + 1; } } else { // mapped/committed page, remember it imax = imid; } } // nbot = nbot + page_sz; // Adjust stack bottom one page up if last checked page is not mapped test_addr = bottom + imid * page_sz; if (mincore_return_value == -1 || (committed_only && (vec[0] & 0x01) == 0)) { test_addr = test_addr + page_sz; } return test_addr; } int main(int argc, char* argv[]) { int pages, committed_pages; printf("Enter number of pages to map: "); scanf("%d", &pages); printf("Enter number of pages to commit: "); scanf("%d", &committed_pages); if (pages <= 0 || committed_pages < 0 || committed_pages > pages) { printf("Invalid pages/committed pages\n"); return -1; } size_t size = pages * page_size(); address addr = anon_mmap(NULL, size, false); if (addr == NULL) { printf("failed to anon_mmap, error = %d\n", errno); return -1; } address mapped_bottom = get_stack_mapped_bottom(addr, size, false); address committed_bottom = get_stack_mapped_bottom(addr, size, true); printf("mapped [%lx - %lx] (%d KB)", (unsigned long)addr, (unsigned long)(addr + size), (int)(size / K)); printf(" mapped_bottom %lx (%d KB), committed_bottom %lx (%d KB) \n", (unsigned long)mapped_bottom, (size - (mapped_bottom - addr)) / K, (unsigned long)committed_bottom, (size - (committed_bottom - addr)) / K); size_t commit_size = committed_pages * page_size(); address commit_base = addr + size - commit_size; void* res = mmap(commit_base, commit_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0); if (res == (void*) MAP_FAILED) { printf("commit failed\n"); return -1; } printf("committed [%lx - %lx] (%d KB)\n", (unsigned long)commit_base, (unsigned long)(commit_base + commit_size), (int)(commit_size / K)); // Write something to actually get pages mapped char* ch = (char*)commit_base; for (size_t index = 0; index < commit_size; index ++) { ch[index] = 'a'; } mapped_bottom = get_stack_mapped_bottom(addr, size, false); committed_bottom = get_stack_mapped_bottom(addr, size, true); printf(" mapped_bottom %lx (%d KB), committed_bottom %lx (%d KB) \n", (unsigned long)mapped_bottom, (size - (mapped_bottom - addr)) / 1024, (unsigned long)committed_bottom, (size - (committed_bottom - addr)) / 1024); ch[10] = '\0'; printf("What I wrote: %s\n", ch); return 0; }