main.s
コード:
.text
.globl main
main:
pushq %rbp
movq %rsp, %rbp
call sub_foo
movl $0, %eax
popq %rbp
ret
sub1.s
コード:
.data
.globl hoge
hoge:
.string "hoge"
.text
.globl sub_hoge
sub_hoge:
pushq %rbp
movq %rsp, %rbp
movl $hoge, %edi
call puts
nop
popq %rbp
ret
sub2.s
コード:
.data
.globl foo
foo:
.string "foo"
.globl bar
bar:
.string "bar"
.text
.globl sub_foo
sub_foo:
pushq %rbp
movq %rsp, %rbp
movl $foo, %edi
call puts
nop
popq %rbp
ret
.globl sub_bar
sub_bar:
pushq %rbp
movq %rsp, %rbp
movl $bar, %edi
call puts
nop
popq %rbp
ret
アセンブル
コード:
gcc -c main.s sub1.s sub2.s
オブジェクトファイル main.o, sub1.o, sub2.o ができた。
コード:
nm -n main.o sub1.o sub2.o
各ファイルのシンボルテーブルの内容は次のとおり。
コード:
main.o:
U sub_foo
0000000000000000 T main
sub1.o:
U puts
0000000000000000 D hoge
0000000000000000 T sub_hoge
sub2.o:
U puts
0000000000000000 D foo
0000000000000000 T sub_foo
0000000000000004 D bar
0000000000000011 T sub_bar
sub2.o には 2つのデータセグメントと 2つのテキスト セグメント(コード) が ある。
変数foo が 4バイトだから、変数bar のアドレスは 0004。
関数sub_foo が 11バイトだから、関数sub_bar のアドレスは 0011 となる。
次に、ライブラリ(アーカイブファイル)を作る。
コード:
ar r sub.lib sub1.o sub2.o
ライブラリ sub.lib ができた。
その中には、sub1.o と sub2.o が含まれる。
最後にリンクする。
実行ファイル a.out ができたので、実行する。
リンクで何が行われたか、わかっていますか?
main.o では、関数main を定義していますが、
呼びだそうとしている関数sub_foo は未定義(Undefined)です。
そこで、ライブラリ sub.lib の中から、シンボルsub_fooを含む
オブジェクトファイル sub2.o を取り出します。
そして、crt0.o crtn.o ... main.o sub2.o libc.dll をリンクします。
crt0.c などは main を呼び出すスタートアップコードです。
libc.dll は puts や、そこから呼び出されるたくさんの
関数やデータを提供し、それらがリンクされます。
sub_hoge はどこからも呼び出されないので、sub1.o は
リンクされません。
リンクというのは、未定義のシンボル(関数名や変数名)を結合するために、
オブジェクトファイルを結合するものです。
オブジェクトファイル内のシンボルは、データセグメントや
テキストセグメントの相対位置しか保持していません。
リンクが完了した時に、絶対アドレスが決定します。
nm -n a.out で確認してください。
「その際にライブラリと関数の保存されているアドレスがわかれば」とは
どういうことでしょうか?