bug诞生记——动态库加载错乱导致程序执行异常
大纲
- 背景
- 问题发生
- 问题猜测和分析过程
- 是不是编译了本工程中的其他代码
- 是不是有缓存
- 是不是编译了非本工程的文件
- 是不是调用了其他可执行文件
- 查看CMakefiles
- 分析源码
- 检查正在运行程序的动态库
- 解决方案
这个案例发生在我研究ROS 2的测试Demo时发生的。
整体现象是:修改了源码,编译也成功了,但是执行流程和没修改前一致,新代码的逻辑没有体现。
最后定位到“动态库加载错乱”这个根本的问题,方案也就呼之欲出。但是整个排查过程经历了若干假设和推导,还是值得记录下。
背景
在《Robot Operating System——Ubuntu上以二进制形式安装环境》这篇文章中,我们安装了二进制的ROS 2,并且通过下面的指令进行了测试
source /opt/ros/jazzy/setup.bash
ros2 run demo_nodes_cpp talker

后来为了研究它的一些源码,我从github上将demo_nodes_cpp的源码(https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp)给下载到本地。执行编译后会生成build目录。在目录下会生成talker这类的可执行程序。然后我就用这些可执行程序进行编译结果测试。
问题发生
然后我看到demo_nodes_cpp/src/topics/talker_serialized_message.cpp源码时,有这么一段注释
// We know the size of the data to be sent, and thus can pre-allocate the// necessary memory to hold all the data.// This is specifically interesting to do here, because this means// no dynamic memory allocation has to be done down the stack.// If we don't allocate enough memory, the serialized message will be// dynamically allocated before sending it to the wire.auto message_header_length = 8u;auto message_payload_length = static_cast<size_t>(string_msg->data.size());serialized_msg_.reserve(message_header_length + message_payload_length);
它表达的是:这段代码去掉,程序也可以正常运行。因为rclcpp::SerializedMessage的空间会根据内容而动态分配。
然后我就去掉了这段代码,并新增了一个printf。
// We know the size of the data to be sent, and thus can pre-allocate the// necessary memory to hold all the data.// This is specifically interesting to do here, because this means// no dynamic memory allocation has to be done down the stack.// If we don't allocate enough memory, the serialized message will be// dynamically allocated before sending it to the wire.// auto message_header_length = 8u;// auto message_payload_length = static_cast<size_t>(string_msg->data.size());// serialized_msg_.reserve(message_header_length + message_payload_length);printf("serialized_msg_ allocate memory\n");
使用下面的指令编译后
colcon build --allow-overriding demo_nodes_cpp

再运行talker_serialized_message,发现“serialized_msg_ allocate memory”这句并没有输出。

问题猜测和分析过程
是不是编译了本工程中的其他代码
因为整个工程的编译模块我没细看,只能先盲猜一种最简单的原因,即:是不是编译了其他代码。
然后我搜索了上述输出中的关键字“serialized message”,发现源码文件中只有我修改的文件中才有。

这个猜测被排除!
是不是有缓存
我决定清掉build目录,重新执行编译。
中间也试过通过增加命令来在编译前清除缓存。
colcon build --cmake-clean-cache --cmake-clean-first --allow-overriding demo_nodes_cpp
很不幸,执行结果还是修改代码前的逻辑。
这个猜测排除!
是不是编译了非本工程的文件
这次测试比较暴力,直接将当前修改文件中printf的语法改错,看看编译是否报错。

报错了。
这个猜测排除!
将源文件还原成正确语法。
是不是调用了其他可执行文件
因为在《Robot Operating System——Ubuntu上以二进制形式安装环境》这篇文章中,我们使用安装的二进制文件,也运行成功了测试用例,所以怀疑通过源码编译的文件是不是在底层调用了之前通过二进制安装的另外一个环境的逻辑。
查看CMakefiles
在demo_nodes_cpp/build/demo_nodes_cpp/CMakeFiles目录下,有两个有关本例修改的目录。
- talker_serialized_message_library.dir
- talker_serialized_message.dir

通过名字可以看出来talker_serialized_message.dir对应于我们运行的可执行文件;talker_serialized_message_library.dir对应于某个库(是静态库还是动态库目前不明)。
我们将重点放在talker_serialized_message.dir上,因为我们运行的程序大概率就是通过它编译的。
在demo_nodes_cpp/build/demo_nodes_cpp/CMakeFiles/talker_serialized_message.dir/DependInfo.cmake文件中,我们看到一个比较陌生的文件node_main_talker_serialized_message.cpp
分析源码
# Consider dependencies only in project.
set(CMAKE_DEPENDS_IN_PROJECT_ONLY OFF)# The set of languages for which implicit dependencies are needed:
set(CMAKE_DEPENDS_LANGUAGES)# The set of dependency files which are needed:
set(CMAKE_DEPENDS_DEPENDENCY_FILES"/home/fangliang/demos/demo_nodes_cpp/build/demo_nodes_cpp/rclcpp_components/node_main_talker_serialized_message.cpp" "CMakeFiles/talker_serialized_message.dir/rclcpp_components/node_main_talker_serialized_message.cpp.o" "gcc" "CMakeFiles/talker_serialized_message.dir/rclcpp_components/node_main_talker_serialized_message.cpp.o.d")# Targets to which this target links which contain Fortran sources.
set(CMAKE_Fortran_TARGET_LINKED_INFO_FILES)# Targets to which this target links which contain Fortran sources.
set(CMAKE_Fortran_TARGET_FORWARD_LINKED_INFO_FILES)# Fortran module output directory.
set(CMAKE_Fortran_TARGET_MODULE_DIR "")
打开这个文件,我们发现它实际调用了libtalker_serialized_message_library.so来实现了整体功能。

