私はELF仕様に取り組んでいます(http://www.skyfree.org/linux/references/ELF_Format.pdf)、プログラムのロードプロセスについて私が知らないことの1つは、スタックの初期化方法と初期ページサイズが何であるかです。テストは次のとおりです(Ubuntu x86-64)。
$ cat test.s
.text
.global _start
_start:
mov $0x3c,%eax
mov $0,%edi
syscall
$ as test.s -o test.o && ld test.o
$ gdb a.out -q
Reading symbols from a.out...(no debugging symbols found)...done.
(gdb) b _start
Breakpoint 1 at 0x400078
(gdb) run
Starting program: ~/a.out
Breakpoint 1, 0x0000000000400078 in _start ()
(gdb) print $sp
$1 = (void *) 0x7fffffffdf00
(gdb) info proc map
process 20062
Mapped address spaces:
Start Addr End Addr Size Offset objfile
0x400000 0x401000 0x1000 0x0 ~/a.out
0x7ffff7ffa000 0x7ffff7ffd000 0x3000 0x0 [vvar]
0x7ffff7ffd000 0x7ffff7fff000 0x2000 0x0 [vdso]
0x7ffffffde000 0x7ffffffff000 0x21000 0x0 [stack]
0xffffffffff600000 0xffffffffff601000 0x1000 0x0 [vsyscall]
ELF仕様では、このスタックページが最初にどのように、なぜ存在するかについてはほとんど説明していませんが、argv、envp、および上記の補助ベクトルを使用してargcを指すSPでスタックを初期化する必要があるといういくつかの参照を見つけることができます。私が確認しました。わかりました。ところで、SPの下にはどれだけの空きスペースが残っていますか?私のシステムにはSPの下にマップされたバイトがありますが、0x1FF00
おそらくこれはスタックの上部でカウントダウンされ、マップ全体にバイトが0x7ffffffff000
あります。0x21000
この数字にはどのような影響がありますか?
私はスタックの直下にあるページが「ガードページ」であることを知っています。巨大なスタックフレームがあると、ガードページとセグフォルトを超える可能性があるため、プロセスの開始時にどのくらいのスペースが正しく割り当てられているかを確認したいと思います。
編集する:データが多くなるほど何が起こっているのか分からない。テストは次のとおりです。
.text
.global _start
_start:
subq $0x7fe000,%rsp
movq $1,(%rsp)
mov $0x3c,%eax
mov $0,%edi
syscall
ここでは、何が起こるかを確認するために異なる定数値を使用しました0x7fe000
。どの値に対しても segfault が発生するかどうかは定義されません。 GDBによると、subq
命令自体はmmapのサイズを拡張しますが、これは私にとって不思議です(Linuxは私のレジスタに何があるのかをどうやって知ることができますか?)。ただし、プログラムは通常、何らかの理由で終了時にGDBと競合します。 GOTやPLT部分を使用しないため、ASLRは不確実性を引き起こすことはできません。実行可能ファイルは毎回仮想メモリの同じ場所にロードされます。もしそうなら、これは一種のPIDまたは物理メモリへのランダム侵入ですか?全体的に、私は実際にランダムアクセスに合法的に使用できるスタックの量とRSPを変更したり、「範囲外」の正当なメモリ領域に書き込むときにどれだけの量が必要かを混乱させます。
ベストアンサー1
私はこの質問が実際にELFに関連しているとは思わない。私が知る限り、ELFは「フラットパック「プログラムイメージはファイルに書き込まれ、最初の実行を準備するために再アセンブルされます。オペレーティングシステムの動作がPOSIXに昇格されていない場合、スタック定義とスタックの実装方法はCPU固有とオペレーティングシステムの間のどこかにあります。 ELF仕様がスタックに必要な事項にいくつかの要件を課すことは間違いありません。
最小スタック割り当て
あなたの質問から:
私はスタックの直下にあるページが「ガードページ」であることを知っています。巨大なスタックフレームがあると、ガードページとセグフォルトを超える可能性があるため、プロセスの開始時にどのくらいのスペースが正しく割り当てられているかを確認したいと思います。
これについての権威ある参考資料を見つけようとしています。しかし、私はこれが間違っていることを示唆するほど信頼できない参照を十分に見つけました。
私が知っている限り、ガードページは、「通常の」スタック増加ではなく、最大スタック割り当ての外部からのアクセスをキャプチャするために使用されます。物理メモリ割り当て(ページをメモリアドレスにマッピング)は、要求に応じて実行されます。つまり、メモリからマップされていないアドレス(スタックベースとスタックベース間 - max-stack-size + 1)にアクセスすると、CPUは例外をトリガーできますが、カーネルはページメモリをマッピングして例外を処理します。分割エラーがカスケードで発生します。
したがって、最大割り当て範囲内でスタックにアクセスすると分割エラーが発生しないようにする必要があります。あなたが見つけたように
最大スタック割り当て
研究文書は、スレッドの作成とイメージのロードに関するLinux文書に従う必要があります(フォーク(2)、クローン(2)、実行(2))。 execveのドキュメントいくつかの興味深いことが言及されました。
パラメータサイズと環境制限
...チラシ...
カーネル2.6.23以降では、ほとんどのアーキテクチャはソフトから派生したサイズ制限をサポートしています。RLIMIT_STACKリソース制限(参照制限を受ける(2))
...チラシ...
これは、その制限をサポートするためにアーキテクチャが必要であることを確認し、その制限も言及します(制限を受ける(2))。
RLIMIT_STACK
これはプロセススタックの最大サイズ(バイト単位)です。この制限に達すると、SIGSEGV信号が生成されます。この信号を処理するには、プロセスは代替信号スタック(sigaltstack(2))を使用する必要があります。
Linux 2.6.23以降、この制限はプロセスコマンドライン引数と環境変数が使用するスペースの量も決定します。詳細については execve(2) を参照してください。
RSP レジスタを変更してスタックを増やします。
私はx86アセンブラを知りません。 しかし、SSレジスタが変更されたときにx86 CPUによってトリガされる可能性がある「スタックエラー例外」に注意を払いたいと思います。 間違っていたら訂正してくださいが、x86-64 SS:SPでは「RSP」になったと思います。したがって、正しく理解した場合は、RSP()を減らしてスタックエラー例外を引き起こす可能性がありますsubq $0x7fe000,%rsp
。
222ページをご覧ください。https://xem.github.io/minix86/manual/intel-x86-and-64-manual-vol3/o_fe12b1e2a880e0ce.html