Linux运行时动态库搜索路径优先级

Windows运行时动态库搜索路径优先级:

在Windows运行时,动态库(通常指DLL文件)的搜索路径遵循一定的优先级顺序,以确保程序能够正确地加载所需的动态库。以下是对Windows运行时动态库搜索路径优先级的总结:

  1. 应用程序所在的目录
  • 当一个应用程序(如exe文件)尝试加载一个DLL时,它首先会在自己所在的目录中查找该DLL文件。这是搜索路径中的第一优先级。
  1. 系统目录(System32)
  • 如果在应用程序所在目录中没有找到DLL,系统接下来会在系统目录中查找。在Windows操作系统中,这通常是C:\Windows\System32目录。此目录包含了大量系统级的DLL文件,这些文件对于操作系统的正常运行至关重要。
  1. Windows目录
  • 如果系统目录中也没有找到所需的DLL,搜索会继续到Windows目录下,即C:\Windows
  1. 环境变量PATH中指定的目录
  • 如果以上三个位置都没有找到DLL,系统会检查环境变量PATH中列出的所有目录。PATH环境变量是一个系统级变量,它包含了多个目录的路径,用于指导系统在执行文件或脚本时搜索相关文件的路径。因此,用户可以通过修改PATH环境变量来添加额外的搜索路径。

 

 

一、Linux运行时动态库搜索路径优先级基础:

在Linux系统中,运行时动态库(shared libraries)的搜索路径优先级是由多个因素决定的。以下是这些因素的详细解释和优先级顺序:

1. RPATH(DT_RPATH)

  • 定义:RPATH是在编译时设置在可执行文件中的路径,它指定了程序运行时应该搜索库文件的位置。
  • 优先级:如果可执行文件包含了RPATH,且DT_RUNPATH属性不存在,那么链接器会首先按照RPATH指定的路径来搜索所需的库。
  • 查看和设置方法:可以使用readelf -d xxx | grep rpath来查看库文件是否指定了rpath,可以在编译时通过编译选项指定RPATH。
// 在编译时指定rpath
if(OS_LINUX)
    set_target_properties(xxx PROPERTIES LINK_FLAGS "-Wl,-rpath='$ORIGIN' ")
endif()

备注:$ORIGIN是一个动态链接器的特殊变量,表示可执行文件或动态库文件所在的目录,可以用于指定相对路径。

2. LD_LIBRARY_PATH环境变量

  • 定义:LD_LIBRARY_PATH是一个环境变量,用户可以在运行时设置,以添加额外的库搜索路径。
  • 优先级:LD_LIBRARY_PATH中指定的路径会在系统默认路径之前进行查找,但其优先级低于RPATH。
  • 设置方法

临时设置:可以通过export LD_LIBRARY_PATH=/my/lib/path/来设置环境变量;

永久设置:可以将导出设置命令添加到shell的配置文件中,如:~/.bashrc或~/.bash_profile,设置完后执行source命令即可立即生效。之后可以通过echo $LD_LIBRARY_PATH进行验证。

3. RUNPATH(DT_RUNPATH)

  • 定义:RUNPATH是另一个与RPATH相似的特性,同样可以在可执行文件或动态库编译时设置,它也用于指定运行时查找动态库的路径。值得注意的是,当RUNPATH存在时,它会覆盖RPATH的设置。
  • 优先级:RUNPATH中指定的路径会在系统默认路径之前进行查找,但其优先级低于LD_LIBRARY_PATH。
  • 查看和设置方法:可以使用readelf -d xxx | grep runpath来查看库文件是否指定了runpath,另外除了通过编译选项设置,还可以使用工具如patchelf来设置和修改RUNPATH
// 在编译时设置runpath,需要添加--enable-new-dtags编译选项
if(OS_LINUX)
    set_target_properties(xxx PROPERTIES LINK_FLAGS "-Wl,--enable-new-dtags,-rpath='$ORIGIN' ")
endif()

// 使用工具修改runpath,注意runpath会覆盖rpath的设置
patchelf --set-rpath '$ORIGIN' xxx

4. 缓存文件ld.so.cache

  • 定义:/etc/ld.so.cache是动态链接器ld.so的二进制缓存文件,存储了系统中所有可用的动态库路径和名称信息。/etc/ld.so.cache由ldconfig命令生成,ldconfig会扫描系统中指定路径下的动态库文件(/etc/ld.so.conf及/etc/ld.so.conf.d/下的文件等),并更新/etc/ld.so.cache文件
  • 优先级:ld.so.cache的优先级低于LD_LIBRARY_PATH和RUNPATH,但高于系统默认的路径,这是为了快速找到和加载需要的动态库。
  • 更新方法:运行sudo ldconfig命令来更新缓存。
  • 查看方法:可以通过strings命令查看二进制文件的内容
// 通过strings命令查看/ld.so.cache内容,例如查看libstdc++.so的搜索路径
strings /etc/ld.so.cache | grep libstdc++.so

5. 默认的系统路径

  • 定义:在大多数Linux系统中,系统默认的动态库搜索路径包括:/lib和/usr/lib目录、以及它们的64位对应目录/lib64和/usr/lib64等。
  • 优先级:这些路径的优先级最低,如果在前面的路径中都没有找到所需的库,链接器会回退到这些默认路径。

总结

Linux运行时动态库的搜索路径优先级大致如下:

  1. DT_RPATH(如果DT_RUNRPATH为空)
  2. LD_LIBRARY_PATH环境变量
  3. DT_RUNPATH
  4. 缓存文件ld.so.cache及配置文件/etc/ld.so.conf及/etc/ld.so.conf.d/目录下的配置文件
  5. 默认的系统路径(/lib, /usr/lib, /lib64, /usr/lib64)

 

