GCC 中 -L、-rpath和-rpath-link的区别

来源 http://blog.csdn.net/q1302182594/article/details/42102961

关于这3个参数的说明,有不少资料,但是看完了还是觉得模糊,分不清它们的区别。本文将用实验的方法去探讨这3个参数的区别。

1、三个.c文件

1.1 world.c

  1. #include<stdio.h>
  2. void world(void) {
  3. printf("world.\n");
  4. }

1.2 hello.c

  1. #include <stdio.h>
  2. void world(void);
  3. void hello(void) {
  4. printf("hello\n");
  5. world();
  6. }

1.3 test.c

  1. void main(void) {
  2. hello();
  3. }

2、生成动态库

参照《Linux静态库与动态库制作》,将hello.c和world.c分别生成动态库

  1. ubuntu $ gcc -c hello.c world.c
  2. ubuntu $ gcc -shared -o libhello.so hello.o
  3. ubuntu $ gcc -shared -o libworld.so world.o

这时,生成的文件及其依赖如下图:

由上图可见,libhello.so和libworld都依赖于linux-gate.so.1、libc.so.6以及/lib/ld-linux.so.2,并且这3个库的路径都以及硬编码进libhello.so和libworld.so中了(=>右边的部分)。

然而,虽然libhello.so中调用了libworld.so的函数,但是在上图中并没有显示出此关系。为了达到使libhello.so依赖于libworld.so的目的,在生成libhello.so时要链接到libworld.so:

  1. ubuntu $ gcc -shared -o libworld.so world.o -lhello -L .

此时,再使用ldd查看libhello.so的依赖:


    由上图可见,此时libhello.so已经依赖于libworld.so。

3、编译test.c

3.1 -L

由于test.c直接依赖于libhello.so,因此使用-lhello -L

  1. ubuntu $ gcc test.c -lhello -L .

结果如下图:

由上图可见已经在-L指定的路径找打了libhello.so,只是libhello.so还需要libworld.so。虽然它都在同一目录下,但是还是没有办法自动找到libworld.so。

那么,能不能使用-lworld将libworld.so也一并链接到test.c中呢?下面做一个尝试:

  1. ubuntu $ gcc test.c -lhello -lworld -L .

没有报错,成功生成a.out。

执行a.out并且使用ldd查看a.out的依赖:

由上图可见,虽然使用-lworld参数将libworld.so链接到了a.out中,但是上面只显示a.out依赖于libhello.so。由于找不到libhello.so(=> not found)的路径,因此需要设置环境变量LD_LIBRARY_PATH

  1. ubuntu export LD_LIBRARY_PATH=/home/liyihai/documents

再次执行a.out并使用ldd命令查看a.out的依赖库:

由上图可见,libhello.so已经通过LD_LIBRARY_PATH环境变量找到,并且libworld.so也出现在a.out的依赖中!

结论:-L指定的是链接时的库路径,生成的可执行文件在运行时库的路径由LD_LIBRARY_PATH环境变量指定。

3.2 -rpath

根据3.1第1张图的提示,由于libhello.so依赖于libworld.so,可以只用-rpath或者-rpath-link来指定。这里先使用-rpath。

先清空LD_LIBRARY_PATH环境变量,然后重新编译test.c并且带上-rpath参数:

  1. ubuntu $ export LD_LIBRARY_PATH=
  2. ubuntu $ gcc test.c -lhello -L . -Wl,-rpath .

执行a.out,并且使用ldd命令查看a.out的依赖:

由上图可见,虽然没有明确指出链接libworld.so,但是libworld.so还是出现在a.out的依赖中。

另外,虽然LD_LIBRARY_PATH已经清空,但是a.out还是可以执行,这说明库的路径已经被编译进a.out中了。需要注意的是,libhello.so和libworld.so的路径都是通过-rpath指定的路径找到的。

3.2.1 实验1

这时候,如果libhello.so和libworld.so的路径改变了,将会发生什么情况呢?下面做一个实验。

