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

一次使用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.solibb.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定位问题的经历

在实际工作中&#xff0c;当遇到段错误&#xff0c;我们会很容易的想到这是非法访问内存导致的&#xff0c;比如访问了已经释放的内存&#xff0c;访问数据越界&#xff0c;尝试写没有写权限的内存等。使用gdb进行调试&#xff0c;查看出异常的调用栈&#xff0c;往往可以定位到…...

数据库安全:如何进行数据库安全审计?

数据库安全:如何进行数据库安全审计? 数据库安全审计是保障数据库安全的重要手段之一,可以帮助企业及时发现潜在的安全风险并采取相应的措施。以下是进行数据库安全审计的步骤和方法: 一、确定审计目标 在进行数据库安全审计之前,首先需要确定审计的目标。这可能包括以…...

【Python】基础语法错误和异常

在Python中&#xff0c;语法错误和异常是两个常见的问题。下面对它们进行简要介绍。 1.语法错误 (Syntax Error) 语法错误是指代码的语法不符合Python的语言规则。当Python解释器读取程序代码时&#xff0c;如果发现语法不正确&#xff0c;就会抛出语法错误。这种错误通常在代…...

获取每个页面的元素,并写入json

获取每个页面的元素&#xff0c;并写入json 想法&#xff1a;如何去记住每个页面的元素&#xff0c;如何实现不同页面的导航&#xff0c;如何从主页面遍历每一个页面的每一个元素 1.创建数据结构存储 2.树状图正好是我们想要的结构体&#xff1a;创建树状图结构体 3.记录每个页…...

【ShuQiHere】深入解析数字电路中的锁存器与触发器

深入解析数字电路中的锁存器与触发器 &#x1f916;&#x1f50c; 在数字电路设计中&#xff0c;**锁存器&#xff08;Latch&#xff09;和触发器&#xff08;Flip-Flop&#xff09;**是实现时序逻辑的基本元件。它们能够存储状态&#xff0c;是构建复杂数字系统的关键。本文将…...

【学习AI-相关路程-mnist手写数字分类-python-硬件:jetson orin NX-自我学习AI-基础知识铺垫-遇到问题(1) 】

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

数据轻松上云——Mbox边缘计算网关

随着工业4.0时代的到来&#xff0c;工厂数字化转型已成为提升生产效率、优化资源配置、增强企业竞争力的关键。我们凭借其先进的边缘计算网关与云平台技术&#xff0c;为工厂提供了高效、稳定的数据采集与上云解决方案。本文将为您介绍Mbox边缘计算网关如何配合明达云平台&…...

ifftshift函数

ifftshift 原理 将频域数据移回时域的函数。它通常与 fftshift 配合使用&#xff0c;后者用于将时域数据移动到频域中心。 而ifftshift所作的事正好相反&#xff0c;将频谱恢复到能量集中在两端&#xff08;或四个角&#xff09;上&#xff0c;接着就可以做逆傅里叶变换了 具…...

vue3 + ts + element-plus 二次封装 el-dialog

实现效果&#xff1a; 组件代码&#xff1a;注意 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)

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

如何在浏览器中查看格式化的 HTML?

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

浅谈计算机存储体系和CPU缓存命中

一、计算机存储 一般关于计算机存储体系分为三层 ①三级缓存/寄存器 大多数寄存器只有四字节到八字节&#xff0c;只用于读取很小的数据&#xff1b;三级缓存是为了方便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 请求&#xff0c;系列文章&#xff1a; 《Java使用原生HttpURLConnection实现发送HTTP请求》 《Java使用HttpClient5实现发送HTTP请求》 《SpringBoot使用RestTemplate实现发送HTTP请求》 1、HttpURLConnection 类的介绍 HttpURLConnection 是 Java 提供的…...

TinyC编译器5—词法分析

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

电子电气架构---智能计算架构和SOA应用

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

Python Numpy 实现神经网络自动训练:反向传播与激活函数的应用详解

Python Numpy 实现神经网络自动训练&#xff1a;反向传播与激活函数的应用详解 这篇文章介绍了如何使用 Python 的 Numpy 库来实现神经网络的自动训练&#xff0c;重点展示了反向传播算法和激活函数的应用。反向传播是神经网络训练的核心&#xff0c;能够通过计算梯度来优化模…...

Apache Calcite - 基于规则的查询优化

基于规则的查询优化 基于规则的查询优化&#xff08;Rule-based Query Optimization&#xff09;是一种通过应用一系列预定义的规则来优化查询计划的技术。这些规则描述了如何转换关系表达式&#xff0c;以提高查询执行的效率。基于规则的优化器并不依赖于统计信息&#xff0c…...

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文件

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

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)

服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...

vue3 定时器-定义全局方法 vue+ts

1.创建ts文件 路径&#xff1a;src/utils/timer.ts 完整代码&#xff1a; import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定&#xff0c;这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中&#xff0c;积分电荷法最为常用&#xff0c;其原理是通过测量在电容器上积累的热释电电荷&#xff0c;从而确定热释电系数…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

【 java 虚拟机知识 第一篇 】

目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...

探索Selenium:自动化测试的神奇钥匙

目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...

算法打卡第18天

从中序与后序遍历序列构造二叉树 (力扣106题) 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7…...