• 考虑翻译Qt官方blog中的RPATH and RUNPATH这篇文章,在继续之前,我需要先验证自己的理解是正确的,至少能自圆其说,能说服自己。

用例子说话

 

二进制

对应源码

有一个程序

a.out

main.c

需要加载插件A

libA.so

liba.c

A需要另一个动态库

libB.so

libB1.c 或 libB2.c

本文的关注点就是:到底是哪一个libB.so被加载

目录结构:

/home/debao/ttt/a.out
/home/debao/ttt/libA.so
/home/debao/ttt/libB.so
/usr/lib/libB.so

具体源码

  • main.c ==> ./a.out

#include <stdio.h>
#include <dlfcn.h>
typedef int (*funcA)(int, int);
int main()
{
    void * plugin = dlopen("./libA.so", RTLD_LAZY);
    funcA f = (funcA)dlsym(plugin, "funcA");
    printf("main: %d\n", f(3,4));
    return 0;
}
  • liba.c ==> ./libA.so

#include <stdio.h>
int funcB(int, int);
int funcA(int a, int b)
{
    printf("hello from funcA\n");
    return funcB(a, b);
}
  • libb1.c ==> ./libB.so

#include <stdio.h>
int funcB(int a, int b)
{
    printf("Hello from funcB 1\n");
    return a*b;
}  
  • libb2.c ==> /usr/lib/libB.so

#include <stdio.h>
int funcB(int a, int b)
{
    printf("Hello from funcB 2\n");
    return a*b;
}  

编译库文件

  • 编译动态库libB.so
$ gcc -shared -fPIC libb2.c -o libB2.so
$ sudo mv libB2.so /usr/lib/libB.so
$ gcc -shared -fPIC libb.c -o libB.so
  • 编译动态库libA.so
$ gcc -shared -fPIC liba.c -o libA.so -L. -lB

顺便看看该elf文件的头部信息:

$ readelf libA.so -d

Dynamic section at offset 0xf20 contains 21 entries:
  Tag        Type      Name/Value
 0x00000001 (NEEDED)   Shared library: [libB.so]
 0x00000001 (NEEDED)   Shared library: [libc.so.6]
...

恩,只有库的文件名信息,而没有路径信息。

编译程序

  • 第一次编译运行(什么路径都不加)
$ gcc main.c -ldl
$ ./a.out
hello from funcA
Hello from funcB 2
main: 12

程序:dlopen从当前目录找到libA.so,然后却在/usr/lib/中找到libB.so(没有使用当前目录的libB.so,这是我们需要的么?)

  • 第二次编译运行(使用DT_RPATH)
$ gcc main.c -ldl  -Wl,--rpath=.
$ ./a.out
hello from funcA
Hello from funcB 1
main: 12

恩,使用当前目录的libB.so,很理想的东西

  • 可是,由于DT_RPATH无法被环境变量LD_LIBRARY_PATH覆盖,不是不建议被使用,而是建议使用DT_RUNPATH么?

  • 第三次编译运行(使用DT_RUNPATH)
$ gcc main.c -ldl -Wl,--rpath=.,--enable-new-dtags
$ ./a.out
hello from funcA
Hello from funcB 2
main: 12

问题重新出现,使用的系统路径中的libB.so 而不是当前目录下的。

程序头部信息

通过下列命令可以查看:

$ readelf -d a.out

为了完整起见,列出前面3次编译的程序的信息:

  • 没有rpath和runpath
Dynamic section at offset 0xf20 contains 21 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libdl.so.2]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x8048360
...
  • 包含rpath
Dynamic section at offset 0xf18 contains 22 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libdl.so.2]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000f (RPATH)                      Library rpath: [.]
 0x0000000c (INIT)                       0x8048360
....
  • 包含rpath和runpath
Dynamic section at offset 0xf10 contains 23 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libdl.so.2]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000f (RPATH)                      Library rpath: [.]
 0x0000001d (RUNPATH)                    Library runpath: [.]

原因

RPATH and RUNPATH给出这个问题的答案:

Unless loading object has RUNPATH:
    RPATH of the loading object,
        then the RPATH of its loader (unless it has a RUNPATH), ...,
        until the end of the chain, which is either the executable
        or an object loaded by dlopen
    Unless executable has RUNPATH:
        RPATH of the executable
LD_LIBRARY_PATH
RUNPATH of the loading object
ld.so.cache
default dirs

用它解释第一个程序:

  • libA.so 没有RUNPATH,故而

    • 使用其RPATH (没有)
    • 递归查找其loader直到链条的顶端(可执行程序或被dlopen打开的对象)的RPATH或者遇RUNPATH退出 (没有命中)
    • 可执行程序没有RUNPATH,故而
      • 使用其RPATH (没有)
  • 环境变量LD_LIBRARY_PATH,(没有)
  • libA.so 的RUNPATH (没有)
  • ld.so.cache (没有命中)
  • 默认路径/usr/lib (命中)

用它解释第二个程序:

  • libA.so 没有RUNPATH,故而

    • 使用其RPATH (没有)
    • 递归查找其loader直到链条的顶端(可执行程序或被dlopen打开的对象)的RPATH或者遇RUNPATH退出 (没有命中)
    • 可执行程序没有RUNPATH,故而
      • 使用其RPATH (命中)

