ロード、について
現代広く使われている ELF な実行フォーマットは ELF インタプリタから実行する形と
なっており、プログラムヘッダで LOAD な型のセグメントがメモリに展開された後に、ELF インタプリタ自身もメモリに展開されるようです (load_elf_interp() 手続きらしい)。そもそもこれ自体の実行が kickoff されるまでに
というあたりも結構な道のりな上にその ELF インタプリタは
- 共有ライブラリをロード
- リロケーション
で諸々の後回しなソレを解決して_元々の ELF バイナリでエントリアドレスに設定されているアドレスから処理が開始_とのこと。
さすがに仕組みが複雑なので大変です。
対して、KOZOS や UNIX v6 ではどうか、という事を確認してみます。
ただ、例えば KOZOS だとシリアル経由で取得する boot loader ですし、v6 UNIX は a.out なフォーマット限定です。逆に言えばこれらは単純明快であるがために、分かりやすい、と言えると思います。
KOZOS の場合
LOAD なセグメントをメモリに展開して ELF ヘッダのエントリポイントから実行を開始しています。ええと、load する手続きから
return (char *)header->entry_point;
なカンジで開始番地が戻されて jmp してます (正確には jmp ではないですが)。
f = (void (*)(void))entry_point; f();
これは非常に直接的な書き方というか分かりやすい書き方というか。
また、KOZOS ではプログラムヘッダの paddr な属性が使われています。こちらも特徴的と言えるのではないかと。つうか MMU 介していないので、という事で良いのかどうか。
memcpy((char *)phdr->physical_addr, (char *)header + phdr->offset, phdr->file_size); memset((char *)phdr->physical_addr + phdr->file_size, 0, phdr->memory_size - phdr->file_size);
上記で LOAD なセグメントをメモリに展開して行ってらっしゃい、な模様。繰り返しますが、これは boot loader の例です。KOZOS が起動してから後については別途確認の方向。
v6 UNIX の場合
こちらはマルチタスクになっているのでちょっと複雑になってます。命令なセグメントがメモリに展開されるのは exec 手続き (3020) から呼び出される xalloc 手続き (4433) で該当部分が以下。
4459 expand(USIZE+ts); 4460 estabur(0, ts, 0, 0); 4461 u.u_count = u.u_arg[1]; 4462 u.u_offset[1] = 020; 4463 u.u_base = 0; 4464 readi(ip);
0 番地に読み込んで、という形です。a.out ヘッダを飛び越えた位置を u.u_offset[1] で指定して u.u_base な番地に展開、という形のはず。
で、exec 手続きの出口あたりでレジスタの値をリセットしています。
3186 for(cp = ®loc[0]; cp < ®log[6];) 3187 u.u_ar0[*cp++] = 0; 3188 u.u_ar0[R7] = 0;
上記、R6 は sp のはずで、それ以外をゼロで初期化しているはず。R7 な ip もゼロになっているので割り込みから復帰して次にこのプロセスが動き始める折には 0 番地から実行が開始されるはず、ということなのか。
そういった意味では当り前ですがここで云々されてるアドレス (番地) は仮想のソレ、という理解で良いですよね。