一次使用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个注意事项&…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...

MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...

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

第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10+pip3.10)
第一篇:Liunx环境下搭建PaddlePaddle 3.0基础环境(Liunx Centos8.5安装Python3.10pip3.10) 一:前言二:安装编译依赖二:安装Python3.10三:安装PIP3.10四:安装Paddlepaddle基础框架4.1…...