Shared Object 入門
最近の gcc だとシンボル解決がうまくいかないといかないことがあったので、ちょっと調べました。
次のような 3つのソースファイルがあったとして、ダイナミックリンクで作ってみましょう。
main.c
int foo(void);
int main(void)
{
return foo() + 1;
}
foo.c
int bar(void);
int foo(void)
{
return bar() + 1;
}
bar.c
int bar(void)
{
return 0;
}
shared object を作るのに必要なのは、コンパイル時に -fPIC
オプションをつけることと、リンク時に -shared
オプションをつけることの 2点です。
$ gcc -fPIC -c -o bar.o bar.c
$ gcc -shared -o libbar.so bar.o
のように中間オブジェクト bar.o
を作ってもいいですし
$ gcc -shared -fPIC -o libbar.so bar.c
のように一度にやってもよいです。いずれにせよ libbar.so
が作られます。
では、最後までやってみます。
$ gcc -shared -fPIC -o libbar.so bar.c
$ gcc -shared -fPIC -o libfoo.so foo.c
$ gcc -L . -lfoo -lbar main.c
/tmp/ccqQv8uv.o: In function `main':
main.c:(.text+0x5): undefined reference to `foo'
collect2: ld returned 1 exit status
あれれ、 foo
が見つからないと言われました。。。
gcc のバージョンによっては、上記のやり方でもエラーなしにリンクできる場合もあるかもしれません。
gcc のバージョンが新しいとうまくいかないようです。
私の手元の gcc のバージョンは 4.6.3 です。
ちなみに、
$ gcc -L . main.c -lfoo -lbar
のようにすればうまくいきました。
関数が main()
-> foo()
-> bar()
のようにコールしているならば、
リンク時にも main.c -lfoo -lbar
の順に並べないと、リンク解決できないようです。
とは言うものの、リンク時に順番をいちいち考えるのも面倒です。
順番を気にせずうまくやってくれるためには、リンカーに、 -no-as-needed
を渡せばよいようです。
$ gcc -Wl,-no-as-needed -L. -lfoo -lbar main.c
でも
$ gcc -Wl,-no-as-needed -L. -lfoo main.c -lbar
でもうまくいきます。
実行してみます。
libfoo.so
と libbar.so
は検索パスに置いてないので、 LD_LIBRARY_PATH
を指定します。
$ LD_LIBRARY_PATH=$PWD ./a.out
$ echo $?
2
うまくいきました。
ちなみに、リンク時に -R
または -rpath
でライブラリ検索パスを指定しておけば、実行時に LD_LIBRARY_PATH
は指定しなくてもいいです。
$ gcc -Wl,-no-as-needed -Wl,-R$PWD -L . -lfoo -lbar main.c
または
$ gcc -Wl,-no-as-needed -Wl,-rpath $PWD -L . -lfoo -lbar main.c
まだよくない点があります。
関数 main()
から直接的にコールしているのは foo()
だけであって、 bar()
を直接的にコールしているわけではないです。
なので、関数 foo()
をコールするために
$ gcc -Wl,-no-as-needed -L. -lfoo -lbar main.c
のように -lfoo
も -lbar
もリンクしないといけないのは変な話です。
関数 foo()
は関数 bar()
をコールしているので、 libfoo.so
は libbar.so
に依存していることになります。
そこで、shared object 間の依存関係を考慮することにします。
libfoo.so
を作るときにも -Wl,-no-as-needed
を指定すればうまくいきます。
$ gcc -shared -fPIC -o libbar.so bar.c
$ gcc -shared -fPIC -Wl,-no-as-needed -L . -lbar -o libfoo.so foo.c
$ gcc -Wl,-no-as-needed -Wl,-rpath $PWD -Wl,-rpath-link $PWD -L . -lfoo main.c
最終行で、 -lbar
は不要になっています。 ただし、 -rpath-link
オプションがいります。
ldd
コマンドで依存関係を見ると、 libfoo.so は libbar.so を必要としています。
$ ldd libfoo.so
linux-vdso.so.1 => (0x00007fff1fbff000)
libbar.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5141d0d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f51422ea000)
ちなみに、shared object の勉強は 共有ライブラリーを解剖する というページがわかりやすかったです。
blog comments powered by Disqus