这是一个非常重要的发现。它可以让我们将排查的方向指向动态库。
检查正在运行程序的动态库
我们先让程序运行起来

然后在另外一个终端中查找这个进程ID
ps -ef | grep talker_serialized_message

然后使用lsof来查看这个进程加载的是哪个目录下的动态库libtalker_serialized_message_library.so。
lsof -p 64759 | grep "libtalker_serialized_message_library.so"

可以发现它调用的是“/opt/ros/jazzy/lib/libtalker_serialized_message_library.so”,而不是我们编译的结果所在的目录(/home/fangliang/demos/demo_nodes_cpp/build/demo_nodes_cpp)。
这样就可以确定这个离奇的问题发生的原因了:
- 可执行程序调用了动态库来完成逻辑。
- 系统中有两份同名动态库。
- 可执行程序使用了错误路径下得动态库。
解决方案
解决方案也很简单,我们通过export LD_LIBRARY_PATH来修改优先级。
首先我们看下当前环境下的加载优先级(执行了source /opt/ros/jazzy/setup.bash导致环境是面向二进制ROS 2的)
echo $LD_LIBRARY_PATH
/opt/ros/jazzy/opt/rviz_ogre_vendor/lib:/opt/ros/jazzy/lib/x86_64-linux-gnu:/opt/ros/jazzy/opt/gz_math_vendor/lib:/opt/ros/jazzy/opt/gz_utils_vendor/lib:/opt/ros/jazzy/opt/gz_cmake_vendor/lib:/opt/ros/jazzy/lib
可以看到二进制安装的ROS 2环境位于高优先级。
我们只要将我们的路径提前即可
export LD_LIBRARY_PATH=/home/fangliang/demos/demo_nodes_cpp/build/demo_nodes_cpp:$LD_LIBRARY_PATH
然后执行程序,我们就看到我们修改的代码生效了。

相关文章:
bug诞生记——动态库加载错乱导致程序执行异常
大纲 背景问题发生问题猜测和分析过程是不是编译了本工程中的其他代码是不是有缓存是不是编译了非本工程的文件是不是调用了其他可执行文件查看CMakefiles分析源码检查正在运行程序的动态库 解决方案 这个案例发生在我研究ROS 2的测试Demo时发生的。 整体现象是:修改…...
Matlab演示三维坐标系旋转
function showTwo3DCoordinateSystemsWithAngleDifference() clear all close all % 第一个三维坐标系 origin1 [0 0 0]; x_axis1 [1 0 0]; y_axis1 [0 1 0]; z_axis1 [0 0 1];% 绕 x 轴旋转 30 度的旋转矩阵 theta_x 30 * pi / 180; rotation_matrix_x [1 0 0; 0 cos(th…...
redis的持久化机制以及集群模式
1.redis的持久化机制 内存数据库具有高速读写的优势,但由于数据存储在内存中,一旦服务器停止或崩溃,所有数据将会丢失。持久化机制的引入旨在将内存中的数据持久化到磁盘上,从而在服务器重启后能够恢复数据,提供更好的…...
【论文解读】大模型算法发展
一、简要介绍 论文研究了自深度学习出现以来,预训练语言模型的算法的改进速度。使用Wikitext和Penn Treebank上超过200个语言模型评估的数据集(2012-2023年),论文发现达到设定性能阈值所需的计算大约每8个月减半一次,95%置信区间约为5到14个月…...
WebApi配置Swagger、Serilog、NewtonsoftJson、Sqlsugar、依赖注入框架Autofac、MD5加密
文章目录 项目准备1、创建WebApi项目配置Swagger、Serilog、NewtonsoftJsonNewtonsoftJsonSwaggerSerilog 使用ORM框架SqlSugar创建Service类库构成MVC框架使用AutoFac进行依赖注入 创建用户登录接口添加用户时进行安全防护 项目准备 1、创建WebApi项目 配置Swagger、Serilog…...
【ffmpeg命令基础】视频选项讲解
文章目录 前言设置输出文件的帧数设置每秒播放的帧数设置输出视频的帧率示例1:更改输出视频的帧率示例2:将图像序列转换为视频 设置输入视频的帧率示例3:处理高帧率视频示例4:处理低帧率视频 同时设置输入和输出帧率示例5…...
使用uniapp开发小程序(基础篇)
本文章只介绍微信小程序的开发流程,如果需要了解其他平台的开发的流程的话,后续根据情况更新相应的文章,也可以根据uniapp官网的链接了解不同平台的开发流程 HBuilderX使用:https://uniapp.dcloud.net.cn/quickstart-hx.html 开发工具 开始…...
vue3【详解】组合式函数
什么是组合式函数? 利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数,用于实现逻辑复用,类似 react18 中的 hook 函数名称 – 以 use 开头,采用驼峰命名,如 useTitle参数 – 建议使用 toValue() 处理(…...
微服务实战系列之玩转Docker(六)
前言 刚进入大暑,“清凉不肯来,烈日不肯暮”,空调开到晚,还是满身汗。——碎碎念 我们知道,仓库可见于不同领域,比如粮食仓库、数据仓库。在容器领域,自然也有镜像仓库(registry&…...
Python题解Leetcode Hot100之动态规划
动态规划解题步骤-5部曲 确定dp数组(dp table)以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 70. 爬楼梯 题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到…...
你了解GD32 MCU上下电要求吗
你了解GD32 MCU的上下电要求吗?MCU的上下电对于系统的稳定运行非常重要。 以GD32F30X为例,上电/掉电复位波形如如下图所示。 上电过程中,VDD/VDDA电压上电爬坡,当电压高于VPOR(上电复位电压)MCU开始启动&a…...
二、【Python】入门 - 【PyCharm】安装教程
往期博主文章分享文章: 【机器学习】专栏http://t.csdnimg.cn/sQBvw 目录 第一步:PyCharm下载 第二步:安装(点击安装包打开下图页面) 第三步:科学使用,请前往下载最新工具及教程:…...
2、程序设计语言基础知识
这一章节的内容在我们的软件设计师考试当中,考的题型比较固定,基本都是选择题,分值大概在2~4分左右。 而且考的还多是程序设计语言的一些基本语法,特别是这两年比较火的Python。 所以对于有一定要编程基础的即使本章的内容不学习&…...
ARM/Linux嵌入式面经(十八):TP-Link联洲
文章目录 虚拟内存,页表,copy on write面试题1:面试题2:面试题3:进程和线程的区别红黑树和b+树的应用红黑树的应用B+树的应用视频会议用了哪些协议1. H.323协议2. SIP协议(会话发起协议)3. WebRTC(网页实时通信)4. 其他协议io多路复用(select,poll,epoll)面试题li…...
解读vue3源码-响应式篇2
提示:看到我 请让我滚去学习 文章目录 vue3源码剖析reactivereactive使用proxy代理一个对象1.首先我们会走isObject(target)判断,我们reactive全家桶仅对对象类型有效(对象、数组和 Map、Set 这样的集合类型),而对 str…...
【测开能力提升-fastapi框架】fastapi能力提升 - 中间件与CORS
1. 中间件 1.1 介绍(ChatGPT抄的,大致可以理解) 一种机制,用于在处理请求和响应之前对其进行拦截、处理或修改。中间件可以在应用程序的请求处理管道中插入自定义逻辑,以实现一些通用的功能,如身份验证、…...
centos7安装es及简单使用
为了方便日后查看,简单记录下! 【启动es前,需要调整这个配置文件(/opt/elasticsearch-6.3.0/config/elasticsearch.yml)的两处ip地址,同时访问页面地址的ip:9200时,ip地址也对应修改】 【启动kibana前,需要调整这个配置文件(/opt/kibana-6.3.0/config/k…...
2024年自动驾驶SLAM面试题及答案(更新中)
自动驾驶中的SLAM(Simultaneous Localization and Mapping,即同步定位与地图构建)是关键技术,它能够让车辆在未知环境中进行自主定位和地图建构。秋招来临之际,相信大家都已经在忙碌的准备当中了,尤其是应届…...
HTML零基础自学笔记(上)-7.18
HTML零基础自学笔记(上) 参考:pink老师一、HTML, Javascript, CSS的关系是什么?二、什么是HTML?1、网页,网站的概念2、THML的基本概念3、THML的骨架标签/基本结构标签 三、HTML标签1、THML标签介绍2、常用标签图像标签ÿ…...
数学建模--图论与最短路径
目录 图论与最短路径问题 最短路径问题定义 常用的最短路径算法 Dijkstra算法 Floyd算法 Bellman-Ford算法 SPFA算法 应用实例 结论 延伸 如何在实际应用中优化Dijkstra算法以提高效率? 数据结构优化: 边的优化: 并行计算&…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
初探Service服务发现机制
1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能:服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
git: early EOF
macOS报错: Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...
英国云服务器上安装宝塔面板(BT Panel)
在英国云服务器上安装宝塔面板(BT Panel) 是完全可行的,尤其适合需要远程管理Linux服务器、快速部署网站、数据库、FTP、SSL证书等服务的用户。宝塔面板以其可视化操作界面和强大的功能广受国内用户欢迎,虽然官方主要面向中国大陆…...