创建一个lib_tmp目录,然后将libhello.so和libworld.so移动进这个目录。

  1. ubuntu $ mdir lib_tmp
  2. ubuntu $ mv libhello.so lib_tmp/
  3. ubuntu $ mv libworld.so lib_tmp/

这时再执行a.out时,提示找不动态库,使用ldd命令查看a.out的库路径:

由上图红色圈部分可见,libhello.so的路径是not found的,并且libworld.so没有出现在其中。这和3.1的情况是相同的。

究其原因,就是要先找到libhello.so再去找libworl.so,因为是libhello.so依赖于libworld.so,而不是a.out依赖于libworld.so。

由此可见,使用了-rpath参数指定库的路径后,生成的可执行文件的依赖库路径并非就固定不变了。而是执行时先从-rpath指定的路径去找依赖库,如果找不到,还是会报not fund。

那么,这时候,LD_LIBRARY_PATH对a.out是否还有影响呢?下面将LD_LIBRARY_PATH设为当前libhello.so和libworld.so所在的路径

  1. ubuntu $ export LD_LIBRARY_PATH=./lib_tmp

再次执行a.out,并且使用ldd查看此时a.out的依赖库路径:

由上图可见LD_LIBRARY_PATH还是起作用的!由上图可见,和使用-rpath指定路径的效果是一样的。

3.2.2 实验2

将LD_LIBRARY_PATH清空,然后将libhello.so移动到lib_tmp中,而libworld.so则留在documents目录中。

执行a.out,并且使用ldd查看此时a.out的依赖库:

由上图可见,找不到libhello.so。这时,再指定LD_LIBRARY_PATH的路径为libhello.so所在的路径:

  1. ubuntu $ export LD_LIBRARY_PATH=./lib_tmp/

再次执行a.out,并且使用ldd查看其依赖库:

由上图可见,一切又恢复了正常。此时,libhello.so是通过LD_LIBRARY_PATH找到的,而libworld.so则是通过-rpath指定的路径找到的。

3.2.3 回顾

其实,经过测试,在3.1小节中,如果先指定LD_LIBRARY_PATH的值为libhello.so和libworld.so所在的路径,然后再编译test.c(执行3.1节的第1条编译命令),是可以成功编译的,并不会报3.1小节第1张图的那种错误。也就是说,LD_LIBRARY_PATH不仅指定可执行文件的库路径,还指定了库所依赖于其它库的路径。

3.2.4 结论

并非指定-rpath参数后,就抛弃LD_LIBRARY_PATH环境变量,只是多了个可选的依赖库路径而已。

3.3 -rpath-link

先将LD_LIBRARY_PATH的值清空,然后将libworld.so移动到lib_tmp目录中,而libhello.so则留在documents目录中,使用以下命令对test.c进行编译:

  1. ubuntu $ gcc test.c -lhello  -L . -Wl,-rpath-link ./lib_tmp

执行a.out并且使用ldd查看a.out的依赖库:

找不到 libhello.so,这在预料之中。下面指定LD_LIBRARY_PATH的值为libhello.so的路径,然后在执行a.out,并且查看a.out的依赖:

由上图可见,libhello.so已经通过LD_LIBRARY_PATH找到,但是libworld.so由于没有在LD_LIBRARY_PATH指定的路径中,而且编译时a.out又没有包含库的路径,因此找不到。这

对比3.2.2可以得出结论:-rpath和-rpath-link都可以在链接时指定库的路径;但是运行可执行文件时,-rpath-link指定的路径就不再有效(链接器没有将库的路径包含进可执行文件中),而-rpath指定的路径还有效(因为链接器已经将库的路径包含在可执行文件中了。)

最后,不管使用了-rpath还是-rpath-link,LD_LIBRARY_PATH还是有效的。

4、ld命令的man手册