补充说明:

$ORIGIN和./的区别

在Linux和其他类Unix系统中,当动态链接库(如.so文件)被加载时,系统需要知道从哪里查找这些库。rpath(运行时库搜索路径)是存储在可执行文件或库本身中的一种机制,用于指示动态链接器在哪些位置查找依赖的库。

Library rpath:[$ORIGIN/]和 Library rpath:[./]指定了两种不同的相对路径:

1. [$ORIGIN/]:

  • $ORIGIN 是一个特殊的占位符,代表可执行文件或库文件自身的目录;
  • 当设置为[$ORIGIN/]时,它告诉动态链接器在可执行文件或库所在的同一目录下查找依赖的库;
  • 这意味着,如果你的可执行文件位于/path/to/app/myapp,那么动态链接器会在 /path/to/app目录下查找依赖的库。

2. [. /]:

  • ./表示当前工作目录,即启动可执行文件时所在的目录;
  • 当设置为[./]时,它指示动态链接器在启动可执行文件的当前目录下查找依赖的库;
  • 这意味着,如果你在 /home/user/目录下运行/path/to/app/myapp,那么动态链接器会在/home/user/ 目录下查找依赖的库,而不是在可执行文件所在的 /path/to/app/目录下。

两者的主要区别在于它们所引用的基准点不同:[$ORIGIN/]是相对于可执行文件或库的位置,而[./]是相对于当前工作目录

在实际应用中,选择哪种方式取决于你的具体需求,比如你的应用程序和库是如何被部署和使用的。通常,[$ORIGIN/]更为灵活和可靠,因为它不依赖于用户从哪个日录启动应用程序。

2)确认动态库优先级常用到的几个命令

ldd:ldd 是一个工具,用于显示可执行文件或共享库所依赖的共享库。其使用方法为:ldd xxx 或者 ldd libA.so

readelf:readelf 是一个命令行工具,专门用于查看ELF(Executable and Linkable Format)文件的信息。其用法包括:readelf -d xxx 或者 readelf -d libA.so

ps和lsof:在结合使用ps和lsof时,首先需要利用 ps -ef | grep xxx 命令来查找符合要求的进程ID,然后利用 lsof -p [PID] 命令来查看该进程打开的所有文件,从而确认模块加载的路径。

strace:strace 是一个强大的工具,能够通过打印出可执行程序的加载路径来确定优先级。其使用示例为:strace -e open,openat -o xxx.log ./xxx。该命令将执行名为xxx的可执行程序,并捕获所有open和openat系统调用,将相关信息输出到xxx.log文件中,通过查看日志也能确认模块加载的路径。

二、Linux运行时动态库搜索路径优先级规则探索:

在充分掌握动态库搜索路径优先级的基石概念之后,我们能够初步预估程序执行过程中如何精确定位并加载特定的动态库。然而,在错综复杂的大型项目背景下,时常遭遇同一动态库存在多个版本的场景。为确保程序准确无误地加载所需版本的库,深化对搜索路径优先级原则的理解显得尤为重要。

核心议题探讨

利用ldd命令检视一个可执行文件或动态库所依赖的共享库清单时,此清单是否必然反映程序运行时将加载的动态库?

针对此议题,可进一步细化为两个关键问题进行剖析:

1. 双重依赖下的优先级:若可执行文件与动态库均对某一动态库存在依赖,其搜索与加载的优先级机制如何?
2. 间接依赖的优先级:在多个动态库均依赖于同一动态库,而该依赖链不直接关联至可执行文件时,其搜索与加载的优先级又将如何确定?

规则总结

  • 基础搜索步骤:总体上,遵循既定的基本顺序进行搜索。
  • 硬件优化路径优先:在搜索过程中,动态链接器会考虑硬件架构特定的优化,优先查找与当前系统硬件特性相匹配的库版本(如在x86 64位上glibc-hwcaps/x86-64-v4、tls/x86_64/x86_64等),该功能从glibc2.33开始支持。
  • rpath/runpath动态库优先原则:当解析一个动态库的依赖时,动态链接器首先会查看该库是否通过其rpath或runpath属性指定了查找其依赖的特定路径。如果这些属性未设置或未找到所需库,链接器会回退到使用可执行文件的相应属性或系统默认的库搜索路径。
  • 时序原则:多个动态库同时依赖某个动态库时,会从先被加载的动态库设置的rpath/runpath查找它们依赖的动态库。
  • 路径缓存与去重:动态链接器会维护一个路径缓存,以避免重复搜索已知不包含目标库的文件系统路径。这种机制提高了库加载过程的效率,减少了不必要的文件系统访问。

四、macOS和鸿蒙系统的动态库搜索路径优先级探索

鉴于macOS与鸿蒙系统均基于Unix操作系统进行开发,我们可以观察到两者的动态库搜索路径的优先级与Linux系统存在相似之处。然而,值得注意的是,macOS应用程序通常以.app包的形式呈现,动态库可被置于应用程序包内的特定位置。此外,作为国产自主可控的操作系统,鸿蒙系统在动态库搜索路径的优先级上可能也存在细微差别。以下是对macOS和鸿蒙系统动态库搜索路径优先级的猜想,时间因素未作详细验证,期待来自macOS和鸿蒙系统开发领域的大佬们能够为我们提供更加深入和精彩的见解。

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

posted @   水云间月掌柜  阅读(369)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示