9

RPATH与RUNPATH的区别

 3 years ago
source link: https://answerywj.com/2021/02/22/difference-between-RPATH-and-RUNPATH/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

RPATH与RUNPATH的区别

发表于 2021-02-22

| 分类于 编译链接

| 0

| 阅读次数 11次

字数统计: 1.6k 字

|

阅读时长 ≈ 8 分钟

本文从一个实际遇到的问题出发,分析 RPATHRUNPATH 的区别,以及产生的原因。


RPATH与RUNPATH的区别

年前升级了操作系统后,同样的代码在新系统编译后无法执行,提示找不到依赖库,本文用来记录一下是如何解决这个问题的。

main.c

#include "a.h"

int main() {
a();
return 0;
}

libA.so

// a.c
#include "b.h"

void a() {
b();
}


// a.h
void a();

ubuntu 16.04(gcc version 5.4.0)

$ gcc -fPIC -shared a.c -I. -L. -lB -o libA.so

libB.so

// b.c
#include <stdio.h>

void b() {
printf("Hello, World\n");
}


// b.h
void b();

ubuntu 16.04(gcc version 5.4.0)

$ gcc -fPIC -shared -I. b.c -o libB.so

函数调用依赖关系:main -> a -> b

  1. 分别在 ubuntu 16.04(gcc version 5.4.0)ubuntu 20.04(gcc version 9.3.0) 编译可执行程序:

    $ gcc -I. -o main main.c -Wl,--rpath,. -L. -lA -lB
  2. ubuntu 16.04(gcc version 5.4.0) 运行成功

    $ ./main
    Hello, World
  3. ubuntu 20.04(gcc version 9.3.0) 运行失败

    $ ./main 
    ./main: error while loading shared libraries: libB.so: cannot open shared object file: No such file or directory

错误提示为找不到可执行程序 ./main 依赖的共享库 libB.so

排除共享库本身问题

首先,由于 libA.solibB.so 是在 ubuntu 16.04(gcc version 5.4.0) 上编译的,所以重新在ubuntu 20.04(gcc version 9.3.0) 上编译 libA.solibB.so,并重复复现步骤,现象相同,说明不是 libA.solibB.so 导致的问题;

分析库查找过程

使用 LD_DEBUG,打开链接器的调试功能,分析共享库的查找过程:
ubuntu 16.04(gcc version 5.4.0)

$ LD_DEBUG=libs ./main                                                        [16:37:15]
22331: find library=libA.so [0]; searching
22331: search path=./tls/x86_64:./tls:./x86_64:. (RPATH from file ./main)
......
22331: trying file=./libA.so
......
22331: find library=libB.so [0]; searching
22331: search path=./tls/x86_64:./tls:./x86_64:. (RPATH from file ./main)
......
22331: trying file=./libB.so
......

ubuntu 20.04(gcc version 9.3.0)

$ LD_DEBUG=libs ./main
33218: find library=libA.so [0]; searching
33218: search path=./tls/haswell/x86_64:./tls/haswell:./tls/x86_64:./tls:./haswell/x86_64:./haswell:./x86_64:. (RUNPATH from file ./main)
......
33218: trying file=./libA.so
......
33218: find library=libB.so [0]; searching
33218: search cache=/etc/ld.so.cache
33218: search path=/lib/x86_64-linux-gnu/tls/haswell/x86_64:/lib/x86_64-linux-gnu/tls/haswell:/lib/x86_64-linux-gnu/tls/x86_64:/lib/x86_64-linux-gnu/tls:/lib/x86_64-linux-gnu/haswell/x86_64:/lib/x86_64-linux-gnu/haswell:/lib/x86_64-linux-gnu/x86_64:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu/tls/haswell/x86_64:/usr/lib/x86_64-linux-gnu/tls/haswell:/usr/lib/x86_64-linux-gnu/tls/x86_64:/usr/lib/x86_64-linux-gnu/tls:/usr/lib/x86_64-linux-gnu/haswell/x86_64:/usr/lib/x86_64-linux-gnu/haswell:/usr/lib/x86_64-linux-gnu/x86_64:/usr/lib/x86_64-linux-gnu:/lib/tls/haswell/x86_64:/lib/tls/haswell:/lib/tls/x86_64:/lib/tls:/lib/haswell/x86_64:/lib/haswell:/lib/x86_64:/lib:/usr/lib/tls/haswell/x86_64:/usr/lib/tls/haswell:/usr/lib/tls/x86_64:/usr/lib/tls:/usr/lib/haswell/x86_64:/usr/lib/haswell:/usr/lib/x86_64:/usr/lib (system search path)
......
./main: error while loading shared libraries: libB.so: cannot open shared object file: No such file or directory

