ELF 実行エントリ ポイントの仮想アドレスがゼロ 0x0 ではなく、0x80xxxxx 形式であるのはなぜですか?

StackOverflow https://stackoverflow.com/questions/2187484

質問

実行すると、プログラムは仮想アドレス 0x80482c0 から実行を開始します。このアドレスは私たちを指すものではありません main() プロシージャですが、という名前のプロシージャに _start リンカーによって作成されます。

これまでのところ、Google で調べたところ、次のような (曖昧な) 歴史的推測が得られただけです。

カリフォルニア州サンタクルーズのグループによって公布された *NIX から i386 へのポートでは、0x08048000 がかつて STACK_TOP (つまり、スタックは 0x08048000 付近から 0 に向かって下向きに成長した) だったという伝説があります。当時は 128MB の RAM が高価で、4GB の RAM など考えられない時代でした。

誰かこれを肯定/否定できますか?

役に立ちましたか?

解決

Mads 氏が指摘したように、null ポインタを介したほとんどのアクセスを捕捉するために、Unix 系システムはアドレス 0 のページを「マップ解除」する傾向があります。したがって、アクセスするとすぐに CPU 例外、つまりセグメンテーション違反がトリガーされます。これは、アプリケーションを不正な状態にするよりもはるかに優れています。ただし、例外ベクタ テーブルは、少なくとも x86 プロセッサでは任意のアドレスに配置できます (そのための特殊レジスタがあり、 lidt オペコード)。

開始点アドレスは、メモリのレイアウト方法を記述する一連の規則の一部です。リンカーは、実行可能バイナリを生成するときに、これらの規則を認識している必要があるため、規則が変更される可能性は高くありません。基本的に、Linux の場合、メモリ レイアウト規則は 90 年代初頭の Linux の最初のバージョンから継承されています。プロセスは、次のいくつかの領域にアクセスできる必要があります。

  • コードは開始点を含む範囲内にある必要があります。
  • スタックがあるはずです。
  • ヒープが必要であり、制限は次のように増加します。 brk() そして sbrk() システムコール。
  • ある程度の余地があるはずです mmap() システムコール(共有ライブラリのロードを含む)。

現在、ヒープはどこにありますか? malloc() 行きます、によって支えられています mmap() カーネルが適切と判断したアドレスでメモリのチャンクを取得する呼び出し。しかし、昔の Linux は以前の Unix に似たシステムであり、そのヒープには中断のない 1 つのチャンクに大きな領域が必要であり、アドレスの増加に伴って拡大する可能性がありました。したがって、規約が何であれ、コードを詰め込んで低いアドレスに向かってスタックし、特定のポイント以降のアドレス空間のすべてのチャンクをヒープに与える必要がありました。

ただし、スタックもあり、通常は非常に小さいですが、場合によっては非常に劇的に増加する可能性があります。スタックは減少し、スタックがいっぱいになったとき、一部のデータを上書きするのではなく、プロセスが予測どおりにクラッシュするようにしたいと考えています。したがって、スタック用の広い領域が必要であり、その領域の下端にはマップされていないページが存在する必要がありました。そしてなんと!NULL ポインターの逆参照をキャッチするために、アドレス 0 にマップされていないページがあります。したがって、スタックは最初のページを除いて、アドレス空間の最初の 128 MB を取得することが定義されました。これは、コードが 128 MB の後の、0x080xxxxx のようなアドレスにある必要があることを意味します。

Michael 氏が指摘するように、実際に使用できるアドレス空間は非常に大きかったため、128 MB のアドレス空間が「失われる」ことは大したことではありませんでした。当時、Linux カーネルは単一プロセスのアドレス空間を 1 GB に制限しており、ハードウェアで許容される最大 4 GB を超えていましたが、それは大きな問題とは考えられませんでした。

他のヒント

なぜアドレス 0x0 から始めないのでしょうか?これには少なくとも 2 つの理由があります。

  • なぜなら、アドレス 0 は NULL ポインターとしてよく知られており、プログラミング言語でポインターを正常にチェックするために使用されるからです。そこでコードを実行する場合、アドレス値を使用することはできません。
  • アドレス 0 の実際の内容は、多くの場合 (常にではありませんが) 例外ベクタ テーブルであるため、非特権モードではアクセスできません。特定のアーキテクチャのドキュメントを参照してください。

エントリーポイントに関しては _startmain:C ランタイム (C 標準ライブラリ) に対してリンクすると、ライブラリは次の名前の関数をラップします。 main, 、したがって、前に環境を初期化できます main と呼ばれます。Linux では、次のとおりです。 argc そして 引数 アプリケーションへのパラメータ、 環境 変数、そしておそらくいくつかの同期プリミティブとロック。また、メインから戻ったときにステータス コードが渡されることを確認し、 _exit プロセスを終了する関数。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top