用它解释第三个程序:

  • libA.so 没有RUNPATH,故而

    • 使用其RPATH (没有)
    • 递归查找其loader直到链条的顶端(可执行程序或被dlopen打开的对象)的RPATH或者遇RUNPATH退出 (没有命中)
    • 可执行程序有RUNPATH,(继续前行)
  • 环境变量LD_LIBRARY_PATH,(没有)
  • libA.so 的RUNPATH (没有)
  • ld.so.cache (没有命中)
  • 默认路径/usr/lib (命中)

有意思的就是这个程序了,可执行程序的RUNPATH是一个重要的判断条件,却并不被做为这儿搜索路径!!

结束

本文是在kubuntu 11.10下编写测试的。为了尽可能简单,例子也都是认为制造的。而且我们看到,在使用RPATH的时候是正常的,RUNPATH一般来说,被推荐使用,但这儿它却不能正常工作。

所以,当使用RUNPATH时,我们需要明白:某些情况下可能需要设置环境变量 LD_LIBRARY_PATH

rpath和runpath的区别相关推荐

  1. linux动态库加载RPATH,RUNPATH

    引用自:http://gotowqj.iteye.com/blog/1926771 链接动态库 如何程序在连接时使用了共享库,就必须在运行的时候能够找到共享库的位置.linux的可执行程序在执行的时候 ...

  2. cmake详细教程(经验版)

    一.参考资料 CMake DSL语言 CMake 快速入门 cmake使用教程 CMake简明教程 CMake 入门实战 | HaHack Cmake入门和MindsporeLite Cmake文件分 ...

  3. Linux下动态链接库的查找问题

    Linux下动态链接库的查找问题 上一篇文章我们从 Linux C 编程的角度分析了一下 Linux 中的静态链接库和动态链接库的区别,这篇文章着重从 Linux 编译和运行的角度分析一下 Linux ...

  4. 【linux】程序找不到动态库.so的解决办法|查看.so动态库信息|.so动态库加载顺序

    目录 找不到.so解决方法 方法一:添加环境变量 方法二:复制so文件到lib路径 方法三:(推荐)添加ldconfig寻找路径 方法四:在编译目标代码时指定该程序的动态库搜索路径 让程序在本目录找到 ...

  5. glibc 知:ld.so

    文章目录 1. 名称 2. 概要 3. 描述 3.1. 动态字符串标记 4. 选项 5. 环境 5.1. 安全执行模式 5.2. 环境变量 5.2.1. LD_ASSUME_KERNEL(自 glib ...

  6. bazel 链接第三方动态库_C/C++编程知识:Linux 动态库相关知识整理

    动态库和静态库在C/C++开发中很常见,相比静态库直接被编译到可执行程序,动态库运行时加载使得可执行程序的体积更小,更新动态库可以不用重新编译可执行程序等诸多好处.作者是一个Linux后台开发,这些知 ...

  7. Linux系统和程序中的DEP和ASLR保护机制

    2014年,OpenSSL加密库中的一个缓冲区溢出漏洞被公开.该缺陷被称为"心脏出血".它使受欢迎的在线服务和软件平台的数亿用户暴露于易受攻击的OpenSSL软件版本中.于是操作系 ...

  8. Lib库使用学习笔记

    Lib库使用学习笔记 转自:http://blog.csdn.net/macky0668/article/details/6044867 技术前沿 2008-03-31 14:21:10 阅读177  ...

  9. Linux编译之(1)C语言基础

    Linux编译之C语言基础 Author:Once Day Date:2023年3月11日 漫漫长路,才刚刚开始- 1.概述 在Linux下开发多源文件的C代码文件,是一定要了解Makefile的,虽 ...

最新文章

  1. java qq ui界面,java UI之QQ登录
  2. 我的世界java版怎么装在u盘_我的世界选择器参数怎么使用?
  3. Flume fan out(扇出)详解
  4. 《SAP HANA平台应用开发》—第2章2.3节熟悉SAP HANA工作台
  5. c#队列取值_C# 队列
  6. Java获取系统时间
  7. android应用程序 多少钱_关于APP开发你最想了解的事,开发一个APP多少钱?
  8. ACM竞赛常用STL(二)之STL--algorithm
  9. 程序自删除方法大总结
  10. [Ext JS]Grid的列过滤
  11. 面试稳了!集齐几千名程序员精选的 100 道前端面试题!
  12. 30个经典机器学习项目,GitHub星星加起来超过16万丨资源
  13. hsrp+route-map 解决多路由器多isp
  14. python解析地址
  15. android 源代码 毛笔,Android-毛笔的探索与开发
  16. 邮箱确认html,html5+JavaScript进行邮箱地址验证
  17. day06--java高级编程:多线程,枚举类,注解,反射,网络通讯
  18. 蓝桥 区别质因数,因数
  19. 国外问卷调查到底能不能赚钱?
  20. (四) appium-desktop 脚本录制常用AW使用介绍

热门文章

  1. php 复选框 单选 全选,复选框全选/不全选,选择结果提交
  2. Keras TensorFlow教程:使用自己的数据集进行训练
  3. SpringBoot配置postgre多数据源(亲测有效!!!)
  4. 概率论—随机变量的数字特征、大数定律及中心极限定理
  5. LabVIEW彩色图像分割(基础篇—14)
  6. 力扣(LeetCode)刷题,简单题+中等题(第20期)
  7. GitHub开源的超逼真俄罗斯方块游戏
  8. 【面向对象编程】(2) 类属性的定义及使用;__repr__()方法
  9. 办公计算机培训方案,计算机办公软件应用培训教学计划规划方案.docx
  10. 张正友平面标定法的一些注意事项