Fedora 17に含まれる実行可能な共有ライブラリ - Okiraku Programming の続きです。
さて、前記事で説明した glibc のような実行可能なshared libraryを作るにはどうすれば良いか? が気になるところですね?
基本的にはentry pointを指定して共有ライブラリを作成すればOKです。
が、ちょっと罠があって、普通に共有ライブラリを作ってしまうと、プログラムヘッダ中に存在するインタプリタ情報(INTERP)が設定されないため、他のライブラリといっさいリンクされない状態で実行が開始されてしまい、printf などのライブラリ関数が全く呼べない状態になってしまいます。これでもアセンブリで直接システムコール呼び出しすれば、メッセージ表示などもできないことはないのですが…(もちろんglibcはそうしてます)。
test.c
#include <stdio.h> #include <stdlib.h> void msg() { puts("libtest-0.1"); exit(0); }
% gcc -shared -fPIC -e msg test.c -o libtest.so # -e msg でエントリポイントを指定 % ./libtest.so zsh: segmentation fault ./libtest.so
↑落ちた… (gdbでトレースしてみると、puts@pltを解決しようとして、未リンクの場所に飛んでしまっています。)
% readelf -l libtest.so Elf file type is DYN (Shared object file) Entry point 0x6b0 There are 6 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000764 0x0000000000000764 R E 200000 LOAD 0x0000000000000768 0x0000000000200768 0x0000000000200768 0x0000000000000238 0x0000000000000240 RW 200000 DYNAMIC 0x0000000000000788 0x0000000000200788 0x0000000000200788 0x00000000000001c0 0x00000000000001c0 RW 8 NOTE 0x0000000000000190 0x0000000000000190 0x0000000000000190 0x0000000000000024 0x0000000000000024 R 4 GNU_EH_FRAME 0x00000000000006e4 0x00000000000006e4 0x00000000000006e4 0x000000000000001c 0x000000000000001c R 4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 8 ...
↑インタプリタ情報(INTERP)がない。
それならと、ld に --dynamic-linker=/lib64/ld-linux*.so でも渡せば良いのかと思いきや、-shared指定があるとこれでもうまくいきません。
仕方ないので、ライブラリのソース中に .interp セクションを指定してINTERPを作らせるようにします。(→参考: http://gcc.gnu.org/ml/gcc-help/2003-07/msg00232.html )
test.c
#include <stdio.h> #include <stdlib.h> const char interp[] __attribute__((section(".interp"))) = "/lib64/ld-linux-x86-64.so.2"; void msg() { puts("libtest-0.1"); exit(0); /* この関数を抜ける前に終了しないとSEGVで異常終了する */ }
% gcc -shared -fPIC -e msg test.c -o libtest.so # -e msg でエントリポイントを指定 % ./libtest.so libtest-0.1
↑実行できた! (もちろんshared libraryとして、他のプログラムから gcc -ltest 〜でリンクして関数呼び出しもできます。)
% readelf -l libtest.so Elf file type is DYN (Shared object file) Entry point 0x740 There are 8 program headers, starting at offset 64 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040 0x00000000000001c0 0x00000000000001c0 R E 8 INTERP 0x0000000000000780 0x0000000000000780 0x0000000000000780 0x000000000000001c 0x000000000000001c R 10 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x000000000000081c 0x000000000000081c R E 200000 LOAD 0x0000000000000820 0x0000000000200820 0x0000000000200820 0x0000000000000238 0x0000000000000240 RW 200000 DYNAMIC 0x0000000000000840 0x0000000000200840 0x0000000000200840 0x00000000000001c0 0x00000000000001c0 RW 8 NOTE 0x0000000000000200 0x0000000000000200 0x0000000000000200 0x0000000000000024 0x0000000000000024 R 4 GNU_EH_FRAME 0x000000000000079c 0x000000000000079c 0x000000000000079c 0x000000000000001c 0x000000000000001c R 4 GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 0x0000000000000000 RW 8 ...
今度はINTERPが指定されており、ちゃんとライブラリ呼び出しも含めて実行できいることが分かります。