libtool的使用 linux下的so、o、lo、a、la文件有什么区别

本文详细介绍了libtool工具的工作原理及其在解决库依赖问题方面的应用。解释了如何通过libtool简化跨平台动态库的创建与调用,并探讨了在特定编译环境下如何确保正确链接外部库。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

o: 编译的目标文件
a: 静态库,其实就是把若干o文件打了个包
so: 动态链接库(共享库)

lo: 使用libtool编译出的目标文件,其实就是在o文件中添加了一些信息
la: 使用libtool编译出的库文件,其实是个文本文件,记录同名动态库和静态库的相关信息
 
1 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 --mode=link gcc -o myprog -rpath /usr/lib –L/usr/lib –la

如果liba.so不是使用libtool工具生成的,则libtool此时根本找不到liba.la文件(不存在该文件)。这种情况下,libtool只会把“–L/usr/lib –la”当作参数传递给gcc命令行。

考虑以下情况:要从myprog.o文件编译生成myprog,其依赖于库liba.so(使用libtool生成),liba.so又依赖于libb.so(libb.so的生成不使用libtool),而且由于某种原因,a对b的依赖并没有写入到liba.la中,那么如果用以下命令编译:

$libtool --mode=link gcc -o myprog -rpath /usr/lib –L/usr/lib –la

激发出的gcc命令行类似于下面:

              gcc –o myprog /usr/lib/liba.so

由于liba.so依赖于libb.so(这种依赖可以用readelf读liba.so的ELF文件看到),而上面的命令行中,并没有出现libb.so,于是,可能会出现问题。

       说“可能”,是因为如果在本地编译的情况下,gcc在命令行中找不到一个库(比如上面的liba.so)依赖的其它库(比如libb.so),链接器会按照某种策略到某些路径下面去寻找需要的共享库:

1. 所有由'-rpath-link'选项指定的搜索路径.

2. 所有由'-rpath'指定的搜索路径. '-rpath'跟'-rpath_link'的不同之处在于,由'-rpath'指定的路径被包含在可执行文件中,并在运行时使用, 而'-rpath-link'选项仅仅在连接时起作用.

3. 在一个ELF系统中, 如果'-rpath'和'rpath-link'选项没有被使用, 会搜索环境变量'LD_RUN_PATH'的内容.它也只对本地连接器起作用.

4. 在SunOS上, '-rpath'选项不使用, 只搜索所有由'-L'指定的目录.

5. 对于一个本地连接器,环境变量'LD_LIBRARY_PATH'的内容被搜索.

6. 对于一个本地ELF连接器,共享库中的`DT_RUNPATH'和`DT_RPATH'操作符会被需要它的共享库搜索. 如果'DT_RUNPATH'存在了, 那'DT_RPATH'就会被忽略.

7. 缺省目录, 常规的,如'/lib'和'/usr/lib'.

8. 对于ELF系统上的本地连接器, 如果文件'/etc/ld.so.conf'存在, 这个文件中有的目录会被搜索.

从以上可以看出,在使用本地工具链进行本地编译情况下,只要库存在于某个位置,gcc总能通过如上策略找到需要的共享库。但在交叉编译下,上述八种策略,可以使用的仅仅有两个:-rpath-link,-rpath。这两个选项在上述八种策略当中优先级最高,当指定这两个选项时,如果链接需要的共享库找不到,链接器会优先到这两个选项指定的路径下去搜索需要的共享库。通过上面的描述可以看到:-rpath指定的路径将被写到可执行文件中;-rpath-link则不会;我们当然不希望交叉编译情况下使用的路径信息被写进最终的可执行文件,所以我们选择使用选项-rpath-link。

       gcc的选项“-Wl,--rpath-link –Wl,DIR”会把-rpath-link选项及路径信息传递给链接器。回到上面那个例子,如果命令行中没有出现libb.so,但gcc指定了“-Wl,--rpath-link –Wl,DIR”,则链接器找不到libb.so的时候,会首先到后面-rpath-link指定的路径去寻找其依赖的库。此处我们使用的编译命令的示例是使用unicore平台的工具链。

$ unicore32-linux-gcc –o myprog /usr/lib/liba.so /

-Wl,--rpath-link -Wl,/home/UNITY_float/install/usr/lib

这样,编译器会首先到“/home/UNITY_float/install/usr/lib”下面去搜索libb.so

       libtool如何把选项“-Wl,--rpath-link –Wl,DIR”传递给gcc?libtool中有一个变量“hardcode_libdir_flag_spec”,该变量本来是传递“-rpath”选项的,但我们可以修改它,添加我们需要的路径,传递给unicore32-linux-gcc。

       “hardcode_libdir_flag_spec”原来的定义如下:

