ILD

libtool学习笔记
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2020-1-8 站点:Inside Linux Development

Using libtool

下面的小节,简单介绍了libtool的使用方法。

Creating object files

使用下面的命令:

$ 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产物。


Linking libraries

使用下面的命令:

$ 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处理安装版本和未安装版本的链接路径不一样。


Linking executables

上面的共享库在目录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


Installing libraries

使用类型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.
----------------------------------------------------------------------


Installing executables

$ sudo libtool --mode=install install -c main /usr/bin/main
libtool: install: install -c .libs/main /usr/bin/main


安装后执行:

$ main
10

Invoking libtool

语法如下:

libtool [options] ... [mode-arg] ...


有下列选项

--config 显示配置选项

--debug,输出执行的shell脚本到标准输出。

-n, --dry-run,不实际执行,只显示要执行的命令。

--mode=mode,支持的模式有compile/link/install/finish/uninstall/clean/execute


Compile mode

mode-args,应该以编译器的名字开始,且要包含-c,表示只创建目标文件。


-o,制定输出lo

-prefer-pic,只尝试编译PIC目标

-prefer-no-pic,只尝试编译非PIC目标

-shared

-static

-Wc,flag

-Xcompiler flag 传递编译选项给编译器


Link mode

链接生成库文件或者可执行文件。

-avoid-version,对于共享库,不产生版本信息。例如不创建符号链接。

-bindir,

-Llibdir,在已经安装的路径搜索需要的共享库。

-lname,输出文件需要已安装的共享库。

-module,创建一个可以被dlopen的共享库。

-rpath libdir,如果输出是一个共享库,那这个共享库将逐渐安装到这个目录,如果是个可执行文件,将这个路径加到可执行文件的运行时目录。

-R libdir,如果输出文件是一个可执行文件,讲libdir加到它的运行时路径,如果输出文件是一个库,将-Rlibdir添加到它的dependency_libs,以致于无论何时这个库被链接到可执行程序,libdir都被添加到运行时路径。


Install mode

-inst-prefix-dir dir

    安装到一个临时的staging area,而不是最终的prefix。这通常是automake的DESTDIR的行为,但是,la里面的libdir是最终的路径,而不是inst-prefix-dir。


Cross-compile problem with libtool

当你编译的依赖另外一个共享库,而那个共享库安装到一个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]


有网友也碰到了这个头疼的问题。

http://metastatic.org/text/libtool.html


解决方法,是跳过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


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.