December 29th, 2023

How to allocate address space with a custom alignment or in a custom address region

Windows 10 version 1803 added the ability to request specific alignment and address region requirements when creating address space mappings either for virtual memory (Virtual­Alloc2) or file mappings (Map­View­Of­File3). These new functions let you pass a MEM_ADDRESS_REQUIREMENTS structure which can specify additional constraints:

  • Lowest­Starting­Address: The lowest address in the region.
  • Highest­Ending­Address: The highest address in the region.
  • Alignment: The desired alignment.

If any field is zero, then the system uses whatever value it normally would have. For example, if you say that the alignment is zero, then the memory will be aligned on the allocation granularity (for normal-sized pages) or the large page granularity (for large pages).

Here are some sample usages:

Scenario Lowest­Starting­Address Highest­Ending­Address Alignment
Align to multiple of N 0 0 N
Below 4GB 0 0x00000000`FFFFFFFF 0
Above 4GB 0x00000001`00000000 0 0
At 1MB boundary
between 2GB and 4GB
0x00000000`80000000 0x00000000`FFFFFFFF 0x00000000`00100000

Note that the Highest­Ending­Address is endpoint-inclusive. In other words, it is the highest address in the region, not the highest address plus one.

Here’s how you pass the MEM_ADDRESS_REQUIREMENTS:

MEM_ADDRESS_REQUIREMENTS requirements = {0};

requirements.LowestStartingAddress = ⟦ value ⟧;
requirements.HighestStartingAddress = ⟦ value ⟧;
requirements.Alignment = ⟦ value ⟧;

MEM_EXTENDED_PARAMETER param = {0};
param.Type = MemExtendedParameterAddressRequirements;
param.Pointer = &requirements;

auto result = VirtualAlloc2(nullptr, nullptr,
        size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE,
        &param, 1);

The Virtual­Alloc2 function takes a pointer to an array of MEM_EXTENDED_PARAMETER structures, but since we have only one, we just pass the address of a single object and tell the system that we have an array of only one element.

Note that you cannot combine non-default MEM_ADDRESS_REQUIREMENTS with an explicit Base­Address. If you pass an explicit base address, then you are saying, “I want the memory to be exactly here; I don’t want the system to choose for me.” Under those conditions, it’s meaningless to tell the system “And here is how I’d like you to choose the address for me.”

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

2 comments

Discussion is closed. Login to edit/delete existing comments.

  • Derp McDerperson

    I requested a function like this 2 decades ago. It's nice to see that Microsoft finally implemented it.

    They half-baked what I requested though:

    1. There should be a way to specify a "preceding allocation", i.e. lets say you want something aligned on 1 MiB boundary but want a 4 KiB block preceding it it.

    2. There should be a way to specify that the end address of the block be aligned as opposed to the begin address...

    Read more
  • Declan

    Hi Raymond, I had a question and I wasn't sure if there might be a better place to ask it.

    I know that you've commented before on some of the internal aspects of the global memory manager and how it validates pointers to functions like GlobalLock. I noticed though, that when you GlobalLock a pointer, it only increments the lock count until it reaches 255, but subsequent calls to GlobalLock still return a pointer to...

    Read more