茂木さんに教えてもらったことなんですが。
x64 のプログラムで、32bit アドレッシングをしていたり、うっかりポインタを 32bit にキャストした後に使ってしまったりすると、32bit の範囲を超えるポインタが渡ってきたときに、変なところにアクセスして死んでしまいます。(例)
普通、メモリは下位アドレスから割り当てられるため、プログラムを起動しても最初のうちは大丈夫だったりして実行時には多少見つけづらかったりします。しかし、この Microsoft のページに書いてある通り、レジストリの HKLM\System\CurrentControlSet\Control\Session Manager\Memory Management
キーの下の AllocationPreference
という DWORD 値を 0x100000 に設定すると、システム全体であらゆるメモリ割り当てが上位アドレス、具体的には 0x000007ffffffffff (8TB) から下位の方に向かって行われるようになり、上の問題を発見しやすくなります。
ここでポイントなのは「あらゆるメモリ割り当てが」という点です。VirtualAlloc() にはメモリを上位アドレスから割り当てる MEM_TOP_DOWN というオプションがあるのですが、他のメモリ割り当て関数、例えば malloc() や GlobalAlloc() や HeapAlloc() にはありません。上のレジストリ設定をすると、これらの関数であっても上位アドレスから割り当てられるようになります。さらには、スタック領域も上位アドレスからになります。
試しに以下のようなプログラムを作ってみます。
#include <stdio.h> #include <windows.h> #define MEMSIZE (16 * 1024 * 1024) int main(void) { int i; printf("symbols:\n"); printf("main = %p\n", main); printf("malloc = %p\n", malloc); printf("VirtualAlloc = %p\n", VirtualAlloc); printf("\n"); printf("memory allocations:\n"); printf("main thread stack = %p\n", &i); printf("malloc = %p\n", malloc(MEMSIZE)); printf("GlobalAlloc = %p\n", GlobalAlloc(GPTR, MEMSIZE)); printf("HeapAlloc = %p\n", HeapAlloc(GetProcessHeap(), 0, MEMSIZE)); printf("VirtualAlloc = %p\n", VirtualAlloc(NULL, MEMSIZE, MEM_RESERVE, PAGE_NOACCESS)); printf("VirtualAlloc (MTD) = %p\n", VirtualAlloc(NULL, MEMSIZE, MEM_RESERVE | MEM_TOP_DOWN, PAGE_NOACCESS)); return 0; }
これを普通の状態の Windows 7 で実行すると、例えば以下のような出力になります。
symbols: main = 0000000140001000 malloc = 0000000140001130 VirtualAlloc = 0000000076BE67A0 memory allocations: main thread stack = 000000000012FEF0 malloc = 00000000004C0040 GlobalAlloc = 00000000014D0040 HeapAlloc = 00000000024E0040 VirtualAlloc = 00000000034F0000 VirtualAlloc (MTD) = 000007FFFEFB0000
上のレジストリ設定を行った Windows 7 で実行すると、例えば以下のような出力になります。
symbols: main = 0000000140001000 malloc = 0000000140001130 VirtualAlloc = 00000000774E67A0 memory allocations: main thread stack = 000007FFFFF8FEF0 malloc = 000007FFFEBE0040 GlobalAlloc = 000007FFFDBD0040 HeapAlloc = 000007FFFCBC0040 VirtualAlloc = 000007FFFBBC0000 VirtualAlloc (MTD) = 000007FFFABC0000
残念ながら、DLL の配置等は上位アドレスからにはならないようなので、関数ポインタに関しては効果はありません(通常通り)。
注意点としては「システム全体で」効果があるため、出来の悪いソフトウェアがあると通常の使用に支障が出ることがある点です。茂木さんは某社のプリンタのドライバに問題があってクラッシュするようになったと言っていました。
no comment untill now