dylib 的 @rpath @executable_path、@loader_path、@rpath 运用

dylib 的 @rpath @executable_path、@loader_path、@rpath 运用

1 场景

程序通常不是孤立的,需要多个lib的加载,可是在程序的部署时 我们经常遇到各种lib找不到的问题,经常收到报错为 (**** Library not loaded ), 程序通常说在一堆指定的路径中都找不到指定的libs。这是很容易 出现的问题, 如何解决这样的问题,我们就从程序如何加载lib说起

2 lib的加载规则

2.1什么是dylib

dylib(dynamic library)是苹果动态函数库,在应用程序编译的时候, 不会编译进二进制目标代码中, 只有当程序里执行相应的函数才调用该函数库里对应的函数。
当应用程序启动的时候,有一个叫做动态连接器和加载器dyld会寻找,加载,连接动态库.

2.2它的加载规则是什么呢???

在编译程序时, 我们通常会指定需要加载的动态链接库 ,例如在QT中我们通常这样写

  • -L$$PWD/…/ExternalLibs/FFmpegLib/lib/ -lavcodec.59.32.101 -lavutil.57.25.100 -lswresample.4.6.100 -lswscale.6.6.100 -lavformat.59.24.100
    这样写的意思就是让程序运行时去找这几个库加载(avcodec.59.32.101、avutil.57.25.100,swresample.4.6.100,swscale.6.6.100)
  • 可是这几个库到底在哪呢??
    我们以 avcodec.59.32.101 为例 看看他都有啥信息 otool -l 可以查看库的信息
    dylib 的 @rpath @executable_path、@loader_path、@rpath 运用_第1张图片

dylib 的 @rpath @executable_path、@loader_path、@rpath 运用_第2张图片
这条 Load Command LC_ID_DYLIB 的name就是这个库的INSTALL_PATH, 在编译一个动态库的时候, 你需要指定 INSTALL_PATH. 也就是它的安装路径;编译完成后如果一个可执行程序使用了该动态库, 那么在编译可执行程序的时候, 动态库的 INSTALL_PATH 会被记录到可执行程序中, 用来定位这个动态库。
当其他程序要加载它时 这个INSTALL_PATH就是告诉其他程序去哪加载它, 被其他程序连接到后 这个name就记录到了其他程序的 LC_LOAD_DYLIB 字段中 。
下图为例 可以看到 其实这库还要加载 @rpath/libswresample.4.6.100.dylib 、@rpath/libavutil.57.25.100.dylib 这两个库, 其实这里的rpath/libswresample.4.6.100.dylib 就是libswresample.4.6.100.dylib这个库的INSTALL_PATH, 意思是 libswresample.4.6.100.dylib 告诉要加载我的程序 我在这:@rpath/libswresample.4.6.100.dylib
dylib 的 @rpath @executable_path、@loader_path、@rpath 运用_第3张图片

那么这里的 rpath 是什么??

@rpath。这个就是今天要介绍的重点,它是run path的缩写。本质上它不是一个明确的path,甚至可以说它不是一个path。它只是一个变量,或者叫占位符。这个变量通过XCode中的run path选项设置值,或者通过install_name_tool的-add_rpath设置值。设置好run path之后,所有的@rpath都会被替换掉。此外,run path是可以设置多个值的,这样看来就和Windows下的PATH变量差不多了

在程序发布时我们通常要指定这rpath ,就是为了指定库的加载路径, 通常mac 程序的库都放在 Frameworkds 中,

dylib 的 @rpath @executable_path、@loader_path、@rpath 运用_第4张图片

一般通过通过发布脚本的copy 命令拷贝到其中, 这个时要加载的库的位置就变化了, 这时候rpath就发挥了它的作用, 这时候我们可以给rpath设定值了 可以用 install_name_tool -add path 命令
我们这样运行
以 avcodec.59.32.101 为例 我们这样写:
先看运行前库的信息
dylib 的 @rpath @executable_path、@loader_path、@rpath 运用_第5张图片

执行 install_name_tool -add_rpath @loader_path/…/Frameworks libavutil.57.25.100.dylib

然后再查看 库的信息

dylib 的 @rpath @executable_path、@loader_path、@rpath 运用_第6张图片
注意这里
在这里插入图片描述
avcodec.59.32.101 多了这条信息
Load command 25
cmd LC_RPATH
cmdsize 40
path @loader_path/…/Frameworks (offset 12)

这个@loader_path/…/Frameworks 就会替换 @rpath,, 那么 libswresample.4.6.100.dylib的加载路径 就会被解释成这样:
@loader_path/…/Frameworks/libswresample.4.6.100.dylib。

那前面的 @loader_path是啥?

看看 官方解释 : @loader_path/ This variable is replaced with the path to the directory containing the mach-o binary which contains the load command using @loader_path. Thus, in every binary, @loader_path resolves to a different path

它在运行时 会被替换为可执行文件的路径,

dylib 的 @rpath @executable_path、@loader_path、@rpath 运用_第7张图片
对于app 也就MacOS下的可行性app的路径,,那么@loader_path/…/Frameworks/libswresample.4.6.100.dylib 就会被解释成 app路径上层目录中的Frameworks路径, 我们发布时把 libswresample.4.6.100.dylib 拷贝到Frameworks中,就可以正常被加载到了。

1、@executable_path

可执行文件的路径,例如/Applications/WeChat.app/Contents/MacOS。

2、@loader_path

被加载的二进制的路径,若该二进制是可执行文件,则@loader_path等价于@executable_path。

适用于非可执行二进制嵌套的场景,例如frameworkA包含frameworkB,frameworkB的加载路径就可以根据frameworkA的@loader_path给出。

被加载的二进制的路径,若该二进制是可执行文件,则@loader_path等价于@executable_path。

你可能感兴趣的:(xcode,macos,ide,qt)