一. libtool的工作原理
libtool 是一个通用库支持脚本,将使用动态库的复杂性隐藏在统一、可移植的接口中;使用libtool的标准方法,可以在不同平台上创建并调用动态库。可以认为libtool是gcc的一个抽象,其包装了gcc(或者其他的编译器),用户无需知道细节,只要告诉libtool需要编译哪些库即可,libtool将处理库的依赖等细节。libtool只与后缀名为lo、la的libtool文件打交道。
libtool主要的一个作用是在编译大型软件的过程中解决了库的依赖问题;将繁重的库依赖关系的维护工作承担下来,从而释放了程序员的人力资源。libtool提供统一的接口,隐藏了不同平台间库的名称的差异等细节,生成一个抽象的后缀名为la高层库libxx.la(其实是个文本文件),并将该库对其它库的依赖关系,都写在该la的文件中。该文件中的dependency_libs记录该库依赖的所有库(其中有些是以.la文件的形式加入的);libdir则指出了库的安装位置;library_names记录了共享库的名字;old_library记录了静态库的名字。
当编译过程到link阶段的时候,如果有下面的命令:
$libtool --mode=link gcc -o myprog -rpath /usr/lib –L/usr/lib –la
libtool会到/usr/lib路径下去寻找liba.la,然后从中读取实际的共享库的名字(library_names中记录了该名字,比如liba.so)和路径(lib_dir中记录了,比如libdir=’/usr/lib’),返回诸如/usr/lib/liba.so的参数给激发出的gcc命令行。
如果liba.so依赖于库/usr/lib/libb.so,则在liba.la中将会有dependency_libs=’-L/usr/lib -lb’或者dependency_libs=’/usr/lib/libb.la’的行,如果是前者,其将直接把“-L/usr/lib –lb”当作参数传给gcc命令行;如果是后者,libtool将从/usr/lib/libb.la中读取实际的libb.so的库名称和路径,然后组合成参数“/usr/lib/libb.so”传递给gcc命令行。
当要生成的文件是诸如libmylib.la的时候,比如:
$libtool --mode=link gcc -o libmylib.la -rpath /usr/lib –L/usr/lib –la
其依赖的库的搜索基本类似,只是在这个时候会根据相应的规则生成相应的共享库和静态库。
注意:libtool在链接的时候只会涉及到后缀名为la的libtool文件;实际的库文件名称和库安装路径以及依赖关系是从该文件中读取的。
2 为何使用 -Wl,--rpath-link -Wl,DIR?
使用libtool解决编译问题看上去没什么问题:库的名称、路径、依赖都得到了很好的解决。但下结论不要那么着急,一个显而易见的问题就是:并不是所有的库都是用libtool编译的。
libtool常见于autoconf/automake,单独用的例子很少。
二. libtool的使用
1.Creating object files
# libtool --mode=compile gcc -g -O -c foo.c
gcc -g -O -c foo.c -fPIC -DPIC -o.libs/foo.o
gcc -g -O -c foo.c -o foo.o >/dev/null2>&1
# libtool --mode=compile gcc -g -O -c hello.c
gcc -g -O -c hello.c -fPIC -DPIC -o.libs/hello.o
gcc -g -O -c hello.c -o hello.o>/dev/null 2>&1
【说明】libtool编译出两个版本的relocatableobject,一个是fPIC(位置无关的),放在.libs目录下;另一个则是普通的,放在本地。
2.linking shared library
# libtool --mode=link --tag=CC gcc -g -O -olibhello.la -rpath /usr/local/lib foo.lo
rm -fr .libs/libhello.a.libs/libhello.la .libs/libhello.lai .libs/libhello.so libs/libhello.so.0.libs/libhello.so.0.0.0
gcc -shared .libs/foo.o -Wl,-soname -Wl,libhello.so.0 -o .libs/libhello.so.0.0.0
(cd .libs && rm -f libhello.so.0&& ln -s libhello.so.0.0.0 libhello.so.0)
(cd .libs && rm -f libhello.so &&ln -s libhello.so.0.0.0 libhello.so)
ar cru .libs/libhello.a foo.o
ranlib .libs/libhello.a
creating libhello.la
(cd .libs && rm -f libhello.la&& ln -s ../libhello.la libhello.la)
【说明】link出两个共享库,一个是static,一个则是dynamic;需要注意的是,-rpath必须有才能产生dynamic库来,如果用-static,则只创建static库。
ranlib的作用:
On some older UNIX systems, ranlib added a tableof contents to archive libraries, which converted each archive to a form thatcould be linked more rapidly. This is no longer needed as the ar commandautomatically provides all the functionality ranlib used to provide.
在一些旧版本的系统上,ranlib负责把静态库转换为其他的某种格式,使得新的库能够更快的链接;现在ar命令已经包含了上述功能;
This command is provided as a convenience forsoftware developers who need to maintain Makefiles that are portable across avariety of operating systems.
为了兼容性,在makefile中还是保留ranlib
3.install shared library
libtool --mode=install cp libhello.la/usr/local/lib/libhello.la
libtool --mode=install install -c libhello.la/usr/local/lib/libhello.la
两个命令都可以,效果相同
4.linking executable file
# libtool --mode=link gcc -g -O -o hello hello.lo-rpath /usr/local/lib libhello.la
gcc -g -O -o .libs/hello .libs/hello.o ./.libs/libhello.so
creating hello
-rpath项负责添加运行时库路径,否则只能手工修改LD_LIBRARY_PATH环境变量了。
验证一下:
# ldd .libs/hello
linux-gate.so.1 => (0xffffe000)
libhello.so.0 => /usr/local/lib/libhello.so.0 (0x40019000)
libc.so.6 => /lib/tls/libc.so.6 (0x40031000)
/lib/ld-linux.so.2 (0x40000000)
5.install executablefile
#libtool --mode=install cp hello /usr/local/bin/hello
安装可执行程序。
6.运行
libtool --mode=execute hello
或直接运行hello
注意:此处hello已经安装在/usr/local/bin下了,可以用which hello来查看
【附】源码
foo.c
#include <stdio.h>
char msg[128]="Hello world";
void print()
{
printf("%s/n",msg);
}
hello.c:
#include <stdio.h>
extern char msg[128];
extern void print();
int main()
{
print();
}
Makefile:
LO_OBJS = foo.lo
PACKAGE_VERSION = 1:1:1
LIBDIR=/usr/local/lib
BINDIR=/usr/local/bin
all : hello
install : libhello.la hello
libtool --mode=install install -clibhello.la
${LIBDIR}/libhello.la
libtool --mode=install cp hello${BINDIR}/hello
uninstall : ${LIBDIR}/libhello.la ${BINDIR}/hello
libtool --mode=uninstall /bin/rm${LIBDIR}/libhello.la
libtool --mode=uninstall /bin/rm${BINDIR}/hello
hello : libhello.la hello.o
libtool --mode=install install -clibhello.la
${LIBDIR}/libhello.la
libtool --mode=link gcc -g -O -o hellohello.o -rpath ${LIBDIR} libhello.la
libhello.la : $(LO_OBJS)
libtool --mode=link --tag=CCgcc -g -O -o libhello.la
$(LO_OBJS) -rpath ${LIBDIR} ${PACKAGE_VERSION}
foo.lo : foo.c
libtool --mode=compile gcc -g -O -cfoo.c
hello.lo : hello.c
libtool --mode=compile gcc -g -O -chello.c
clean :
rm -f lib*.a *~ *core *.lo *.o *.la hello
rm -rf .libs
这样,用户可以用make编译,makeinstall/uninstall安装/卸载,makeclean清除编译临时文件,安装成功后,可以直接执行hello,不必指明路径也不必再另设环境变量LD_LIBRARY_PATH,非常方便!
lo: 使用libtool编译出的目标文件,其实就是在o文件中添加了一些信息
la: 使用libtool编译出的库文件,其实是个文本文件,记录同名动态库和静态库的相关信息
o: 编译的目标文件
a: 静态库,其实就是把若干o文件打了个包
so: 动态链接库(共享库)