ソースファイルの分割

「EAXレジスタの値を10進数で出力する(前ページのprint_eax)」のような汎用的なサブルーチンは,いろいろなプログラムで使いたくなる。 しかし,print_eaxの中身をいろいろなプログラムにコピー & ペーストするのは,手間も掛かるし,同じもののコピーがたくさんできてディスク領域が無駄だ。さらに,print_eaxに修正の必要が生じると,それらのコピーをすべて修正しなければならない。

もっとよい方法は,print_eaxを独立したソースファイルに記述して,ほかのプログラムからそれを利用することだ。 以下は,プログラム本体を定義するソースファイル test_print.s と,print_eaxを定義するソースファイル print_eax.s の二つに分けてプログラムを記述した例だ。

test_print.s

        section .text
        global  _start
        extern  print_eax       ; 別ファイルのラベル
_start:
        mov     eax, 0xffff
        call    print_eax       ; eaxの値を10進数で出力
        mul     eax             ; eaxの2乗
        call    print_eax       ; eaxの値を10進数で出力

        mov     eax, 1
        mov     ebx, 0
        int     0x80            ; exit

print_eax.s

        ; eaxの値を10進数で標準出力に出力する.
        ; 汎用レジスタの値は保存される (破壊しない).

        section .text
        global  print_eax       ; 別ファイルから参照可能にする
print_eax:
        -- print_eaxの本体 --
        ret                     ; print_eaxの終端

        -- 以降, print_eaxのためのデータ領域の定義など --

アセンブル手順は以下のようになる。

$ nasm test_print.s             -- test_print.oを生成
$ nasm print_eax.s              -- print_eax.oを生成
$ ld test_print.o print_eax.o   -- 結合してa.outを生成
$ ./a.out
65535
4294836225

アセンブル結果であるオブジェクトファイル(.o ファイル)は,複数を結合 (link) することが可能だ。上記の例の場合,test_print.s にはprint_eaxの定義がなく,print_eax.s には実行開始点 (_start) がなく,どちらも単独では不完全だが,それぞれアセンブルしてオブジェクトファイルを生成することは支障なくできる。 これらのオブジェクトファイルをldコマンドに入力すると,.textセクション同士や.dataセクション同士が一つにまとめられ,test_print.o では未確定だったラベルの値(print_eaxの番地)が確定されて,完成した実行可能ファイルが出来上がる。

複数のソースファイルに分割してプログラムを記述する際のポイントは以下の2点だ。

  • 別ファイルで使うラベルは「global宣言」する。
  • 別ファイルのラベルを使う場合は「extern宣言」する。

例えば上記の例で,ラベル print_eax は print_eax.s でglobal宣言され,test_print.s でextern宣言されている。つまり,ラベルを提供する側でglobal宣言(「このラベルは皆で使うよ」),ラベルを使う側でextern宣言(「このラベルはこのファイルの外で定義されてるよ」)する。test_print.s 中のextern宣言がないと,アセンブル時に「print_eaxが未定義」というエラーになる。print_eax.s 中のglobal宣言がないと,リンク時に「print_eaxが未定義」というエラーになる。

results matching ""

    No results matching ""