見出し画像

リロケータブルとバイナリローダ - 絶対アドレスの扱い

タイトルを見て何を言っているのかわからないのではないかとも思いましたが、どんな言語で作られたにせよ実行されるファイル(スクリプトはおいておいて)にはプログラムがマシン語で格納されており、そこに含まれている「アドレス」の取り扱いには結構、秘密があるんだよというお話です。

CPUがメモリのアドレスを指定する時には、何番地というアドレスをそのまま指定するのが基本です。もちろんプログラムカウンタを含むいろいろなレジスタの値に対して計算をしてアドレスを作ることもあるのですが、いずれにしてもプログラムの置かれているアドレスが変わってしまえば、そのままのアドレスは使えません。

ということでコンパイルなりアセンブルなりして出来上がったマシン語のバイナリは決まったアドレスに置いて使うことしか出来ません。これは意外と不便で、複数のバイナリを組み合わせて使おうとすれば、同じアドレスから始められないので作り直さなければいけませんし、それに搭載しているメモリ容量によっては、使おうと思っていたアドレスが実は存在しなかったりすることだってあります。

ですからバイナリを置くアドレスを変えても動作するように「相対アドレス」のみを使うようにして解決することもありました。このようなコードを「リロケータブル」と呼びます。とはいえ80系の元祖8080には相対アドレスの命令自身が存在しませんでしたし、Z-80や6801、6502には条件分岐に相対アドレスを使うことができるようになったものの、その飛び先は前後128バイト程度の範囲に限られ、どうしても必要な特別な場合に使われる程度でした(挿す場所によってアドレスが変わってしまう拡張スロット内のROMなど)。

ざっくりCPU~アドレス指定方式~

もちろん6809であればほぼ全ての命令に対して相対アドレスを使うことができるので、そのつもりになれば完全なリロケータブルなバイナリを作ることも可能です。そしてOS-9というOSに至っては、すべてのプログラムがリロケータブルであることを前提としていて、OSは必要に応じて好きなアドレスにバイナリを読み込んで実行することができました。まあ多少パフォーマンスが落ちるということもあるにはありましたが、メモリレイアウトを気にしないで良いことには多くのメリットがありました。とはいえこれは6809だけの話です。

次善の作として、出来上がったバイナリに対して、開始アドレスが変わった際に変更すべきアドレスの入っている部分を調べ上げて、例えば0H1000から始まるように作られたコードを0H2000からに変えたい時に、0H10だけ値を増やせば良いアドレスの一覧を作っておいて、バイナリを書き換えるプログラムを用意するようになりました。これで16Kメモリを搭載しているシステム用に作られたバイナリを32Kメモリで使いたい時に、もっと後ろの方に移動して走らせることが出来ます。

リロケータブルバイナリ

だいたいDISK-BASICな時代には、このようにして自分のシステムにあったバイナリを用意してテープなりディスクなりから読み込んでマシン語プログラムを動かしていたのですが、CP/MやMS-DOSといったOSが動く時代にはさすがにシステムごとのバイナリを用意するというのも大変すぎるので、いくつかの改善が図られました。

C言語などのコンパイラを使ったことがあれば、ご存知とは思いますが、ソースコードをコンパイルすると、まずオブジェクトファイルと呼ばれる中間ファイルが作られます。このファイルには使用するアドレスが「名前」の状態で格納されており、バイナリの部分とは別に名前の一覧表を持っています。そして複数のオブジェクトファイルをリンクして一つの実行ファイルのバイナリを作る際に、この「名前」のアドレスを確定させて、名前を使っている部分に実際のアドレスを埋め込んで行くのです。

オブジェクトファイル

ところが、この方法だとやはり絶対アドレスが固定されてしまい、プログラムを置く場所は決まったアドレスになってしまいます。

COMファイル

その後CPUが仮想アドレスを使うようになりプログラムごとに独自のアドレス空間を持つようになったので、アドレスが決まっていることは問題にはならなくなったのですが、その独自のアドレスを設定するための追加情報などがバイナリに付加されるようになりました。

EXEフォーマット

COFF

結局、プログラム自身がリロケータブルである必要性が無くなってしまったのですが、アドレスを指定する仕組みのほうが複雑怪奇になりました。最近のシステムではプログラムから見えるのはあくまで仮想アドレスなので物理アドレスにアクセスするには、かなり面倒なことになります。Linuxであれば/proc/pid/pagemap を眺めたり、physシステムコールと仲良くしなければなりません。まあ普通のコードであれば縁のない話なんですけどね。

ということで、プログラムの入ったバイナリが実際にメモリに読み込まれて実行されるまでには、それなりに面倒な処理が行われているわけなんです。

Portable Executable

Executable and Linkable Format

こんな話を知っていても、もうどこで役に立つのかわからないのですが、ダイナミックリンクライブラリや共有ライブラリを作ろうなんて言う時には、関連するリンクオプションを理解するのに、調べることがあるかもしれません。

ヘッダ画像は、いらすとや さんよりhttps://www.irasutoya.com/2014/02/blog-post_8737.html

#CPU #マシン語 #リロケータブル #アドレス指定 #絶対アドレス #相対アドレス #実行ファイル #リンク  

この記事が気に入ったらサポートをしてみませんか?