当前位置: 首页 > news >正文

C++(Qt)软件调试---Linux动态库链接异常排查(38)

C++(Qt)软件调试—Linux动态库链接异常排查(38)


文章目录

  • C++(Qt)软件调试---Linux动态库链接异常排查(38)
    • @[toc]
    • 1 概述🐜
    • 2 加载动态库方法
    • 3 程序链接动态库常见异常现象和后果
    • 4 查看依赖库的加载情况
    • 5 查看动态库搜索路径
    • 6 修改动态库搜索路径
      • 6.1 编译时设置RUNPATH
      • 6.2 修改可执行程序的RUNPATH

更多精彩内容
👉内容导航 👈
👉C++软件调试 👈

1 概述🐜

本文主要讲解Linux系统中C++程序隐式链接动态库出现动态库找不到的异常分析、排查方法。

排查思路概述:

  • 出现异常;
  • 检查依赖关系lddlddtree
  • 分析搜索路径
  • 编译时指定搜索路径或者使用patchelf 修改搜索路径。
环境说明
系统ubuntu20.04

以下是关于Linux动态链接相关命令和配置的详细说明:

命令/配置说明
ldd用于打印程序或共享库所依赖的共享库列表。它通过设置特殊的环境变量并运行程序来确定依赖关系,是检查可执行文件依赖关系的常用工具
lddtree以树状结构显示程序或共享库的依赖关系,比 ldd 提供更清晰的依赖层次视图,有助于理解复杂的依赖关系链
LD_DEBUG环境变量,用于启用动态链接器的调试输出。可设置不同选项如 libsbindingssymbols 等来获取详细的动态链接过程信息
LD_LIBRARY_PATH环境变量,指定动态链接器在搜索共享库时的额外搜索路径。优先级高于系统默认路径,常用于临时覆盖库搜索路径
LD_PRELOAD环境变量,允许预加载指定的共享库,优先于程序正常加载的库。常用于库函数拦截、性能分析或功能增强
/etc/ld.so.conf系统级配置文件,定义了系统范围内共享库的搜索路径。通常包含多个include指令指向 /etc/ld.so.conf.d/ 目录下的配置文件
ldconfig系统命令,用于更新共享库缓存。它会扫描 /etc/ld.so.conf 中定义的目录,创建或更新 /etc/ld.so.cache 文件以加速库搜索
readelfELF文件分析工具,用于显示ELF格式文件的详细信息,包括节头、程序头、符号表、重定位信息等,是分析可执行文件结构的重要工具
objdump通用目标文件分析工具,可用于反汇编、显示目标文件信息、重定位信息等。支持多种目标文件格式,是二进制分析的核心工具
patchelf专门用于修改ELF可执行文件和共享库的工具。可以更改解释器路径、修改RPATH/RUNPATH、添加或删除所需的库等

2 加载动态库方法

C++加载动态库(DLL)主要有两种方式:

隐式链接

  • 在编译时通过 -l 参数链接动态库
  • 程序启动时自动加载所需的共享库
  • 符号解析在编译时完成
  • 使用简单,直接调用库中的函数和变量
  • 优点
    • 使用简单直观
    • 性能较好,无需手动管理库的加载
    • 编译时就能发现符号错误
  • 缺点
    • 程序依赖的库必须在运行时存在
    • 程序启动时必须加载所有依赖库
    • 灵活性较差

显式链接

  • 通过 dlopen 系列函数在程序运行时手动加载动态库
  • 优点
    • 灵活性高,可以按需加载库
    • 支持插件化架构
    • 可以在运行时决定加载哪个库
    • 库不存在不会影响程序启动
  • 缺点
    • 使用复杂,需要手动管理库的加载和卸载
    • 容易出现内存泄漏或悬空指针
    • 运行时错误处理复杂
    • 符号解析延迟到运行时