结果在 ubuntu 20.04(gcc version 9.3.0) 中, 可以正确查找到 libA.so ,但是无法正确查找到 libB.so
libB.so 的查找路径是 system search path ,而非我们在编译时设定的查找时路径 ./ ,导致可执行程序无法加载 libB.so

动态链接器对共享库的查找顺序:

  1. LD_LIBRARY_PATH-L-rpath
  2. /etc/ld.so.cache
  3. 默认共享库目录:/usr/lib/lib

RPATH与RUNPATH的区别

由上分析可以得出是共享库运行时加载路径非法导致的问题,在排除设置 LD_LIBRARY_PATH 等环境变量的情况下,可以将焦点锁定在使用的链接选项 -rpath 上,查看源文件依赖:
ubuntu 16.04(gcc version 5.4.0)

$ readelf -d main
Dynamic section at offset 0xe08 contains 26 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libA.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000f (RPATH) Library rpath: [.]

$ readelf -d libA.so
Dynamic section at offset 0xe08 contains 25 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libB.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]

$ readelf -d libB.so
Dynamic section at offset 0xe18 contains 24 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]

ubuntu 20.04(gcc version 9.3.0)

$ readelf -d main 
Dynamic section at offset 0x2da8 contains 29 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libA.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000001d (RUNPATH) Library runpath: [.]

$ readelf -d libA.so
Dynamic section at offset 0xe08 contains 25 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libB.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]

$ readelf -d libB.so
Dynamic section at offset 0xe18 contains 24 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]

发现在 ubuntu 20.04(gcc version 9.3.0)RPATH 变成了 RUNPATH ,说明链接器选项 -rpath 的行为发生了改变。

源文件依赖关系:main -> libA.so -> libB.so

综上,问题的原因是 ubuntu 20.04(gcc version 9.3.0)上,链接器选项 -rpath 的行为发生改变,默认配置为 RUNPATH 而不是 RPATH;由于 RUNPATH 不适用于间接依赖的库,所以导致在 ubuntu 20.04(gcc version 9.3.0) 上只能正确查找到 libA.so ,而无法正确查找到 libB.so

gcc version >= 7.5.0时,-rpath默认行为即发生改变。

LD_LIBRARY_PATH(不推荐)

LD_LIBRARY_PATH 是一个环境变量,作用是临时改变链接器的加载路径,可以存储多个路径,用冒号分隔:

$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./libs

不推荐的原因:

  1. 若全局设置 LD_LIBRARY_PATH,会影响其它应用程序的共享库加载过程;
  2. 若只在该应用程序启动时局部设置 LD_LIBRARY_PATH,则每次启动都需要设置,步骤过于繁琐;

–disable-new-dtags

可以使用 -Wl,--disable-new-dtags 选项来使链接器保持旧行为,即在 ubuntu 20.04(gcc version 9.3.0) 使用如下命令编译:

$ gcc -I. -o main main.c -Wl,--disable-new-dtags,--rpath,. -L. -lA -lB

重新运行并查看依赖:

$ ./main 
Hello, World

$ readelf -d main
Dynamic section at offset 0x2da8 contains 29 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libA.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000f (RPATH) Library rpath: [.]

