下面的小节,简单介绍了libtool的使用方法。
使用下面的命令:
$ libtool --mode=compile gcc -g -O -c a.c
libtool: compile: gcc -g -O -c a.c -fPIC -DPIC -o .libs/a.o
libtool: compile: gcc -g -O -c a.c -o a.o >/dev/null 2>&1
libtool创建了下面的文件a.o .libs/a.o a.lo
$ cat a.lo
# a.lo - a libtool object file
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-11
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
# Name of the PIC object.
pic_object='.libs/a.o'
# Name of the non-PIC object
non_pic_object='a.o'
libtool会创建两个目标文件,正常的目标文件跟使用cc命令一样创建在当前目录。同时会创建一个position-independent code的目标文件,在.libs下面,用于链接共享库。
同时libtool会创建一个lo (libtool object)文件,描述了libtool的compile产物。
使用下面的命令:
$ libtool --mode=link gcc -g -O -o liba.la a.lo -rpath /usr/lib
libtool: link: rm -fr .libs/liba.a .libs/liba.la .libs/liba.lai .libs/liba.so .libs/liba.so.0 .libs/liba.so.0.0.0
libtool: link: gcc -shared -fPIC -DPIC .libs/a.o -g -O -Wl,-soname -Wl,liba.so.0 -o .libs/liba.so.0.0.0
libtool: link: (cd ".libs" && rm -f "liba.so.0" && ln -s "liba.so.0.0.0" "liba.so.0")
libtool: link: (cd ".libs" && rm -f "liba.so" && ln -s "liba.so.0.0.0" "liba.so")
libtool: link: ar cru .libs/liba.a a.o
ar: `u' modifier ignored since `D' is the default (see `U')
libtool: link: ranlib .libs/liba.a
libtool: link: ( cd ".libs" && rm -f "liba.la" && ln -s "../liba.la" "liba.la" )
注意,我们输入文件为a.lo,输出文件为liba.la。使用-rpath,指示创建共享库,且指定共享库的最终安装路径。
创建了liba.la,描述链接的成果。创建了静态库和动态库,动态库是标准的链接方式,带版本的。
$ tree -a
.
├── a.c
├── a.lo
├── a.o
├── liba.la
├── .libs
│ ├── a.o
│ ├── liba.a
│ ├── liba.la -> ../liba.la
│ ├── liba.lai
│ ├── liba.so -> liba.so.0.0.0
│ ├── liba.so.0 -> liba.so.0.0.0
│ └── liba.so.0.0.0
└── Makefile
liba.la的内容如下:
$ cat liba.la
# liba.la - a libtool library file
# Generated by libtool (GNU libtool) 2.4.6 Debian-2.4.6-11
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
# The name that we can dlopen(3).
dlname='liba.so.0'
# Names of this library.
library_names='liba.so.0.0.0 liba.so.0 liba.so'
# The name of the static archive.
old_library='liba.a'
# Linker flags that cannot go in dependency_libs.
inherited_linker_flags=''
# Libraries that this one depends upon.
dependency_libs=''
# Names of additional weak libraries provided by this library
weak_library_names=''
# Version information for liba.
current=0
age=0
revision=0
# Is this an already installed library?
installed=no
# Should we warn about portability when linking against -modules?
shouldnotlink=no
# Files to dlopen/dlpreopen
dlopen=''
dlpreopen=''
# Directory that this library needs to be installed in:
libdir='/usr/lib'
同时还创建了.libs/lib.lai,这个是安装la的安装版本,其不同只是installed为yes。在使用la链接时,libtool处理安装版本和未安装版本的链接路径不一样。
上面的共享库在目录a,这里在a的父目录有一个main.c,链接共享库a,命令如下
$ libtool --mode=link gcc -o test main.o a/liba.la
libtool: link: gcc -o .libs/test main.o a/.libs/liba.so
这里指定的是共享库a的未安装版本的la文件,链接是.libs下面的so。产生的文件如下:
.
├── .libs
│ ├── main.o
│ └── test
├── main.c
├── main.lo
├── main.o
├── Makefile
└── test
产生了test和.libs/test。.libs/test是真正的可执行elf文件,test是一个shell脚本,调用.libs/test。由于我们链接的是为安装版本的共享库,因为test提供了找到共享库的环境,.libs/test直接运行会提示找不到共享库。
$ ./test
10
$ .libs/test
.libs/test: error while loading shared libraries: liba.so.0: cannot open shared object file: No such file or directory
使用类型cp的参数:
$ sudo ibtool --mode=install cp liba.la /usr/lib
libtool: install: cp .libs/liba.so.0.0.0 /usr/lib/liba.so.0.0.0
libtool: install: (cd /usr/lib && { ln -s -f liba.so.0.0.0 liba.so.0 || { rm -f liba.so.0 && ln -s liba.so.0.0.0 liba.so.0; }; })
libtool: install: (cd /usr/lib && { ln -s -f liba.so.0.0.0 liba.so || { rm -f liba.so && ln -s liba.so.0.0.0 liba.so; }; })
libtool: install: cp .libs/liba.lai /usr/lib/liba.la
libtool: install: cp .libs/liba.a /usr/lib/liba.a
libtool: install: chmod 644 /usr/lib/liba.a
libtool: install: ranlib /usr/lib/liba.a
libtool: finish: PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin:/sbin" ldconfig -n /usr/lib
----------------------------------------------------------------------
Libraries have been installed in:
/usr/lib
If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the '-LLIBDIR'
flag during linking and do at least one of the following:
- add LIBDIR to the 'LD_LIBRARY_PATH' environment variable
during execution
- add LIBDIR to the 'LD_RUN_PATH' environment variable
during linking
- use the '-Wl,-rpath -Wl,LIBDIR' linker flag
- have your system administrator add LIBDIR to '/etc/ld.so.conf'
See any operating system documentation about shared libraries for
more information, such as the ld(1) and ld.so(8) manual pages.
----------------------------------------------------------------------
$ sudo libtool --mode=install install -c main /usr/bin/main
libtool: install: install -c .libs/main /usr/bin/main
安装后执行:
$ main
10
语法如下:
libtool [options] ... [mode-arg] ...
有下列选项
--config 显示配置选项
--debug,输出执行的shell脚本到标准输出。
-n, --dry-run,不实际执行,只显示要执行的命令。
--mode=mode,支持的模式有compile/link/install/finish/uninstall/clean/execute
mode-args,应该以编译器的名字开始,且要包含-c,表示只创建目标文件。
-o,制定输出lo
-prefer-pic,只尝试编译PIC目标
-prefer-no-pic,只尝试编译非PIC目标
-shared
-static
-Wc,flag
-Xcompiler flag 传递编译选项给编译器
链接生成库文件或者可执行文件。
-avoid-version,对于共享库,不产生版本信息。例如不创建符号链接。
-bindir,
-Llibdir,在已经安装的路径搜索需要的共享库。
-lname,输出文件需要已安装的共享库。
-module,创建一个可以被dlopen的共享库。
-rpath libdir,如果输出是一个共享库,那这个共享库将逐渐安装到这个目录,如果是个可执行文件,将这个路径加到可执行文件的运行时目录。
-R libdir,如果输出文件是一个可执行文件,讲libdir加到它的运行时路径,如果输出文件是一个库,将-Rlibdir添加到它的dependency_libs,以致于无论何时这个库被链接到可执行程序,libdir都被添加到运行时路径。
-inst-prefix-dir dir
安装到一个临时的staging area,而不是最终的prefix。这通常是automake的DESTDIR的行为,但是,la里面的libdir是最终的路径,而不是inst-prefix-dir。
当你编译的依赖另外一个共享库,而那个共享库安装到一个staging dir,当你用libtool链接,制定-Ldir -lname
时,它发现了dir/libname.la存在,解析出里面的libdir,它发现-Ldir的路径不是libdir,它就会告警xxx was moved,同时它就会按rpath的方式给你链接:
libtool --mode=link gcc -o main main.o -shared -L$(pwd)/a/staging -la
libtool: warning: library '/work/proj/libtool/a/staging/liba.la' was moved.
libtool: warning: library '/work/proj/libtool/a/staging/liba.la' was moved.
libtool: link: gcc -o main main.o -L/work/proj/libtool/a/staging /work/proj/libtool/a/staging/liba.so -Wl,-rpath -Wl,/work/proj/libtool/a/staging -Wl,-rpath -Wl,/work/proj/libtool/a/staging
然后发现elf中有rpath,
$ readelf -d staging/main
Dynamic section at offset 0x2da0 contains 29 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [liba.so.0]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [/work/proj/libtool/a/staging]
有网友也碰到了这个头疼的问题。
解决方法,是跳过libtool的框架,现成的libtool框架是不支持的。
1 直接指定so的路径,不要按-Lxxx -lname的方式。
$ libtool --mode=link gcc -o main main.o $(pwd)/a/staging/liba.so
libtool: link: gcc -o main main.o /work/proj/libtool/a/staging/liba.so
2 使用libtool的-Wl,flag接口
$ libtool --mode=link gcc -o main main.o -Wl,-L$(pwd)/a/staging/,-la
libtool: link: gcc -o main main.o -Wl,-L/work/proj/libtool/a/staging/ -Wl,-la