I need to allocate large blocks of memory (to be used by my custom allocator) that fall within the first 32GB of virtual address space.
I imagine that if I needed, say, 1MB blocks, I could iterate using mmap and MAP_FIXED_NOREPLACE (or VirtualAlloc) from low addresses onwards in increments of, say, 1MB, until the call succeeds. Continue from the last successful block for the next one.
This sounds clumsy, but at least it will be somewhat robust against OS address space layout changes, and ASLR algorithm changes. From my understanding of current OS layouts, there should be plenty of memory available in the first 32GB this way, but maybe I am missing something?
Is there anything in Windows, Linux, OS X, iOS or Android that would defeat this scheme? Is there a better way?
Just in case you're wondering, this is for the implementation of a VM for a programming language where fitting all pointers in a 32-bit value on a 64-bit system could give huge memory usage advantages and even speed gains. Since all objects are at least 8-byte aligned, the lower 3 bits can be shifted out, expanding the pointer range from 4GB to 32GB.
For restricting the allocated memory range in Windows, we can use the
NtAllocateVirtualMemoryfunction. This API is available for use in both user and kernel mode. In user mode it exported by ntdll.dll (use ntdll.lib or ntdllp.lib from WDK). This function has a parameter called ZeroBits - The number of high-order address bits that must be zero in the base address of the section view. But in the msdn link above, the next few details about ZeroBits are incorrect. Here's a correct description:So really we can use ZeroBits as mask. This is its most powerful usage. But it can also be used as a zero bit count from the 31st bit. In this case, 63-32 bits will always be equal to 0 due to allocation granularity. Therefore a valid value for ZeroBits in bits-number mode would be any number from 1 to 15 (=31-16). To better understand how this parameter works, look at the example code below. For the sake of this demo, I will be using
MEM_TOP_DOWNas the AllocationType:Output:
So if we want to restrict memory allocation to
32Gb (0x800000000), we can useZeroBits = 0x800000000 - 1:This will allocate memory in the range
[0, 7FFFFFFFF]. (Actually, the range will be[0, 7FFFF0000]. Because of allocation granularity, the low 16 bits of an address are always 0.)You can then create a heap object in the allocated region using
RtlCreateHeapand allocate memory from this heap. (Note - this API is also available in user mode. Use ntdll[p].lib for linker input.)