I'm working on upgrading a .NET library to support 64-bit. That library performs various operation directly in memory of other processes on Windows. I have to choose between the two types IntPtr
(max positive value 7FFF'FFFF'FFFF'FFFF) or UIntPtr
(max positive value FFFF'FFFF'FFFF'FFFF) to handle my memory pointers. There is a lot of information on the Web about the two. IntPtr
seems to be the de facto agreed choice, as it is CLS-compliant and most of .NET API rely on that (ref Marshal
from InteropServices
).
I decided to open a 64-bit process and inspect the allocated memory regions, as well as the loaded modules in the process to see if it would be valuable to support unsigned pointers using UIntPtr
(addresses > 7FFF'FFFF'FFFF'FFFF). As illustrated in the screenshot below, it seems the memory addresses does not load symbols, nor allocate memory over 7FFF'FFFF'FFFF. Is there a specific reason for doing so ? May Windows allocate memory regions over that value in some cases ?
In Windows each process has only an address space of 8TB, therefore the upper limit for user code is 0x7FF'FFFF'FFFF
The range of virtual addresses that is available to a process is called the virtual address space for the process. Each user-mode process has its own private virtual address space. For a 32-bit process, the virtual address space is usually the 2-gigabyte range 0x00000000 through 0x7FFFFFFF. For a 64-bit process, the virtual address space is the 8-terabyte range 0x000'00000000 through 0x7FF'FFFFFFFF. A range of virtual addresses is sometimes called a range of virtual memory.
This diagram illustrates some of the key features of virtual address spaces.
https://docs.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/virtual-address-spaces
The upper 248TB belongs to kernel mode, summing up to 256TB of address space, which is addressed by 48 bits. That means the highest possible positive address is 247-1 = 0x7FFF'FFFF'FFFF
In 64-bit Windows, the theoretical amount of virtual address space is 2^64 bytes (16 exabytes), but only a small portion of the 16-exabyte range is actually used. The 8-terabyte range from 0x000'00000000 through 0x7FF'FFFFFFFF is used for user space, and portions of the 248-terabyte range from 0xFFFF0800'00000000 through 0xFFFFFFFF'FFFFFFFF are used for system space.
As commented below, in Windows 8.1 and Windows Server 2012 R2 or later the user/kernel address space split is 128/128TB which sums up to the same 256TB space
The significant part is 48-bit wide probably because most current x86-64 implementations use 48-bit virtual address
The original implementation of the AMD64 architecture implemented 40-bit physical addresses and so could address up to 1 TB (240 bytes) of RAM. Current implementations of the AMD64 architecture (starting from AMD 10h microarchitecture) extend this to 48-bit physical addresses and therefore can address up to 256 TB of RAM. The architecture permits extending this to 52 bits in the future (limited by the page table entry format); this would allow addressing of up to 4 PB of RAM.
https://en.wikipedia.org/wiki/X86-64#Architectural_features
Due to specifications on x64 you can depend on userspace pointers always fitting in IntPtr
. You cannot depend on a smaller space. The CPU could get more address lines in the future. When this happened between Windows 8 and Windows 8.1, no backwards compatibility flag was added.
In fact you got both positive and negative pointers in x86, but storing pointers in IntPtr
worked anyway because of the no-mans-land at 0x7FFF0000
and the null trap range at 0x00000000
.
I don't think you're trying to do tagged pointers, but if you are, the only acceptable way to do tagged pointers is the bottom two bits.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With