2012-01-25 12 views
3

x86_64アーキテクチャのLinux 3.0でのプロセスには、64ビットの仮想アドレス空間があります。x86_64 Linux 3.0:無効なメモリアドレス

0は、NULLポインタを示すために、このアドレス空間で無効なメモリアドレス[以下の定義を参照]であることが保証されています。

その他の64ビット番号(存在する場合)は、決して有効なメモリアドレスではないことが保証されていますが、その理由は何ですか?

たとえば、1は有効な住所ですか? 2^64-1はどうですか?

: "は無効なメモリアドレスであることが保証されていますか?"とはどういう意味ですか?この質問の目的のために

void deref_and_assign(uint64_t i) 
{ 
    char* p = (char*) i; 
    *p = 42; 
} 

保証無効なメモリ参照が機能deref_and_assignは常にSIGSEGVを上げることを意味します。

+0

、なぜためSIGSEGV

同様に得られることを前提とすることができますあなたが探している情報ですか?おそらくコードに基づいて書くべき情報ではありません。研究のために、もちろん、何でも行く。;) –

+0

好奇心は良いです。私は移植性のない動的なタイプ(Linux/x86_64ターゲット用)を実装していますが、リード64ビット値「コードユニット」のオーバーロードについては私の選択肢を考慮しています。これを行う1つの方法は、コードユニットを(a)即値と(b)ポインタをより多くの情報に分割することです。簡単な例として、次のように動的な数値/文字列型を表すことができます。(0-4095)これは、整数0,1,2 ... 4095として直ちに表される数値です。 (4096-2^64)これはヌルで終了する文字列へのポインタです。実際の世界の例では、RubyのVALUE型がどのように動作するかを見てみましょう。 ruby.t.gz ruby​​.tgz –

答えて

4

ページ変換を有効にし、仮想アドレス0のメモリにアクセスできない場合(物理メモリが仮想アドレス空間にマップされるため)、1 ... 4095にアクセスできません。これらの4096のアドレスはすべて1ページのメモリに対応しており、全体として利用可能であるか、または利用できないためです。仮想アドレス0にメモリをマップしないことは良い考えです。それをマッピングしないと、多くのNULLポインタの逆参照をキャッチするのに役立ちます。ここのCPUは、マップされていない場所または現在実行中のコードよりも高い特権を必要とする場所にページフォールト(別名#PF)を生成します。

64ビットモードでは、CPUは64個より少ない仮想アドレスビットを実装でき、64ビットアドレスは実装されていないビットにすべてゼロまたはすべて1を入れる必要があります(値、0または1、最上位実装アドレスビットの値と同じでなければなりません。これらはすべてアドレス符号拡張として解釈できます)。そのようなアドレスは標準と呼ばれます。非正規アドレスを使用してメモリを読み書きしようとすると、一般的な保護違反(AKA #GP)が発生します。

したがって、OSによって(実際にはメモリのレイアウト上)、実際のCPUによって「無効な」メモリアドレスの範囲が出てくる可能性があります。ユーザモードアプリケーションからカーネルのメモリを読み書きしようとすると、#PFが得られます。マッピングされていないメモリ(たとえば、アドレス0〜4095)の読み書きを試みると、#PFが得られます。非正規アドレスでの読み書きをしようとすると、#GPが得られます。

これはあなたが探しているものですか?

+0

はい、0..4095は最初のページが存在しないことが明らかです。ありがとうございます。 48ビットモードについては不明です。あなたはそれを行うx86_64/Linux CPUの例を挙げることができますか?私のマシンでは、/ proc/*/mapsに、vsyscallがffffffffff600000-ffffffffff601000にマップされていることがわかります。これはほとんどvmの一番上です。また、どうやってユーザーランドのカーネルメモリから "試して"読み込むことができますか?すべての単語は、VMへの参照外のユーザーランドでメモリアドレスとして使用されます。 –

+0

ffffffffff600000は必ずしもトップではありません。私は既に、実装されていないトップのビットは、実装されたトップのビットのコピーであると述べました。したがって、48ビットしか実装されていない場合は、ffffff600000の部分だけが「有意義」で、残りの部分は上位16ビット(ffff)は1に等しい最重要「意味のある」ビットのコピーです。 –

+0

カーネルモードとユーザーモードの両方で好きなアドレスを使うことができます。しかし、メモリ保護メカニズムでは、実際にカーネルメモリをユーザーモードコードから読み書きすることはできません。 'rbx'に任意の値(アドレス)をロードし、' mov al、[rbx] 'を実行して' rbx'に含まれているアドレスのバイトの値を 'al'に読み込まないようにするものはありません。あなたが試すことができます。しかし、十分な特権がない場合、プロセッサはこの命令を実行しません。代わりに#GPまたは#PFが生成されます。これはOSが扱うもので、ここではアプリケーションを終了させるだけの意味があります。 –

0

Linuxプロセスで のセグメントが(void*)0から開始できないことを確認しました。

0 - 0xfff最初のページは決してmmap -edではありません(4Kbページのサイズはプロセッサとシステムに依存しますが、たいていは4kbです)。そして、あなたは、この最初のページ内(Linuxアプリケーション内から)ポインタを参照解除する0xffffffffffffffff(つまり2^64-1)で終わる最後のページ好奇心のうち

+0

mmap(0,4096、PROT_READ | PROT_WRITE、MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS、-1、0)は-1とEPERMを返しますので、間違ってはいけません。 –

+0

最初のページが 'mmap'ではないと仮定できると書いていたので、私はもっと正しいと思います。私は私の答えを編集しました。 –

関連する問題