3 程序链接动态库常见异常现象和后果

  1. 动态库缺失

    • 动态库不存在或者查找动态库路径不对;
    ./untitled: error while loading shared libraries: libtest2.so.1: cannot open shared object file: No such file or directory
    
    • 现象:程序无法启动
    • 后果:进程直接终止,返回非零退出码
  2. 符号未定义错误

    • 动态库存在,链接动态库成功,但是动态库中函数符号定义不同
    ./untitled: symbol lookup error: ./untitled: undefined symbol: _ZN5Test23addEii
    
    • 现象:程序加载时报错,指出缺少特定符号
    • 后果:程序无法正常执行,即使能启动也会在调用相关函数时崩溃
  3. 版本不兼容

    • 编译环境与运行环境不一致
    version `GLIBC_2.XX' not found
    
    • 现象:动态库存在但版本不符合要求
    • 后果:程序可能在运行过程中出现不可预知的行为或崩溃

4 查看依赖库的加载情况

  • 当Linux下出现动态库缺失时,一般异常信息如下所示;

    error while loading shared libraries: libtest2.so.1: cannot open shared object file: No such file or directory
    
  • 可使用ldd命令或者lddtree命令进行查看依赖库的加载情况;

ldd命令

ldd 是一个用于显示 ELF 可执行文件或共享库依赖关系的命令行工具。

主要功能

  • 显示程序或库文件所依赖的共享库列表
  • 显示每个依赖库的路径和加载地址
  • 检查动态链接库的可用性

lddtree 命令

lddtreeldd增强版工具,通常属于 pax-utils 包,能够以树状结构显示依赖关系。

可以使用sudo apt install pax-utils命令安装lddtree工具;

主要功能

  • 以层次化树形结构展示依赖关系
  • 显示完整的依赖链和依赖传递关系
  • 提供比 ldd 更详细的依赖分析
  • 如下所示,用来演示的示例程序,libtest2调用了libtest1,untitled调用了libtest2,并且所有动态库和可执行程序都放在同一路径下;

    在这里插入图片描述

  • 直接运行./untitled提示找不到libtest2.so

    在这里插入图片描述

  • 使用ldd命令查看依赖关系如下所示,明明动态库就在同一路径下,还是显示找不到libtest2.so

    在这里插入图片描述

  • 使用lddtree命令查看;

    在这里插入图片描述

5 查看动态库搜索路径

  • 明明动态库存在,但是还是显示找不到,这时就可以使用LD_DEBUG环境变量或者readelfpatchelfobjdump等命令来排查;

  • 默认情况下在 Linux 系统中,可执行程序在运行时需要链接动态库(共享库)。系统按照下面顺序搜索这些动态库:

    • 可执行文件本身的 DT_RPATHDT_RUNPATH 段;
    • LD_LIBRARY_PATH 环境变量指定的目录;
    • LD_PRELOAD 环境变量;
    • /etc/ld.so.cache缓存文件,这是由 ldconfig 命令根据 /etc/ld.so.conf 及其包含的配置文件生成的缓存;
  • 如下所示使用LD_DEBUG=libs ./untitled 命令显示库搜索过程,并没有包含./搜索路径,所以动态库放在当前路径也找不到;

    在这里插入图片描述

  • 使用readelf -d executable_name命令看完整的动态段信息如下所示,

    • RUNPATH段内容为/opt/Qt5.14.2/5.14.2/gcc_64/lib
    • 程序运行就会优先从/opt/Qt5.14.2/5.14.2/gcc_64/lib路径去找动态库;

    在这里插入图片描述

  • 或者使用objdump -p executable_name也可以查看;

    在这里插入图片描述

  • 使用patchelf --print-rpath executable_name命令也可以查看;

    在这里插入图片描述

  • 使用echo $LD_LIBRARY_PATH命令可以查看LD_LIBRARY_PATH环境变量当前设置;

  • 使用echo $LD_PRELOAD命令可以查看LD_PRELOAD环境变量当前设置;

主要区别对比

特性LD_LIBRARY_PATHLD_PRELOAD
作用机制指定搜索目录指定具体要加载的库文件
搜索方式添加到库搜索路径中直接加载指定的库文件
覆盖能力不能覆盖同名库的特定版本可以覆盖库中的具体函数
使用场景解决库文件位置问题实现库函数拦截和替换
语法格式目录路径列表具体库文件路径列表
  • 使用cat /etc/ld.so.conf命令和cat /etc/ld.so.conf.d/*.conf命令可以查看/etc/ld.so.conf 配置文件指定系统范围内的动态链接库搜索路径;

    在这里插入图片描述

  • 使用ldconfig -p命令可以查看/etc/ld.so.cache缓存内容;

6 修改动态库搜索路径

DT_RPATH

  • 传统的行为方式
  • 会传递给依赖的共享库
  • 在搜索 LD_LIBRARY_PATH 之前搜索

DT_RUNPATH

  • 新的行为方式
  • 不会传递给依赖的共享库
  • 在搜索 LD_LIBRARY_PATH 之后搜索
  • 默认情况下,现代链接器通常生成 DT_RUNPATH。
  • 查看了所有的搜索路径都没有当前路径,那么将怎么将动态库放在当前路径,然后发布出去给用户呢;
  • 方法1:通过安装脚本自动设置环境变量LD_LIBRARY_PATH
  • 方法2:修改程序本身的DT_RUNPATH段;

6.1 编译时设置RUNPATH

  • 编译时使用g++的-rpath选项指定搜索路径;

  • 通常与 -L-l 选项一起使用

  • -rpath 是一个链接器选项,用于在可执行文件或共享库中嵌入运行时库搜索路径;

  • 不过需要注意的是,-rpath 通常是通过 -Wl 选项传递给链接器的,因为 g++ 本身不直接处理这个选项;

  • 可以使用冒号分隔多个路径;

    g++ main.cpp -o main -L./ -ltest2 -Wl,-rpath,./:/home/mhf/
    
  • 可以使用相对于可执行文件位置的路径,其中 $ORIGIN 表示可执行文件所在的目录:

    g++ -Wl,-rpath='$ORIGIN/lib' main.cpp -o myapp
    
  • 编译后就可以在当前路径找到动态库了;

    在这里插入图片描述

  • 使用readelf -d main命令查看RUNPATH如下所示;

在这里插入图片描述

  • 在qmake中可以通过下面代码设置,用法相同;

    QMAKE_LFLAGS += -Wl,-rpath,/usr/local/lib
    
  • 在cmake中可以通过如下所示代码

    cmake_minimum_required(VERSION 3.15)project(main)
    # 查找 test2 库
    find_library(TEST2_LIBRARYNAMES test2PATHS ./
    )add_executable(${PROJECT_NAME} main.cpp)
    # 链接找到的库
    if(TEST2_LIBRARY)target_link_libraries(${PROJECT_NAME} PRIVATE ${TEST2_LIBRARY})
    else()message(FATAL_ERROR "test2 library not found")
    endif()
    # 设置查找路径
    target_link_options(${PROJECT_NAME} PRIVATELINKER:-rpath,./:home/user/lib
    )
    

6.2 修改可执行程序的RUNPATH

  • 当可执行程序已经存在,不想重启编译或者没有源码重新编译;

  • 可通过下面patchelf命令修改可执行文件本身的 DT_RPATHDT_RUNPATH 段;

    patchelf --set-rpath '$ORIGIN/lib/' ./untitled
    
  • 如下所示修改untitled后就可以找到libtest2.so了,但是libtest2.so依赖于libtest1.so,所以还需要修改libtest2.soRUNPATH

    在这里插入图片描述

  • 这样就完成了依赖的查找,程序就可以正常运行了。

    在这里插入图片描述

    在这里插入图片描述



http://www.dtcms.com/a/423957.html

相关文章:

  • 记录 Qt 跨线程 信号无法触发槽函数问题
  • wireshark 01——安装
  • 网上最好购物网站邯郸网上销售公司
  • 使用top域名做网站seo职位是什么意思
  • CUDIS 健康协议在 Sui 上打造更健康的未来
  • 装修网站排行榜前十名有哪些南昌网站建设哪家最好
  • Golang学习笔记:context的使用场景
  • 带有客户案例的网站广州专业网站建设报价
  • 昆明微信网站建设软件开发模型有几种并简述其特点
  • 高效实现实体删除的宏解决方案:使用Rust宏优化删除操作
  • Rust泛型详解
  • 官方手表网站网站专题分类
  • 新乡网站建设方案搜狗网址大全下载安装
  • 关于可视化卷积核和特征图的深度理解
  • 【mysql】Mybatisplus BINARY {0} LIKE CONCAT(‘%‘, {1}, ‘%‘)写这句话是什么意思
  • 开发避坑指南(59):Vue3中高效删除数组元素的方法
  • wordpress建站要用模板吗wordpress搜索筛选
  • 安卓 WPS Office v18.21.0 国际版
  • 衡阳网站推广优化公司行业网站开发运营方案
  • 临海房产中介网站如何制作网站平台管理
  • 做网站多少人建e室内设计网官网平面图
  • git mere 错误后的回滚处理
  • Java开发入门(一)--- JDK与环境变量配置
  • 最好的营销型网站建设公司报电子商务(网站建设与运营)
  • 从0到1制作一个go语言游戏服务器(二)web服务搭建
  • 网站使用流程图昆明网站建设天锐科技
  • (uniapp)基于vue3父子组件间传递参数与方法
  • 铁岭开原网站建设高中课程免费教学网站
  • 高校网站群建设方案网站建设目录结构设计
  • 静态网站源码野花韩国视频在线观看免费高清