5

AMD64 準拠のアーキテクチャでは、アドレスは逆参照される前に正規の形式である必要があります。

Intel マニュアルのセクション 3.3.7.1から:

64 ビット モードでは、アドレス ビット 63 からマイクロアーキテクチャによって実装された最上位ビットまでがすべて 1 またはすべて 0 に設定されている場合、アドレスは標準形式であると見なされます。

現在、現在のオペレーティング システムとアーキテクチャで実装されている最も重要なビットは 47 番目のビットです。これにより、48 ビットのアドレス空間が残ります。

特にASLRが有効になっている場合、ユーザー プログラムは 47 番目のビットが設定されたアドレスを受信することを期待できます。

ポインターのタグ付けなどの最適化が使用され、上位ビットが情報を格納するために使用される場合、プログラムは、アドレスを逆参照する前に、47 番目のビットが何であれ、48 番目から 63 番目のビットが設定されていることを確認する必要があります。

ただし、次のコードを検討してください。

int main()
{
    int* intArray = new int[100];

    int* it = intArray;

    // Fill the array with any value.
    for (int i = 0; i < 100; i++)
    {
        *it = 20;
        it++;   
    }

    delete [] intArray;
    return 0;
}

ここで、次のように考えてintArrayください。

0000 0000 0000 0000 0 111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1100

に設定itして一度intArray増やし、 を考慮すると、次のようになります。itsizeof(int) == 4

0000 0000 0000 0000 1 000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

47 番目のビットは太字です。ここで何が起こるかというと、ポインター演算によって取得された 2 番目のポインターは、正規の形式ではないため無効です。正しいアドレスは次のとおりです。

1111 1111 1111 1111 1 000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

プログラムはこれにどのように対処しますか? アドレス範囲が 47 ビット目まで変化しないメモリが割り当てられないという OS の保証はありますか?

4

1 に答える 1

8

標準アドレス規則は、64 ビット仮想アドレス空間に巨大な穴があることを意味します。2^47-1 はその上の次の有効なアドレスと連続していないため、シングルには 64 ビット アドレスの使用できない範囲は含まれません。mmap

+----------+
| 2^64-1   |   0xffffffffffffffff
| ...      |
| 2^64-2^47|   0xffff800000000000
+----------+
|          |
| unusable |      not to scale: this part is 2^16 times as large
|          |
+----------+
| 2^47-1   |   0x00007fffffffffff
| ...      |
| 0        |   0x0000000000000000
+----------+

また、ほとんどのカーネルは、独自の使用のために正規範囲の上位半分を予約しています。例: x86-64 Linux のメモリ マップ。とにかく、ユーザー空間は連続した低い範囲でしか割り当てることができないため、ギャップの存在は関係ありません。

アドレス範囲が 47 ビット目まで変化しないメモリが割り当てられないという OS の保証はありますか?

ではない正確に。現在のハードウェアでサポートされている 48 ビットのアドレス空間は、実装の詳細です。canonical-address ルールにより、将来のシステムは下位互換性を大幅に損なうことなく、より多くの仮想アドレス ビットをサポートできるようになります。

せいぜい、OSがプロセスに上位ビットのメモリ領域をまったく同じに与えないようにするには、compatフラグが必要なだけです。( Linux のMAP_32BITmmap の現在のフラグ、またはプロセス全体の設定のように)。これにより、タグに上位ビットを使用し、手動で符号拡張をやり直したプログラムをサポートできます。

上位ビットのジャンクは現在エラーであるため、将来のハードウェアは、上位アドレス ビットを無視するかどうかを指定するフラグをサポートする必要はありません。 Intel の 5 レベル ページングは​​、さらに 9 つの仮想アドレス ビットを追加し、正規の上位半分と下位半分を広げます。 ホワイトペーパー

64 ビットでは、物理アドレス (52 ビット長) と比較して仮想アドレスが 4 ビット短い (48 ビット長) のはなぜですか?も参照してください。


豆知識: Linux のデフォルトでは、スタックは有効なアドレス範囲の下限にマッピングされます。(関連: Linux が 0x7f マッピングを好むのはなぜですか? )

$ gdb /bin/ls
...
(gdb) b _start
Function "_start" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (_start) pending.
(gdb) r
Starting program: /bin/ls

Breakpoint 1, 0x00007ffff7dd9cd0 in _start () from /lib64/ld-linux-x86-64.so.2
(gdb) p $rsp
$1 = (void *) 0x7fffffffd850
(gdb) exit

$ calc
2^47-1
              0x7fffffffffff

(最新の GDB はstarti、ブレークポイント コマンドをいじる代わりに、最初のユーザー空間命令が実行される前にブレークするために使用できます。)

于 2016-08-16T19:31:58.853 に答える