4.1  -rpath=dir

  1. Add a directory to the runtime library search path.  This is used when linking an ELF executable with shared objects.
  2. All -rpath arguments are concatenated and passed to the runtime linker, which uses them to locate shared objects at
  3. runtime.  The -rpath option is also used when locating shared objects which are needed by shared objects explicitly
  4. included in the link; see the description of the -rpath-link option.  If -rpath is not used when linking an ELF
  5. executable, the contents of the environment variable "LD_RUN_PATH" will be used if it is defined.
  6. The -rpath option may also be used on SunOS.  By default, on SunOS, the linker will form a runtime search path out of
  7. all the -L options it is given.  If a -rpath option is used, the runtime search path will be formed exclusively using
  8. the -rpath options, ignoring the -L options.  This can be useful when using gcc, which adds many -L options which may
  9. be on NFS mounted file systems.
  10. For compatibility with other ELF linkers, if the -R option is followed by a directory name, rather than a file name, it
  11. is treated as the -rpath option.

4.2  -rpath-link=dir

  1. When using ELF or SunOS, one shared library may require another.  This happens when an "ld -shared" link includes a
  2. shared library as one of the input files.
  3. When the linker encounters such a dependency when doing a non-shared, non-relocatable link, it will automatically try
  4. to locate the required shared library and include it in the link, if it is not included explicitly.  In such a case,
  5. the -rpath-link option specifies the first set of directories to search.  The -rpath-link option may specify a sequence
  6. of directory names either by specifying a list of names separated by colons, or by appearing multiple times.
  7. This option should be used with caution as it overrides the search path that may have been hard compiled into a shared
  8. library. In such a case it is possible to use unintentionally a different search path than the runtime linker would do.

4.3  search paths

  1. The linker uses the following search paths to locate required shared libraries:
  2. 1.  Any directories specified by -rpath-link options.
  3. 2.  Any directories specified by -rpath options.  The difference between -rpath and -rpath-link is that directories
  4. specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is
  5. only effective at link time. Searching -rpath in this way is only supported by native linkers and cross linkers
  6. which have been configured with the --with-sysroot option.
  7. 3.  On an ELF system, for native linkers, if the -rpath and -rpath-link options were not used, search the contents of
  8. the environment variable "LD_RUN_PATH".
  9. 4.  On SunOS, if the -rpath option was not used, search any directories specified using -L options.
  10. 5.  For a native linker, search the contents of the environment variable "LD_LIBRARY_PATH".
  11. 6.  For a native ELF linker, the directories in "DT_RUNPATH" or "DT_RPATH" of a shared library are searched for shared
  12. libraries needed by it. The "DT_RPATH" entries are ignored if "DT_RUNPATH" entries exist.
  13. 7.  The default directories, normally /lib and /usr/lib.
  14. 8.  For a native linker on an ELF system, if the file /etc/ld.so.conf exists, the list of directories found in that
  15. file.

参考资料

[1]动态库的链接和链接选项-L,-rpath-link,-rpath

[2]ld的-rpath与-rpath-link选项

GCC 中 -L、-rpath和-rpath-link的区别的更多相关文章

  1. gcc中动态库和静态库的链接顺序

    so文件:动态库a文件: 静态库exe文件:可执行程序(linux下以文件属性来标示是否是可执行文件,与后缀名无关) 经过自己写的一些测试程序,大致了解了下gcc中链接顺序问题,总结出以下几点:1,动 ...

  2. GNU C/C++ __attributes__ GCC中的弱符号与强符号

    最近在看一些源代码,遇到了一些使用__attribute__修饰函数和变量的属性方面的代码,不是太了解,很是汗颜,再此做个总结:   GCC使用__attribute__关键字来描述函数,变量和数据类 ...

  3. gcc中关于静态库和动态库使用(转)

    转自:http://blog.chinaunix.net/uid-25871104-id-3069931.html 1,如何生成静态库 静态库只是一堆object对象的集合,使用ar命令可以将.o文件 ...

  4. gcc中的内嵌汇编语言(Intel i386平台)

    [转]http://bbs.chinaunix.net/thread-2149855-1-1.html 一.声明  虽然Linux的核心代码大部分是用C语言编写的,但是不可避免的其中还是有一部分是用汇 ...

  5. GCC 中零长数组与变长数组

    前两天看程序,发现在某个函数中有下面这段程序: int n; //define a variable n int array[n]; //define an array with length n 在 ...

  6. [转] GCC 中的编译器堆栈保护技术

    以堆栈溢出为代表的缓冲区溢出已成为最为普遍的安全漏洞.由此引发的安全问题比比皆是.早在 1988 年,美国康奈尔大学的计算机科学系研究生莫里斯 (Morris) 利用 UNIX fingered 程序 ...

  7. C++中L和_T()之区别(转)

    C++中L和_T()之区别 分类: C/C++2011-01-12 11:45 2878人阅读 评论(1) 收藏 举报 c++编译器apic 字符串前面加L表示该字符串是Unicode字符串._T是一 ...

  8. GCC 中的编译器堆栈保护技术

    GCC 中的编译器堆栈保护技术 前几天看到的觉得不错得博客于是转发了,但这里我补充一下一些点. GCC通过栈保护选项-fstack-protector-all编译时额外添加两个符号,__stack_c ...

  9. GCC中的内嵌汇编语言

    原文可参考:GCC中的内嵌汇编语言 一.声明   虽然Linux的核心代码大部分是用C语言编写的,但是不可避免的其中还是有一部分是用汇编语言写成的.有些汇编语言代码是直接写在汇编源程序中的,特别是Li ...

