一次使用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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/454124.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

告别装机烦恼,IT小白到IT大神都在用的免费神器

作为USB启动盘制作工具中的佼佼者&#xff0c;Rufus凭借其卓越的性能和实用性&#xff0c;赢得了众多用户的信赖。这款精巧的实用程序不仅能够将系统ISO镜像完美转换为可引导的USB安装介质&#xff0c;还提供了全面的USB设备管理功能。 Rufus的安装包仅有1.5M大小&#xff0c;…

ubuntu系统库和Anaconda库冲突问题

之前安装opencv时没出现过这种问题,自从安装Anaconda后就总遇到问题。记录下自己的解决过程。 目录 第一步 第二步 第三步 安装opencv时出现以下问题: /usr/bin/ld: /lib/x86_64-linux-gnu/libwayland-client.so.0: undefined reference to `ffi_prep_cif@LIBFFI_BASE_7.…

qt QTableview 左侧 序号 倒序

本文主要在QTableview插入数据的基础上&#xff0c;使左边序号实现倒序&#xff0c;实现如下图所示。 解决办法&#xff1a; QTableview左侧是QHeaderView类构成的&#xff0c;重写QHeaderView的paintSection&#xff0c; 重写序号的文字内容&#xff0c;进而 实现QTableview …

FFT过程中自动补零,补零部分FFT结果不为零

在 FFT&#xff08;快速傅里叶变换&#xff09; 中&#xff0c;补零&#xff08;Zero Padding&#xff09;是为了使信号的点数符合 2 的幂次方&#xff0c;以提高 FFT 的计算效率。然而&#xff0c;即使你对信号进行了补零&#xff0c;FFT 计算后在补零部分可能会得到复数结果不…

医院信息化与智能化系统(5)

医院信息化与智能化系统(5) 这里只描述对应过程&#xff0c;和可能遇到的问题及解决办法以及对应的参考链接&#xff0c;并不会直接每一步详细配置 如果你想通过文字描述或代码画流程图&#xff0c;可以试试PlantUML&#xff0c;告诉GPT你的文件结构&#xff0c;让他给你对应的…

python 文件防感染扫描

一、安装 首先&#xff0c;你需要安装 secplugs-python-client 库。你可以通过 pip 命令来安装&#xff1a; pip install secplugs-python-client确保你的 Python 环境已经正确设置&#xff0c;并且网络连接畅通&#xff0c;以便能够顺利安装。 二、基本用法 1. 初始化客户…

nosql课本习题

nosql题目 1. 文档数据库相比其他 NoSQL 的突出优势和特点是什么&#xff1f; 答案&#xff1a; 文档数据库的突出优势在于它的灵活性和可扩展性。不同于传统的关系型数据库&#xff0c;文档数据库允许存储半结构化和非结构化数据&#xff0c;每个文档可以有不同的字段&#x…

【优先算法】--双指针1

“一念既出&#xff0c;万山无阻。”加油陌生人&#xff01; 目录 1.双指针--移动零 2.双指针-复写零 ok&#xff0c;首先在学习之前&#xff0c;为了方便大家后面的学习&#xff0c;我们这里需要补充一个知识点&#xff0c;我这里所谓的指针&#xff0c;不是之前学习的带有…

二分查找_ x 的平方根搜索插入位置山脉数组的峰顶索引

x 的平方根 在0~X中肯定有数的平方大于X&#xff0c;这是肯定的。我们需要从中找出一个数的平方最接近X且不大于X。0~X递增&#xff0c;它们的平方也是递增的&#xff0c;这样我们就可以用二分查找。 我们找出的数的平方是<或者恰好X&#xff0c;所以把0~X的平方分为<X …

Leetcode—1279. 红绿灯路口【简单】Plus(多线程)

2024每日刷题&#xff08;186&#xff09; Leetcode—1279. 红绿灯路口 C实现代码 class TrafficLight { public:TrafficLight() {}void carArrived(int carId, // ID of the carint roadId, // ID of the road the car travels on. Can …

【Linux】僵尸进程和孤儿进程

一、僵尸进程 何为僵尸进程&#xff1f; 在 Unix/Linux 系统中&#xff0c;正常情况下&#xff0c;子进程是通过父进程创建的&#xff0c;且两者的运行是相互独立的&#xff0c;父进程永远无法预测子进程到底什么时候结束。当一个进程调用 exit 命令结束自己的生命时&#xff…

【Linux】计算机网络协议详解与通信原理探究

目录 1、协议 1.1.初识协议 1.2.协议分层 日常通信的例子&#xff1a; 1.3.OSI 七层模型 1.4.TCP/IP五层(或四层)模型 1.5.OS和网络之间的关系 1.6.协议的本质 2.局域网通信 2.1.什么是局域网&#xff1f; 2.2.关于报文相关基础知识 报文和协议的关系&#xff1a; …

传输层协议UDP详解

目录 一. 知识准备 1.1 传输层 1.2 重识端口号 二. UDP协议 三. UDP协议特点 一. 知识准备 1.1 传输层 前面已经讲过&#xff0c;HTTP协议是应用层协议&#xff0c;在此之前&#xff0c;我们短暂的认为HTTP是直接通过应用层与外界通信的。但是我们要知道&…

4本SCI/SSCI期刊更名,10月WOS更新!速看!

期刊动态 2024年10月科睿唯安期刊目录更新 2024年10月22日&#xff0c;科睿唯安更新了WOS期刊目录&#xff0c;此次更新&#xff0c;期刊被编辑除名11本&#xff0c;停止出版1本&#xff0c;4本更名&#xff0c;停产1本&#xff0c;新增63本。 剔除期刊 11本期刊被剔 Enginee…

go-zero系列-限流(并发控制)及hey压测

参考地址&#xff1a; go-zero系列-限流(并发控制)&#xff1a;https://go-zero.dev/docs/tutorials/service/governance/limiter hey地址&#xff1a;https://github.com/rakyll/hey1、压测工具hey下载安装&#xff1a; 会安装到GOPATH/bin目录下 go install github.com/ra…

AI未来会拥有人类的情感吗,情感计算领域进展如何?

AI在情感理解方面目前还处于相对初级的阶段,但已经有了一些令人鼓舞的进展。 一般来说,AI系统可以通过自然语言处理和计算机视觉技术来检测和分析人类的情感表现,但要真正理解背后的情感原因和动机还面临很大挑战。 目前在情感计算领域&#xff0c;研究者们已经取得了显著的进…

重生之“我打数据结构,真的假的?”--1.顺序表(无习题)

C语言中的顺序表详细总结 1. 概述 顺序表&#xff08;Sequential List&#xff09;是一种线性数据结构&#xff0c;用于存储具有相同数据类型的一组元素。顺序表采用一段连续的存储空间&#xff0c;使用数组来实现&#xff0c;能够高效地支持随机访问操作。在 C 语言中&#…

Unity--AssestBundles--热更新

使用Node.js搭建AssestBundle服务器并验证AB包热更新 一、服务器部分 使用NodeJs作为服务器&#xff0c; 使用Express为基础网页模版。 当然&#xff0c; 使用其他的FTP&#xff0c;http服务器也可以&#xff0c; 基础逻辑是存放资源的位置。 1.下载Node.js 下载地址:https…

AI动漫翻唱项目玩法拆解,起号涨粉咔咔猛,实操干货分享

最近&#xff0c;一种把AI技术和动漫翻唱结合起来的视频&#xff0c;在各大平台火了起来&#xff0c;成了社交媒体的新热门。 下面&#xff0c;我们就来聊聊这种视频的制作方法和赚钱技巧&#xff0c;希望能给你的副业加点料。 一、AI动漫翻唱视频的魅力 AI动漫翻唱视频能迅…

[Luogu 4630] APIO2018 铁人两项(广义圆方树)

铁人两项 求满足存在 x → y x \rightarrow y x→y 和 y → z y \rightarrow z y→z 的不相交简单路径的有序点对 ( x , y , z ) (x, y, z) (x,y,z) 的方案数。 即&#xff0c;选择的路径只经过同一个点至多一次。 线性做法。 广义圆方树 可以解决一些“每个点至多经过…