可执行程序 main 可以正确运行,RUNPATH 也变成了 RPATH,链接器行为与 ubuntu 16.04(gcc version 5.4.0) 保持一致了。

同理,也有 -Wl,--enable-new-dtags 选项来使链接器保持新行为

如下为官方解释:

--enable-new-dtags
--disable-new-dtags
This linker can create the new dynamic tags in ELF. But the older ELF systems may not understand them. If you specify --enable-new-dtags, the new dynamic tags will be created as needed and older dynamic tags will be omitted. If you specify --disable-new-dtags, no new dynamic tags will be created. By default, the new dynamic tags are not created. Note that those options are only available for ELF systems.



微信公众号同步更新,微信搜索"AnSwEr不是答案"或者扫描二维码,即可订阅。

Recommend

  • 128
    • 掘金 juejin.im 6 years ago
    • Cache

    volatile变量与普通变量的区别

    我们通常会用volatile实现一些需要线程安全的代码(也有很多人不敢用,因为不了解),但事实上volatile本身并不是线程安全的,相对于synchoronized,它有更多的使用局限性,只能限制在某些特定的场景。本篇文章的目的就是让大家对 volatile

  • 128

    本篇文章主要介绍Nosql的一些东西,以及Nosql中比较火的三个数据库Redis、Memchache、MongoDb和他们之间的区别。以下是本文章的阅读目录 1.Nosql简介 2.

  • 95

    runOnUiThread 、Handler.post、View.post 有什么区别?(解决篇) Original...

  • 110

    在APP设计中,有很多组建有着类似的功能和用法。如何正确使用这些组件?这些组件之间有什么区别?一起看看作者的解读。 在设计iOS版和Android版的APP过程中会涉及到很多组件。不同的场景使用的组件也不一样。这篇文章讲述六组常见的相似组件的区别和用法。 警示

  • 5
    • hkvision.cn 3 years ago
    • Cache

    g++中的rpath和runpath

    g++中的rpath和runpath2020年11月27日本文为原创文章,转载注明出处,欢迎关注网站https://hkvision.cn最近在弄Linux上的程序,然后由于涉及到要调用mkl的包(armadillo中用...

  • 3
    • blog.xizhibei.me 3 years ago
    • Cache

    RPATH 简介以及 CMake 中的处理

    RPATH 简介以及 CMake 中的处理 所谓的 RPATH,就是硬编码在可执行文件或者动态库中的一个或多个路径,被动态链接加载器用来搜索依赖库。1 这个值是存在可执行文件或者动态库的 ELF...

  • 4
    • matklad.github.io 2 years ago
    • Cache

    Rpath, or why lld doesn’t work on NixOS

    RPATH, or why lld doesn’t work on NixOS Mar 14, 2022 I’ve learned a thing I wish I didn’t know. As a revenge, I am going to write it down so that you, my dear reader, also learn about this. You probab...

  • 4
    • blog.theerrorlog.com 2 years ago
    • Cache

    GNU Linker 和 RPATH

    最近都在折腾 Python ,免不了要安装几个不同版本的执行环境。Python 的主程 序可以选择使用静态链接或者动态链接——默认是静态链接的;本来静态链接的 Python 也没什么问题,但是在别的程序里嵌入 Python 的时候用动态链接就会比 较方便。 我的系统里有...

  • 6
    • zzyongx.github.io 2 years ago
    • Cache

    chroot 环境中使用带 RPATH 的程序

    chroot 环境中使用带 RPATH 的程序 1 java 如何支持安装多个版本 众所周知,同一台机器可以安装多个java版本,但java依赖的库并不是版本化的放在ldconfig可以识别的目录中的,而是借助RPATH,在编译时指定加载目录实现的。验证...

  • 2

    How to fix "dyld: Library not loaded @rpath" error 27 Jul 2023 ⋅ 1 min read ⋅ Xcode

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK