一次使用LD_DEBUG定位问题的经历
在实际工作中,当遇到段错误,我们会很容易的想到这是非法访问内存导致的,比如访问了已经释放的内存,访问数据越界,尝试写没有写权限的内存等。使用gdb进行调试,查看出异常的调用栈,往往可以定位到问题原因。
但是有些时候,段错误问题的定位也没这么简单。本人在工作中就遇到了一个段错误的问题,问题原因不是那么直观,出问题的代码非常简单,直接按照常规的分析方法,很难分析出原因。往内存越界、内存没有权限这个方向分析无法定位问题。
当我们遇到这种问题的时候,没有思路的时候,往往会采用一种方式,就是把编译环境清空,完全重新再编译一遍。的确,很多时候如果我们的运行环境,有些库和可执行文件,如果不是在一个统一的环境下编译出来的,版本号对应不上,也会引起这样的问题。但是对于符号覆盖的情况,即使重新编译一遍,也无法解决问题。
除了重新编译一遍,我们还可以使用LD_DEBUG来查看调试信息,LD_DEBUG可以查看共享库的搜索和链接过程,也可以看到符号的绑定过程。
问题的实际原因:动态库中有同名的符号,在执行程序的时候出现了符号覆盖。
1问题复现
可执行文件直接依赖两个动态库,两个动态库有同名符号,这样编译可执行文件的时候无法编译通过。如果要复现这个问题,需要间接依赖。
如下图所示,liba.so和libb.so中有重名符号。libaa.so依赖liba.so,libbb.so依赖libb.so,可执行文件依赖libaa.so和libbb.so。可执行文件间接依赖liba.so和libb.so。
liba.so | 对于类test::example::Hello,在构造函数中申请了8字节的内存,在函数DoSame中对内存进行了写,在析构函数中释放了内存。 |
libb.so | libb.so中的函数test::example::Hello::DoSame与liba.so中的函数重名。 |
libaa.so | 调用了liba.so中函数test::example::Hello::DoA |
libbb.so | 调用了libb.so中的test::example::Hello::DoB和和test::example::Hello::DoSame |
main | 在main函数中调用了libaa.so中的DoAA和libbb.so中的DoSame |
main函数中调用了3个函数,分别为DoAA、DoBB、DoSame,调用关系如下图所示。
1.1liba.so
liba.h
#ifndef _LIB_A_H_
#define _LIB_A_H_namespace test {
namespace example {
class Hello {public:Hello();~Hello();void DoA();void DoSame();char *p = nullptr;
};
}
}#endif
liba.cpp
#include <stdlib.h>
#include <iostream>
#include "liba.h"namespace test {
namespace example {Hello::Hello() {p = (char *)malloc(8);printf("p: %p, this: %p\n", p, this);
}Hello::~Hello() {if (p) {std::cout << "free p\n";free(p);}
}void Hello::DoA() {std::cout << "do a in liba.\n";
}void Hello::DoSame() {std::cout << "do same in liba.\n";p[0] = 'a';
}
}
}
1.2libb.so
libb.h
#ifndef _LIB_B_H_
#define _LIB_B_H_namespace test {
namespace example {
class Hello {public:void DoB();void DoSame();
};
};
};#endif
libb.cpp
#include <iostream>
#include "libb.h"namespace test {
namespace example {
void Hello::DoB() {std::cout << "do b in libb.\n";
}void Hello::DoSame() {std::cout << "do same in libb.\n";
}
};
};
1.3libaa.so
libaa.h
void DoAA();
libaa.cpp
#include "libaa.h"
#include "liba.h"
#include <iostream>void DoAA() {std::cout <<"do aa\n";test::example::Hello hello;hello.DoA();
}
1.4libbb.so
libbb.h
#include "libb.h"void DoBB();
void DoSame();
libbb.cpp
#include <iostream>
#include "libbb.h"
#include "libb.h"void DoBB() {std::cout << "do bb\n";test::example::Hello hello;hello.DoB();
}void DoSame() {std::cout << "do same\n";test::example::Hello hello;hello.DoSame();
}
1.5main函数
#include <iostream>
#include <string>
#include "libaa.h"
#include "libbb.h"int main() {DoAA();DoBB();DoSame();return 0;
}
使用如下命令进行编译:
g++ liba.cpp -fPIC --shared -o liba.so -g
g++ libb.cpp -fPIC --shared -o libb.so -g
g++ libbb.cpp libb.so -fPIC --shared -o libbb.so -g
g++ libaa.cpp liba.so -fPIC --shared -o libaa.so -g
g++ main.cpp libaa.so libbb.so -g
运行结果,如下,可以看到,出现了段错误。
使用gdb进行定位,在p[0]='a'这行代码出现了段错误。打印p的话,发现p是一个非法地址。
可以看到,按照我们的预期,main函数中调用DoSame是调用的libbb.so中的DoSame,而libbb.so中的DoSame是调用的libb.so中的test::example::Hello::DoSame。但是根据gdb中的栈可以看到,最终调用到了liba.so中的test::example::Hello::DoSame。
调用函数出现了错乱,与预期的不一致。
编译可执行文件的时候,如果有间接调用的函数,那么最终调用的函数不是在编译的时候确定的。而是在运行的时候动态绑定的。比如libbb.so中DoSame调用的test::example::Hello::DoSame,这个函数最终调用liba.so中的还是libb.so中的,是需要动态绑定的。
在启动a.out的时候,使用LD_DEBUG进行调试,如下命令,就可以看到运行是符号绑定的信息。
LD_DEBUG=bindings ./a.out
如下所示,DoSame最终调用到了libs.so中的函数,所以调用的函数与预期的不一致。
41428: binding file /home/wangyanlong/test/symboltest/libbb.so [0] to /home/wangyanlong/test/symboltest/liba.so [0]: normal symbol `_ZN4test7example5Hello6DoSameEv'
2符号优先级
2.1动态库中重复的符号
上一节中,编译main.cpp的时候使用的是下边的命令,libaa.so在libbb.so之前,这样编译的话,查找符号会优先使用liba.so中的符号。
g++ main.cpp libaa.so libbb.so -g
如果修改两个库的顺序,libbb.so放在libss.so之前,那么就会绑定libb.so中的DoSame。
g++ main.cpp libbb.so libaa.so
2.2静态库和动态库重复符号
静态库中的符号是硬编到可执行文件中的,在绑定符号的时候肯定是可执行文件优先。如下所示,将liba.cpp编译成静态库。编译main.cpp的时候不管libaa.so、liba.a、libbb.so的先后顺序,test::example::Hello::DoSame都会绑定liba.so中的符号。
gcc liba.cpp -c
ar rcs liba.a liba.o
g++ libb.cpp -fPIC --shared -o libb.so -g
g++ libbb.cpp libb.so -fPIC --shared -o libbb.so -g
g++ libaa.cpp -fPIC --shared -o libaa.so -g
g++ main.cpp libbb.so libaa.so liba.a -g
相关文章:

一次使用LD_DEBUG定位问题的经历
在实际工作中,当遇到段错误,我们会很容易的想到这是非法访问内存导致的,比如访问了已经释放的内存,访问数据越界,尝试写没有写权限的内存等。使用gdb进行调试,查看出异常的调用栈,往往可以定位到…...
数据库安全:如何进行数据库安全审计?
数据库安全:如何进行数据库安全审计? 数据库安全审计是保障数据库安全的重要手段之一,可以帮助企业及时发现潜在的安全风险并采取相应的措施。以下是进行数据库安全审计的步骤和方法: 一、确定审计目标 在进行数据库安全审计之前,首先需要确定审计的目标。这可能包括以…...
【Python】基础语法错误和异常
在Python中,语法错误和异常是两个常见的问题。下面对它们进行简要介绍。 1.语法错误 (Syntax Error) 语法错误是指代码的语法不符合Python的语言规则。当Python解释器读取程序代码时,如果发现语法不正确,就会抛出语法错误。这种错误通常在代…...
获取每个页面的元素,并写入json
获取每个页面的元素,并写入json 想法:如何去记住每个页面的元素,如何实现不同页面的导航,如何从主页面遍历每一个页面的每一个元素 1.创建数据结构存储 2.树状图正好是我们想要的结构体:创建树状图结构体 3.记录每个页…...
【ShuQiHere】深入解析数字电路中的锁存器与触发器
深入解析数字电路中的锁存器与触发器 🤖🔌 在数字电路设计中,**锁存器(Latch)和触发器(Flip-Flop)**是实现时序逻辑的基本元件。它们能够存储状态,是构建复杂数字系统的关键。本文将…...

【学习AI-相关路程-mnist手写数字分类-python-硬件:jetson orin NX-自我学习AI-基础知识铺垫-遇到问题(1) 】
【学习AI-相关路程-mnist手写数字分类-python-硬件:jetson orin NX-自我学习AI-基础知识铺垫-遇到问题(1) 】 1、前言2、先行了解(1)学习基础知识-了解jetson orin nx 设备(2)学习python&AI…...

数据轻松上云——Mbox边缘计算网关
随着工业4.0时代的到来,工厂数字化转型已成为提升生产效率、优化资源配置、增强企业竞争力的关键。我们凭借其先进的边缘计算网关与云平台技术,为工厂提供了高效、稳定的数据采集与上云解决方案。本文将为您介绍Mbox边缘计算网关如何配合明达云平台&…...
ifftshift函数
ifftshift 原理 将频域数据移回时域的函数。它通常与 fftshift 配合使用,后者用于将时域数据移动到频域中心。 而ifftshift所作的事正好相反,将频谱恢复到能量集中在两端(或四个角)上,接着就可以做逆傅里叶变换了 具…...

vue3 + ts + element-plus 二次封装 el-dialog
实现效果: 组件代码:注意 style 不能为 scoped <template><el-dialog class"my-dialog" v-model"isVisible" :show-close"false" :close-on-click-modal"false" :modal"false"modal-class&…...

MySQL9.0安装教程zip手动安装(Windows)
本章教程,主要介绍如何在Windows上安装MySQL9.0,通过zip方式进行手动安装。 一、下载MySQL压缩包 下载地址:https://downloads.mysql.com/archives/community/ 二、解压MySQL压缩包 将下载好的压缩包,进行解压缩,并且将…...

如何在浏览器中查看格式化的 HTML?
问题描述 我需要这个 HTML 页面在我的浏览器中显示格式化后的信息。我只是将文件存储在本地驱动器上。我需要将文件上传到服务器才能将其作为 HTML 查看,还是可以在本地查看?如在屏幕截图中看到的,它当前显示相同的 HTML 代码。我尝试搜索&am…...

浅谈计算机存储体系和CPU缓存命中
一、计算机存储 一般关于计算机存储体系分为三层 ①三级缓存/寄存器 大多数寄存器只有四字节到八字节,只用于读取很小的数据;三级缓存是为了方便CPU读取内存中数据而存在的 ②内存————数据结构就是在内存中管理数据 ③硬盘————数据库/文件就…...
ES操作:linux命令
查询数据库所有索引 没有密码 curl -X GET "http://localhost:9200/_cat/indices?v" 有密码 curl -u elastic:my_password -X GET "http://localhost:9200/_cat/indices?v" 删除索引 curl-X DELETE "http://localhost:9200/index_XXX" 不…...

Java使用原生HttpURLConnection实现发送HTTP请求
Java 实现发送 HTTP 请求,系列文章: 《Java使用原生HttpURLConnection实现发送HTTP请求》 《Java使用HttpClient5实现发送HTTP请求》 《SpringBoot使用RestTemplate实现发送HTTP请求》 1、HttpURLConnection 类的介绍 HttpURLConnection 是 Java 提供的…...

TinyC编译器5—词法分析
1.词法分析的概念 词法分析也称为 分词 ,此阶段编译器从左向右扫描源文件,将其字符流分割成一个个的 词 ( token 、 记号 ,后文中将称为 token )。所谓 token ,就是源文件中不可再进一步分割的一串字符&am…...

电子电气架构---智能计算架构和SOA应用
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不…...

Python Numpy 实现神经网络自动训练:反向传播与激活函数的应用详解
Python Numpy 实现神经网络自动训练:反向传播与激活函数的应用详解 这篇文章介绍了如何使用 Python 的 Numpy 库来实现神经网络的自动训练,重点展示了反向传播算法和激活函数的应用。反向传播是神经网络训练的核心,能够通过计算梯度来优化模…...
Apache Calcite - 基于规则的查询优化
基于规则的查询优化 基于规则的查询优化(Rule-based Query Optimization)是一种通过应用一系列预定义的规则来优化查询计划的技术。这些规则描述了如何转换关系表达式,以提高查询执行的效率。基于规则的优化器并不依赖于统计信息,…...
react学习笔记,ReactDOM,react-router-dom
react 学习 1. 下载与安装 下载 npm install -g create-react-app 安装 npx create-react-app xxx 推荐 npm init react-app xxx yarn create react-app xxx 2. 创建 react 元素 indexjs 文件 import React from "react"; import ReactDOM from "react…...

优化UVM环境(八)-整理project_common_pkg文件
书接上回: 优化UVM环境(七)-整理环境,把scoreboard拿出来放在project_common环境里 Prj_cmn_pkg.sv考虑到是后续所有文件的基础,需要引入uvm_pkg并把自身这个pkg import给后续的文件: 这里有3个注意事项&…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理
在城市的某个角落,一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延,滚滚浓烟弥漫开来,周围群众的生命财产安全受到严重威胁。就在这千钧一发之际,消防救援队伍迅速行动,而豪越科技消防一体化安全管控平台构建的消防“…...