(2003.11.30新規作成。) (2004.11.07 リンク切れを修正。) (2005.7.10 MinGWを追加。)
(2023-03) 全体的に現在の挙動に更新。libtool v2.4.7. このページのサンプルプログラムの置き場所; cpp-examples/libtool-samples at main · netsphere-labs/cpp-examples · GitHub
libtool は、共有オブジェクト (shared object; so、あるいは動的リンクライブラリ;DLL) を生成するための Makefile を、環境に依存せずに書くためのツール。
利用者 (プログラムの開発者) は、Makefile 上で、仮想的なファイル名でコンパイル、あるいはリンクするように書く。libtoolは、それらのファイルに対する操作を解釈して、実際のファイルに対するコンパイルオプションなどを生成し、実行させる。
こちらの方法のほうが通常。
configure.ac ファイルに LT_INIT を加え、各 Makefile.am ファイルに lib_LTLIBRARIES = libmydll.la のように書く。拡張子 .la が libtool 共有ライブラリ。
以降では、libtool を直接使う方法を解説する。
(2023-03) このセクション追加。(2024-08) 2024年8月時点でも状況同じ。このセクションのやり方で回復可能。
MinGW, gcc 12.2, libtool 2.4.7 の環境で, /usr/bin/libtool コマンドをそのまま使うと次のようなエラーが発生する。
libtool --tag=CXX --mode=link g++ -version-info 3:1:2 -no-undefined -rpath /tmp/hoge/lib -o libmydll.la mydll.lo
libtool: link: g++ -shared -nostdlib /usr/lib/gcc/x86_64-pc-msys/11.3.0/crtbeginS.o .libs/mydll.o -L/usr/lib/gcc/x86_64-pc-msys/11.3.0 -L/usr/lib/gcc/x86_64-pc-msys/11.3.0/../../../../x86_64-pc-msys/lib/../lib -L/usr/lib/gcc/x86_64-pc-msys/11.3.0/../../../../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-pc-msys/11.3.0/../../../../x86_64-pc-msys/lib -L/usr/lib/gcc/x86_64-pc-msys/11.3.0/../../.. -lstdc++ -lgcc_s -lgcc -lmsys-2.0 -ladvapi32 -lshell32 -luser32 -lkernel32 -lgcc_s -lgcc /usr/lib/gcc/x86_64-pc-msys/11.3.0/crtend.o -o .libs/msys-mydll-1.dll -Wl,--enable-auto-image-base -Xlinker --out-implib -Xlinker .libs/libmydll.dll.a
C:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: warning: cannot find entry symbol DllMainCRTStartup; not setting start address
C:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: .libs/mydll.o: in function `printf':
E:/hhori/repos/netsphere-labs/cpp-examples/libtool-samples/libtool-only/mydll.cpp:372: undefined reference to `__imp___acrt_iob_func'
C:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: E:/hhori/repos/netsphere-labs/cpp-examples/libtool-samples/libtool-only/mydll.cpp:372: undefined reference to `__mingw_vfprintf'
C:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: .libs/mydll.o: in function `Base::~Base()':
E:/hhori/repos/netsphere-labs/cpp-examples/libtool-samples/libtool-only/mydll.cpp:15: undefined reference to `operator delete(void*, unsigned long long)'
C:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: .libs/mydll.o: in function `C::~C()':
E:/hhori/repos/netsphere-labs/cpp-examples/libtool-samples/libtool-only/mydll.cpp:20: undefined reference to `operator delete(void*, unsigned long long)'
C:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: msys_mydll_1_dll_ertr000001.o:(.rdata+0x0): undefined reference to `_pei386_runtime_relocator'
collect2.exe: error: ld returned 1 exit status
make: *** [Makefile:26: libmydll.la] エラー 1
正しくは dllcrt2.o と crtbegin.o をリンクしなければならないが, crtbeginS.o をリンクしようとするため。
複雑な話ではない。libtool コマンドはシェルスクリプトで, 実行する環境に合わせて調整しなければならない。しかし MinGW のバイナリパッケージは単にファイルコピーでインストールするのだろう。predep_objects 変数がまずい。
Autoconf を使っている場合は, configure スクリプトが ltmain.sh ファイルを組み込んで $(top_builddir)/config.status を生成する。ビルドの際に config.status スクリプトがローカルに libtool スクリプトを生成し、以降はそれが使われる。そのため、グローバルにインストールされているほうに問題があっても発覚しにくい。
簡単なのは、libtool のsource tarball を取ってきてコンパイルし、生成された libtoolize と libtool で /usr/bin のを上書きしてしまう。msys 用に当たっているパッチが巻き戻るが, MinGW では実害なさそう。単に次でよい;
~/src/libtool_build$ ../libtool-2.4.7/configure --prefix=/usr
--disable-ltdl-install は指定しなくてよい (ltdl もインストールする)。MinGW のバイナリパッケージは libtool と libltdl パッケージに分かれている。
次のようにして、バイナリパッケージのファイル一覧を確認しておいてもよい。
$ pacman --query --list libtool
$ pacman --query --owns /usr/include/ltdl.h /usr/include/ltdl.h は libltdl 2.4.7-3 によって所有されています
libtoolは、--mode オプションで実行したい操作を区別する。modeによって、libtoolの動作はまったく異なる。
| --mode の値 | 意味 |
|---|---|
| compile | コンパイルして, libtool オブジェクトを生成. |
| link | リンク. ライブラリまたは実行ファイルの生成. |
| install | インストール |
| finish | libtool ライブラリのインストールを完了させる。 |
| uninstall | アンインストール |
| clean | クリーンアップ. 未インストールのライブラリまたは実行ファイルを削除する。 |
| execute | ライブラリをインストールせずに、ライブラリを使用するプログラムを実行する。 |
Cygwin は, configure.(ac|in) ファイルを見て, 開発版か安定版のいずれかのlibtoolを起動するようになっている。そのため、configure.ac ファイルがない場合は、libtool コマンドを絶対パスで記述する必要がある。
まずはライブラリにするためのソースファイルをコンパイルする。ここでの入力ファイル名は実在のものを使う。出力ファイル名は .lo にする。
$ ./libtool --tag=CXX --mode=compile g++ -c -Wall -Wextra -g -o mydll.lo mydll.cpp libtool: compile: g++ -c -Wall -Wextra -g mydll.cpp -DDLL_EXPORT -DPIC -o .libs/mydll.o
ディレクトリ .libs/ が生成され、必要であれば、本物の生成物はこのディレクトリに入れられる。libtoolは、自動的に適当なコンパイルオプションを追加し (-DPIC など)、コンパイルさせる。
カレントディレクトリに mydll.lo ファイルが生成される。Cygwinでは、mydll.lo はただのテキストファイル。MinGW, Linux でも同様。Linux だと次のようになる;
# mydll.lo - a libtool object file # Generated by libtool (GNU libtool) 2.4.7 # # Please DO NOT delete this file! # It is necessary for linking the library. # Name of the PIC object. pic_object='.libs/mydll.o' # Name of the non-PIC object non_pic_object=none
ライブラリを生成するのは、libtool --mode=link コマンド。リンクする入力 (オブジェクトファイル) は、*.o ではなく *.lo ファイルを指定する。
gcc の -o オプションで出力するライブラリのファイル名を指定する。出力ファイル名は lib で始まらなければならず、拡張子は .la とする。
動的リンクライブラリを出力するときは、libtoolのオプション -rpath を指定しなければならない。このオプションには、ライブラリの最終的なインストール先を指定する。
$ ./libtool --tag=CXX --mode=link g++ -version-info 3:1:2 -no-undefined -rpath /tmp/hoge/lib -o libmydll.la mydll.lo libtool: link: g++ -shared -nostdlib C:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../lib/dllcrt2.o C:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/crtbegin.o .libs/mydll.o -LC:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0 -LC:/git-sdk-64/mingw64/bin/../lib/gcc -LC:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/lib/../lib -LC:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../lib -LC:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-mingw32/lib -LC:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../.. -lstdc++ -lmingw32 -lgcc_s -lgcc -lmoldname -lmingwex -lmsvcrt -lkernel32 -lpthread -ladvapi32 -lshell32 -luser32 -lkernel32 -lmingw32 -lgcc_s -lgcc -lmoldname -lmingwex -lmsvcrt -lkernel32 C:/git-sdk-64/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/crtend.o -o .libs/libmydll-1.dll -Wl,--enable-auto-image-base -Xlinker --out-implib -Xlinker .libs/libmydll.dll.a libtool: link: ( cd ".libs" && rm -f "libmydll.la" && cp -pR "../libmydll.la" "libmydll.la" )
これで、libmydll.la が生成される。Cygwin / MinGWでは、これもテキストファイル。Linux でも同様。
Cygwinでは、libtoolのオプション -no-undefined を付けないとリンク時にエラーが発生する。このオプションは、出力ファイルが他のライブラリに依存していないことを宣言するもの。MinGW では, -no-undefined を付けないと, 静的ライブラリだけが生成される。なので、DLL を出力させる -rpath オプションだけがあると、エラーになる。
./libtool --tag=CXX --mode=link g++ -rpath /tmp/hoge/lib -o libmydll.la mydll.lo
libtool: error: can't build x86_64-w64-mingw32 shared library unless -no-undefined is specified
Linux では、-no-undefined オプションはなくてもいい (付けても問題ない)。ということで、いつでも付けるようにすればよい。
実行に成功すると, Cygwinでは、次のように *.dll ファイルと静的ライブラリ libmydll.dll.a が生成される。MinGW でも同様。
$ ls .libs/ cygmydll-0.dll libmydll.a libmydll.dll.a libmydll.la libmydll.lai mydll.o
libtool のオプション -export-symbols または -export-symbols-regex オプションで, エクスポートするシンボルを指定することもできる。付けなければ、すべてのシンボルがエクスポートされる。
(2005.7.10 この節追加。)
ライブラリをインストールするには、libtool --mode=install を使う。install または cp コマンドとそのオプションを続けて書く。コピー元は *.la ファイル、コピー先はディレクトリにする。
libtool --mode=install [ install | cp ] ...
Linuxでは、次のようになる。
$ libtool --mode=install cp libmydll.la /home/hori/src/test/share/simple/t cp .libs/libmydll.so.0.0.0 /home/hori/src/test/share/simple/t/libmydll.so.0.0.0 (cd /home/hori/src/test/share/simple/t && rm -f libmydll.so.0 && ln -s libmydll.so.0.0.0 libmydll.so.0) (cd /home/hori/src/test/share/simple/t && rm -f libmydll.so && ln -s libmydll.so.0.0.0 libmydll.so) cp .libs/libmydll.lai /home/hori/src/test/share/simple/t/libmydll.la cp .libs/libmydll.a /home/hori/src/test/share/simple/t/libmydll.a ranlib /home/hori/src/test/share/simple/t/libmydll.a chmod 644 /home/hori/src/test/share/simple/t/libmydll.a
オブジェクトファイルなどを一掃するには、libtool --mode=clean とする。
$ /usr/autotool/devel/bin/libtool --mode=clean rm libmydll.la main.lo \
main.o mydll.lo mydll.o testpg testpg.exe
rm libmydll.la .libs/libmydll.dll.a .libs/libmydll.a .libs/libmydll.la .libs/libmydll.lai
rm main.lo ./.libs/main.o ./main.o
rm main.o
rm: cannot remove `main.o': No such file or directory
rm mydll.lo ./.libs/mydll.o ./mydll.o
rm mydll.o
rm: cannot remove `mydll.o': No such file or directory
rm testpg .libs/testpg .libs/testpgS.o .libs/lt-testpg
rm: cannot unlink `.libs/testpg': No such file or directory
rm: cannot remove `.libs/testpgS.o': No such file or directory
rm: cannot remove `.libs/lt-testpg': No such file or directory
rm testpg.exe testpg
rm: cannot remove `testpg': No such file or directory
rmdir .libs
む、main.o、mydll.o、testpg は不要だったか。
メインプログラムをコンパイルする。これも libtool --mode=link を使う。
$ g++ -Wall -Wextra -g -c -o mydll_test.o mydll_test.cpp ./libtool --tag=CXX --mode=link g++ -o mydll_test.exe mydll_test.o libmydll.la libtool: link: g++ -o .libs/mydll_test.exe mydll_test.o ./.libs/libmydll.dll.a -L/tmp/hoge/lib
これで、カレントディレクトリと .libs/ に mydll_test.exe が生成される。
MinGW: ldd コマンドで確認すると, カレントディレクトリに生成されるほうは静的リンク、.libs/ に生成されるほうは動的リンクになっている。Linux: カレントディレクトリのほうはシェルスクリプト, .libs/ のほうが実体。
Makefile の書き方これらを踏まえて、Makefile を作成すると、次のようになる。libtool の場所と拡張子が環境依存になる。完全に差異を吸収するのはなかなか難しい。
Cygwin では LIBTOOL = /usr/autotool/devel/bin/libtool とする。
EXEEXT = .exe # mingw LIBTOOL = ./libtool # 共用ライブラリは拡張子 .la; 'lib' で始まること. TARGETS = libmydll.la mydll_test$(EXEEXT) CXXFLAGS = -Wall -Wextra -g CXXLD = g++ %.lo: %.cpp $(LIBTOOL) --tag=CXX $(LIBTOOLFLAGS) --mode=compile $(CXX) -c $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) $(CXXFLAGS) -o $@ $< all: $(TARGETS) mydll.lo: mydll.cpp mydll.h # -version-info, -no-undefined および -rpath は libtool のオプション. libmydll.la: mydll.lo $(LIBTOOL) --tag=CXX $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(LDFLAGS) $(LDLIBS) -version-info 3:1:2 -no-undefined -rpath /tmp/hoge/lib -o $@ $^ mydll_test$(EXEEXT): mydll_test.o libmydll.la $(LIBTOOL) --tag=CXX $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(LDFLAGS) $(LDLIBS) -o $@ $^ mydll_test.o: mydll_test.cpp mydll.h clean: $(LIBTOOL) --mode=clean rm -f $(TARGETS) *.o *.lo
(2003.12.03)
上記の例では、すべての関数をエクスポートしていた。特定の関数だけエクスポートする場合は、シンボル一覧ファイルを別に用意する。
次のファイルからライブラリを作り、このうち exp_func() だけエクスポートしてみる。
#include <stdio.h>
extern "C" {
void exp_func();
void no_exp_func();
}
void exp_func() {
printf("ok.\n");
}
void no_exp_func() {
printf("failed.\n");
}
次の内容のシンボルファイル dll2.sym を作る。エクスポートするシンボルを1行1シンボルにて記述する。シンボルの先頭に'_'などを付ける必要はない。このシンボル名は、CygwinでもLinuxでも同じものが使える。
exp_func
Makefile では、次のように書く。libtool の -export-symbols オプションでこのファイルを指定する。
libdll2.la: dll2.lo
$(LIBTOOL) --mode=link gcc -o $@ $^ -no-undefined -rpath /usr/local/lib \
-export-symbols dll2.sym
libtoolは、Cygwinの場合は内部で次のファイルを生成し、これを gcc に与えることで特定のシンボルだけエクスポートする。
EXPORTS exp_func