hardcode_libdir_flag_spec="/${wl}--rpath /${wl}/$libdir"

我们修改后的定义如下:

hardcode_libdir_flag_spec="/${wl}—rpath-link /${wl}/$libdir /

-Wl,--rpath-link -Wl,/home/UNITY_float/install/usr/lib /

                    -Wl,--rpath-link -Wl,/home/UNITY_float/install/usr/X11R6/lib "

这样,当libtool在“--mode=link”的模式下,就会把选项“-Wl,--rpath-link –Wl,DIR”传递给gcc编译器了。


本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/fxzhang/archive/2010/03/01/5335823.aspx

sudo make install Making install in libfcgi make[1]: Entering directory '/home/yk/fcgi-2.4.1-SNAP-0910052249/libfcgi' make[2]: Entering directory '/home/yk/fcgi-2.4.1-SNAP-0910052249/libfcgi' test -z "/home/yk/fcgi/lib" || mkdir -p -- "/home/yk/fcgi/lib" /bin/bash ../libtool --mode=install /usr/bin/install -c 'libfcgi.la' '/home/yk/fcgi/lib/libfcgi.la' /usr/bin/install -c .libs/libfcgi.so.0.0.0 /home/yk/fcgi/lib/libfcgi.so.0.0.0 (cd /home/yk/fcgi/lib && { ln -s -f libfcgi.so.0.0.0 libfcgi.so.0 || { rm -f libfcgi.so.0 && ln -s libfcgi.so.0.0.0 libfcgi.so.0; }; }) (cd /home/yk/fcgi/lib && { ln -s -f libfcgi.so.0.0.0 libfcgi.so || { rm -f libfcgi.so && ln -s libfcgi.so.0.0.0 libfcgi.so; }; }) /usr/bin/install -c .libs/libfcgi.lai /home/yk/fcgi/lib/libfcgi.la /usr/bin/install -c .libs/libfcgi.a /home/yk/fcgi/lib/libfcgi.a chmod 644 /home/yk/fcgi/lib/libfcgi.a arm-xilinx-linux-gnueabi-ranlib /home/yk/fcgi/lib/libfcgi.a ../libtool: line 6556: arm-xilinx-linux-gnueabi-ranlib: command not found /bin/bash ../libtool --mode=install /usr/bin/install -c 'libfcgi++.la' '/home/yk/fcgi/lib/libfcgi++.la' libtool: install: warning: relinking `libfcgi++.la' (cd /home/yk/fcgi-2.4.1-SNAP-0910052249/libfcgi; /bin/bash ../libtool --tag=CXX --mode=relink arm-xilinx-linux-gnueabi-g++ -march=armv7-a -mthumb -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a9 --sysroot=/home/yk/sdk/sysroots/cortexa9t2hf-neon-xilinx-linux-gnueabi -O2 -pipe -g -feliminate-unused-debug-types -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed -o libfcgi++.la -lfcgi -rpath /home/yk/fcgi/lib fcgio.lo ) arm-xilinx-linux-gnueabi-g++ -march=armv7-a -mthumb -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a9 --sysroot=/home/yk/sdk/sysroots/cortexa9t2hf-neon-xilinx-linux-gnueabi -shared -nostdlib /home/yk/sdk/sysroots/cortexa9t2hf-neon-xilinx-linux-gnueabi/usr/lib/crti.o /home/yk/sdk/sysroots/cortexa9t2hf-neon-xilinx-linux-gnueabi/usr/lib/arm-xilinx-linux-gnueabi/8.2.0/crtbeginS.o .libs/fcgio.o -Wl,--rpath -Wl,/home/yk/fcgi/lib -L/home/yk/fcgi/lib -lfcgi -L/home/yk/sdk/sysroots/x86_64-petalinux-linux/usr/lib/arm-xilinx-linux-gnueabi/gcc/arm-xilinx-linux-gnueabi/8.2.0 -L/home/yk/sdk/sysroots/cortexa9t2hf-neon-xilinx-linux-gnueabi/lib -L/home/yk/sdk/sysroots/cortexa9t2hf-neon-xilinx-linux-gnueabi/usr/lib/arm-xilinx-linux-gnueabi/8.2.0 -L/home/yk/sdk/sysroots/cortexa9t2hf-neon-xilinx-linux-gnueabi/usr/lib -lstdc++ -lm -lc -lgcc_s /home/yk/sdk/sysroots/cortexa9t2hf-neon-xilinx-linux-gnueabi/usr/lib/arm-xilinx-linux-gnueabi/8.2.0/crtendS.o /home/yk/sdk/sysroots/cortexa9t2hf-neon-xilinx-linux-gnueabi/usr/lib/crtn.o -march=armv7-a -mthumb -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a9 -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed -Wl,-soname -Wl,libfcgi++.so.0 -o .libs/libfcgi++.so.0.0.0 ../libtool: line 4501: arm-xilinx-linux-gnueabi-g++: command not found libtool: install: error: relink `libfcgi++.la' with the above command before installing it Makefile:256: recipe for target 'install-libLTLIBRARIES' failed make[2]: *** [install-libLTLIBRARIES] Error 1 make[2]: Leaving directory '/home/yk/fcgi-2.4.1-SNAP-0910052249/libfcgi' Makefile:459: recipe for target 'install-am' failed make[1]: *** [install-am] Error 2 make[1]: Leaving directory '/home/yk/fcgi-2.4.1-SNAP-0910052249/libfcgi' Makefile:373: recipe for target 'install-recursive' failed make: *** [install-recursive] Error 1
07-21
<think>我们正在讨论的是文件类型:.la, .so, .a 文件。根据引用内容,我们可以总结如下: 1. **.so 文件**:共享对象(Shared Object)文件,即动态链接。在运行时被动态加载到内存中,多个程序可以共享同一个的实例,节省内存。但是,如果目标系统上没有该或版本不兼容,可能导致程序无法运行[^3]。 2. **.a 文件**:静态(Archive)文件,包含多个目标文件(.o)的归档。在编译时,静态的代码会被完整地复制到最终的可执行文件中,因此生成的可执行文件较大,但不需要依赖外部的文件。 3. **.la 文件**:Libtool 文件,是一个文本文件,用于描述的元数据。它由 GNU Libtool 生成,主要目的是解决跨平台的依赖问题,统一管理的构建和链接过程。.la 文件记录了的依赖关系(`dependency_libs`)、的安装路径(`libdir`)、共享的名字(`library_names`)和静态的名字(`old_library`)等信息[^2]。 ### 区别与联系 | 特性 | .so 文件 (动态) | .a 文件 (静态) | .la 文件 (Libtool 元数据文件) | |---------------|---------------------------------------|---------------------------------------|--------------------------------------| | **文件性质** | 二进制文件,可直接执行加载 | 二进制归档文件,包含多个 .o 文件 | 文本文件,包含的元数据 | | **链接方式** | 动态链接(运行时加载) | 静态链接(编译时复制到可执行文件) | 不直接参与链接,用于指导链接过程 | | **存在形式** | 独立文件,如 `libexample.so` | 独立文件,如 `libexample.a` | 文本文件,如 `libexample.la` | | **内容** | 编译后的机器码 | 多个目标文件(.o)的集合 | 元数据(依赖、路径、名等) | | **使用场景** | 运行时需要,支持多个程序共享 | 编译时嵌入,生成独立可执行文件 | 构建过程中辅助工具(如 Autotools)使用 | | **跨平台支持**| 不同系统扩展名不同(.so、.dylib 等) | 扩展名相对统一(.a) | 通过 Libtool 抽象统一处理不同平台 | | **依赖关系** | 运行时需确保存在且版本兼容 | 无运行时依赖 | 记录的依赖关系(其他 .la路径) | ### 联系 1. **构建过程的协作**: - 在 Autotools 项目中,Libtool 使用 `.la` 文件来生成 `.so` 或 `.a` 文件。例如,执行 `libtool --mode=link` 命令时,会根据 `.la` 文件中的信息(如 `dependency_libs`)自动添加必要的链接选项[^2]。 - `.la` 文件中的 `library_names` 字段指定了对应的共享(如 `libexample.so.0.1.0`),而 `old_library` 字段指定了静态(如 `libexample.a`)[^2]。 2. **依赖管理**: - 当项目 A 依赖项目 B 时,项目 B 生成的 `.la` 文件会记录其依赖的其他(如 `-lm` 表示数学)。在链接项目 A 时,Libtool 会读取项目 B 的 `.la` 文件,自动添加这些依赖,避免手动指定 `-L` 和 `-l` 标志的繁琐操作[^2]。 3. **跨平台抽象**: - Libtool 通过 `.la` 文件隐藏了不同平台的命名差异(如 Linux 的 `.so` 和 macOS 的 `.dylib`)。开发者只需使用统一的 `libexample.la` 作为目标,Libtool 会根据当前平台选择正确的实际文件[^2]。 ### 示例说明 假设有一个项目 `libfoo`,其构建过程如下: 1. Libtool 生成 `libfoo.la` 文件,其中包含: ```makefile # libfoo.la 片段 dependency_libs='-L/usr/local/lib -lbar' libdir='/usr/local/lib' library_names='libfoo.so.1.0.0' old_library='libfoo.a' ``` 2. 当编译一个使用 `libfoo` 的程序时,Libtool 会: - 根据 `libfoo.la` 中的 `dependency_libs` 自动添加 `-lbar` 依赖。 - 根据 `libdir` 自动添加 `-L/usr/local/lib` 路径。 - 根据当前链接模式(动态或静态)选择 `libfoo.so` 或 `libfoo.a`。 ### 注意事项 - 现代构建系统(如 CMake)通常不生成或依赖 `.la` 文件,而是通过自身的机制管理依赖和跨平台问题。 - 在软件打包(如 Debian)中,`.la` 文件有时会被移除,以避免潜在的依赖冲突[^2]。 §§相关问题§§ 1. 动态(.so)和静态(.a)在性能上有什么区别? 2. 如何手动创建或修改 .la 文件? 3. 为什么现代构建系统(如 CMake)逐渐弃用 .la 文件? 4. 在 Linux 系统中如何查看 .so 或 .a 文件的内容?</think>### .la 文件与 .so/.a 文件区别和联系 根据引用内容,结合文件的核心特性,总结如下: #### 一、本质区别 | 文件类型 | 文件性质 | 内容形式 | 主要作用 | |----------|------------------------|-------------------|----------------------------------| | **.la** | **元数据文件** | 纯文本文件 | 记录的依赖关系和配置信息[^2] | | **.so** | **动态链接** | 二进制可执行代码 | 运行时动态加载(共享内存)[^1] | | **.a** | **静态** | 多个 `.o` 的归档 | 编译时完整嵌入可执行文件[^1] | **关键区别**: - `.la` 是**描述性文件**(文本),而 `.so`/`.a` 是**实际代码**(二进制)。 - `.la` 不包含任何可执行代码,仅存储链接和依赖的元数据[^2]。 #### 二、核心联系 1. **依赖关系管理** - `.la` 文件通过 `dependency_libs` 字段明确记录依赖的 `.so` 或 `.a` 文件[^2]。 - 示例:一个 `libfoo.la` 可能包含: ```makefile dependency_libs=' -L/path -lbar.so -lbaz.a' # 显式声明依赖 library_names='libfoo.so.1.0.0' # 关联的动态 old_library='libfoo.a' # 关联的静态 ``` 2. **构建系统的桥梁** - Libtool 工具通过 `.la` 文件**统一抽象层**,隐藏平台差异: - Linux 动态:`.so` → 通过 `.la` 的 `library_names` 字段映射 - 静态:`.a` → 通过 `.la` 的 `old_library` 字段映射[^2] - 编译命令简化(无需手动指定路径): ```bash libtool --mode=link gcc -o app main.o libfoo.la # 自动解析依赖 ``` 3. **跨平台支持** - `.la` 文件使构建系统能自动适配不同平台: - Linux 生成 `.so` → 通过 `.la` 中的 `library_names` 指向 - macOS 生成 `.dylib` → 同一 `.la` 文件适配不同后缀[^2] #### 三、典型工作流程(以 Autotools 为例) ```mermaid graph LR A[源代码] --> B[Libtool] B --> C1[生成 libfoo.la] --> D1[记录依赖和路径] B --> C2[生成 libfoo.so/.a] D1 --> E[编译应用] --> F[通过 libfoo.la 自动链接 libfoo.so/libfoo.a] ``` #### 四、应用场景对比 | 场景 | .la 文件作用 | .so/.a 文件作用 | |----------------------|---------------------------------------|-------------------------------| | **编译链接** | 提供依赖路径和链接参数[^2] | 提供实际代码实现 | | **运行时** | 无作用(不参与执行) | .so 被动态加载到内存[^3] | | **软件打包** | 记录的元数据(Debian 等包管理)[^2] | 作为二进制包主体分发 | | **跨平台移植** | 屏蔽系统差异(.so/.dylib 等)[^2] | 需重新编译适配目标平台 | #### 五、注意事项 1. **现代构建系统的演进** - CMake/Meson 等工具通过内置机制替代 `.la` 的功能(如 `target_link_libraries`),逐渐减少对其依赖[^2]。 2. **文件冗余问题** - 删除时需同步移除 `.la` 文件,否则残留的元数据可能导致链接错误[^2]。 > 简单总结: > - **`.so` 是“可共享的代码”**, **`.a` 是“嵌入式的代码”**,而 **`.la` 是“说明书”**,指导如何正确使用它们[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值