随机推荐

  1. map集合修改其中元素 去除Map集合中所有具有相同值的元素 Properties长久保存的流操作 两种用map记录单词或字母个数的方法

    package com.swift.lianxi; import java.util.HashMap; import java.util.Iterator; import java.util.Map; ...

  2. ASP.NET 使用 MySQL

    基本是通用的 C#与MySQL的交互, 先添加MySQL.Data.dll(位于MySQL安装目录下的Connector NET 8.0\Assemblies${version}目录下)引用, 之后代 ...

  3. 【UE4】二十四、UE4内部版本引擎和官方版本引擎版本保持兼容的方法

    内部使用的引擎和官方正式发布的引擎版本号不一致,这种情况会导致一些插件由于版本不一致无法使用,有其是在没有插件源码的情况下.解决方法为 修改Engine\Source\Runtime\Launch\R ...

  4. kafka生产者与消费者的生产消息与消费消息所遇到的问题

    当我们用API写kafka的时候 生产者生产消息,但是消费者接收不到消息?集群上启动消费者显示生产的消息.我们需要修改一下配置 (1)我们打开在虚拟机中修改kafka集群的配置文件 [root@spa ...

  5. oracle(sql)基础篇系列(一)——基础select语句、常用sql函数、组函数、分组函数

    花点时间整理下sql基础,温故而知新.文章的demo来自oracle自带的dept,emp,salgrade三张表.解锁scott用户,使用scott用户登录就可以看到自带的表. #使用oracle用 ...

  6. 剑指Offer - 九度1360 - 乐透之猜数游戏

    剑指Offer - 九度1360 - 乐透之猜数游戏2014-02-05 19:54 题目描述: 六一儿童节到了,YZ买了很多丰厚的礼品,准备奖励给JOBDU里辛劳的员工.为了增添一点趣味性,他还准备 ...

  7. 《Cracking the Coding Interview》——第3章:栈和队列——题目4

    2014-03-18 05:28 题目:你肯定听过汉诺威塔的故事:三个柱子和N个从小到大的盘子.既然每次你只能移动放在顶上的盘子,这不就是栈操作吗?所以,请用三个栈来模拟N级汉诺威塔的玩法.放心,N不 ...

  8. Linux之Permission denied没有权限

    在Linux上启动solr时,出现-bash: ./solr: Permission denied的问题. 最简单的解决方式: chmod 777 solr 傻瓜式直接赋予权限

  9. python-线程进程与队列

    线程,有时被称为轻量级进程,是程序执行流的最小单元线程是程序中一个单一的顺序控制流程.进程内一个相对独立的.可调度的执行单元,是系统独立调度和分派CPU的基本单位指进行中的程序的调度单位.在单个程序中 ...

  10. (原)Skeletal With DirectX12

    @author: 白袍小道 @来源: Advanced Animation with DirectX, 游戏引擎架构         (暗影不解释连招)     引言: 3D模型动画的基本原理是让模型 ...