反汇编定位代码崩溃位置_4

2023-05-16

原帖:http://blog.csdn.net/_xiao/article/details/23177577


标签: GDBcoredumpso调试动态库
2014-04-08 14:29  7559人阅读  评论(0)  收藏  举报

版权声明:本文为博主原创文章,未经博主允许不得转载。

[原创]转载请注明来源于CSDN _xiao。

Linux生成Coredump文件时程序并没有对动态链接库文件信息进行特殊处理,但GDB在载入Coredump文件时却能正确加载所有的动态链接库,包括程序运行中调用dlopen动态载入的so文件,其原理是什么呢?这里通过对GDB源码的略览来解开这个过程。

[关于如何设置库搜索路径以及路径搜索的优先级可参考"GDB动态库搜索路径"笔记]

GDB的大略架构:gdb.c中的main函数是程序的入口,它只是简单地调用gdb_main,后者再调用captured_maincaptured_main是执行的主体函数。它先解析命令行参数选项,再初始化所有变量和所有文件,最后是一个while循环,这个循环里不断调用captured_command_loop来获取输入的命令并执行命令所指示的动作。其中初始化时gdb_init会调用initialize_all_files函数来初始化所有文件,这个函数在编译之前是看不到的,在编译时Makefile会扫描所有源文件,将所有类型为initialize_file_ftype的函数搜集起来,放在gdb目录下新生成的init.c文件中,并在此文件中创建initialize_all_files函数,此函数依次调用每一个_initialize_xxx函数。模块在自己的_initialize_xxx函数中除了初始化自身外,还会调用add_cmd或类似的函数来注册命令,之后用户输入所注册的命令时就会调用模块的处理函数了。例如corefile.c文件在_initialize_core函数中注册了"core-file”命令,命令的处理函数是core_file_command,这样当用户敲入"core Coredump”时就会调用core_file_command函数来处理了("core”是"core-file”命令的别名)。同样exec.c注册了"file”指令,其处理函数为file_command。通常,命令"xxx”的处理函数名为"xxx_command”,例如"info sharedLibrary”命令的处理函数为info_sharedlibrary_command,根据此规则,可以快速找到命令的处理函数。

载入一个Coredump文件时,由core_file_command来处理,首先通过find_core_target来查找有能力处理"CORE”文件的target,并调用targetopen函数处理。corelow.c在初始化时注册了该类型的target,所以会进入corelow.ccore_open函数。core_open函数调用bfd_fopen函数打开该文件(bfd_fopen会识别格式并按ELF格式打开),然后调用build_section_table读入Coredump文件的所有sections信息(即segments信息),完成后调用push_targetcoretarget操作添加到target链表中(这样类似于readmemory等的一些动作就可以在Coredump文件的地址空间进行了)。最后调用post_create_inferior进行后处理,调用init_thread_list读取PT_NOTE信息段中的线程信息,调用target_fetch_registers读取寄存器信息,并根据寄存器恢复调用帧(frame),最后调用print_stack_frame打印帧信息(即backtrace),整个Coredump文件的载入就完成了。

对于如何恢复动态链接库信息,我们需要关注的是post_create_inferior函数。在这个函数里,如果在core指令之前已执行了fileexec_file命令,即已拥有了主执行程序的信息,那么就会调用solib_add来添加所有的so库。

可见,恢复动态链接库信息的前提是必须拥有Coredump文件和原始主执行程序的Binary文件,如果只有其中一个,是不能恢复动态链接库信息的。

继续看solib_add函数,它主要调用update_solib_list来更新所有的so库列表,在update_solib_list函数里,关键的地方是调用ops->current_sos函数来获取so库信息列表,而current_sos函数总是根据当前信息重建so库列表。

在不同的操作系统和体系架构上,会有不同的current_sos实现。对于工程中通常使用的ARM指令和MIPS指令上的Linux系统,会由svr4_current_sos函数来实现重建功能。

进入svr4_current_sos函数,首先调用locate_base获取调试信息的基址。它调用elf_locate_base分析主执行程序的ELF文件得到该信息。elf_locate_base先调用scan_dyntag查找类型为DT_MIPS_RLD_MAP(0x70000016)的动态信息,如果失败再调用scan_dyntag查找类型为DT_DEBUG(21)的动态信息。对于MIPS,编译器用DT_MIPS_RLD_MAP信息存放调试信息,而DT_DEBUG信息是无意义的,对于其他平台如ARM,则用DT_DEBUG信息存放调试信息,没有DT_MIPS_RLD_MAP信息。san_dyntag读取名为".dynamic”section并逐一扫描,该section的内容由dynamic section structure数组组成,每个structure由两个整数组成,第一个整数是dynamic的类型(例如DT_DEBUG),第二个整数是dynamic的值,值的意义与类型相关。scan_dyntag逐一扫描,找到类型为DT_MIPS_RLD_MAP的动态信息,然后返回其值。该值是在编译时已经计算好的,实际上其值总是名为".rld_map”section的地址。elf_locate_base会读取scan_dyntag返回的值所指向的内容,也就是".rld_map” section的内容。".rld_map” section的长度只有4字节,其内容是调试信息的基址,指向dynamic linker structs。在编译时,".rld_map”的值为0,在运行时,由加载器填写其值,加载器会维护一个dynamic linker structs,地址就放在".rld_map”中。在linux中,加载器通常是ld.so或者ld_linux.solocate_baseelf_locate_base返回的值赋给全局变量debug_base,这样debug_base就指向了dynamic linker structs。由于这个信息是运行时才有的,所以GDB只有在同时载入主执行文件和Coredump文件后才能恢复这个链表。

svr4_current_sos再调用solib_svr4_r_map从dynamic linker structs中获取link map list链表,由于不同平台上数据的组织不同,GDB在读取信息时会调用svr4_fetch_link_map_offsets等函数来获取各变量的偏移地址和尺寸,在mips中,它最终会通过svr4_ilp32_fetch_link_map_offsets提供的信息来解析结构体的数据。在这里r_map_offset的信息为4,所以solib_svr4_r_map从debug_base + 4的地方读取link map list信息,这样就得到了整个链接映射表的头指针。

然后svr4_current_sos开始遍历link map list,这里链表每个元素为20字节,其大概的结构如下(在不同平台上其大小和位置是不同的):

      U32 l_addr;       // 4 bytes

      U32 l_name;      // 4 bytes

      U32 l_ld;           // 4 bytes

      U32 l_next;       // 4 bytes

      U32 l_prev;       // 4 bytes

GDB从Coredump文件中读取链表所在内存中的值,l_name是模块名称的地址,从所指地址即可读出so库文件的名称,l_addr则是模块的加载地址,l_next是下一个模块链接信息的地址,svr4_current_sos逐一遍历,将所有so库文件的名称和信息重建为struct so_list结构的链表,最后返回这个链表。

之后回到update_solib_list函数,这个函数扫描从current_sos返回的so库链表,检查哪些so库已加载,哪些so库需要重新加载,哪些已加载的so库需要卸载掉,然后对每一个需要加载的so库调用solib_map_sections将这些so库映射到target的内存空间。加载so库时会调用tilde_expand和solib_open来扩展库文件名,如果设置了正确sys_root路径和库搜索路径,库就能正确找到和加载。

在所有库都加载到target的内存空间后,整个进程的内存镜像就恢复到Coredump时的状况了,然后就可以观察Coredump时的变量和状态信息了。

GDB加载动态库信息的过程示意如图1所示。

库文件信息加载过程示意图

图(1)库文件信息加载过程示意图

 

下面用一个测试例子来描述库信息的恢复过程。

该示例程序由两个文件组成,一个主程序,一个动态so库,主程序调用动态so库里的一个函数,动态库里的函数操作一个空指针以生成Coredump。

主程序,编译后生成gdbso:

[cpp]  view plain  copy
  1. int main()  
  2. {  
  3.     int ires = 0;  
  4.   
  5.     LPFun lpFun = NULL;  
  6.   
  7.     void *pHandle = dlopen("./libddd.so", RTLD_LAZY);  
  8.   
  9.     if (NULL == pHandle)  
  10.     {  
  11.         printf("open libddd.so failed\n");  
  12.         return 1;  
  13.     }  
  14.     else  
  15.     {  
  16.         printf("open libddd.so success\n");  
  17.     }  
  18.   
  19.     lpFun = (LPFun)dlsym(pHandle, "fun_dll");  
  20.     if (NULL == lpFun)  
  21.     {  
  22.         printf("dlsym failed\n");  
  23.         return 2;  
  24.     }  
  25.   
  26.     ires = lpFun();  
  27.   
  28.     dlclose(pHandle);  
  29.   
  30.     return 0;  
  31. }  

动态库程序,编译后生成libddd.so:

[cpp]  view plain  copy
  1. int fun_dll()  
  2. {  
  3.     void *pTmp = NULL;  
  4.     printf("In dll\n");  
  5.   
  6.     memcpy(pTmp, 0, sizeof(100));  
  7.   
  8.     return 1;  
  9. }  

编译主程序和动态库,在MIPS平台上运行生成Coredump,然后用GDB载入主程序gdbso和Coredump文件,载入前使用set sys_root和set solib_search_path设置正确的库搜索路径。

在GDB中,使用"set debug target 10”可以打开加载target时的调试信息,观察GDB是如何加载文件的。

根据CORE加载过程,GDB会读取主程序gdbso的".dynamic” section内容,我们使用objdump –h指令查看gdbso的section信息,如图2所示。

gdbso的objdump结果

gdbso的objdump结果

图(2)gdbso的objdump结果

从objdump的结果看到,.dynamic section在文件中的偏移地址为0x017C,在加载后内存中的地址为0x0040017C,这段数据是只读的,所以在内存中的数据与文件中的数据是相同的。我们在GDB中通过"x /28w 0x0040017c”查看.dynamic section的内容,如图3所示。

.dynamic section的内容

图(3).dynamic section的内容

从.dynmaic section的内容看到,地址0x004001dc就是DT_MIPS_RLD_MAP信息,其类型为0x70000016,值为0x00410ac0,这刚好是.rld_map section的地址,与前文所述一致。

再使用"x /w 0x00410ac0”查看.rld_map section的内容,如图4所示。

.rld_map section的内容

图(4).rld_map section的内容

可以看到,.rld_map section的内容为0x2aad7a10(该section是可写的,在文件中的值为0x00000000,在Coredump载入后内存中的值为0x2aad7a10),所以dynamic linker structs的基地址为0x2aad7a10。

使用"x /4w 0x2aad7a10”查看dynamic linker structs的内容,如图5所示。

dynamic linker structs的部分内容

图(5)dynamic linker structs的部分内容

根据前文分析,在dynamic linker structs中,偏移地址为4的地方就是link map list的地址。所以图5中链接映射表(link map list)的头指针为0x2aad7a28。链表的每个元素是20个字节,使用"x /8w 0x2aad7a28”查看第一个链表元素的内容,如图6所示,注意其中只有前20个字节是有效的。

link map第一个元素的内容

图(6)link map第一个元素的内容

从图6看到,第一个链表元素的l_addr为0x00000000,l_name为0x2aac47e8,l_ld为0x0040017c,l_next为0x2aac75f8,l_prev为0x00000000。此模块的加载地址为0x00000000,表示是主程序gdbso的模块信息,所以忽略掉它,看下一个链表元素。

使用"x /8w 0x2aac75f8”查看第二个链表元素的内容,如图7所示。

link map第二个元素的内容

图(7)link map第二个元素的内容

从图7看到,第二个链表元素的l_addr为0x2aad8000,l_name为0x2aac75e8,l_ld为0x2aad818c,l_next为0x2aac7958,l_prev为0x2aad7a28。该模块的加载地址为0x2aad8000,模块名称地址为0x2aac75e8。使用"x /4w 0x2aac75e8”和"x /s 0x2aac75e8”查看该地址的内容,如图8所示,可以看到,该模块的名称为"/lib/librt.so.1”。

link map第二个元素的模块的名称

图(8)link map第二个元素的模块的名称

按上面的方式,继续根据l_next浏览链表中的每一个模块,直到l_next为0x00000000,如图9所示。

link map后续元素的解析

图(9)link map后续元素的解析

从图9看到,整个link map list,包含了"/lib/librt.so.1”、"/lib/libm.so.6”、"/lib/libpthread.so.0”、"/lib/libc.so.6”、"/lib/libdl.so.2”、"/lib/ld.so.1”、"./libddd.so”共7个模块的信息。

从最后一个元素看到,动态库libddd.so被加载到地址0x2ad1a000处,这是整个模块的加载地址,并不是其代码段的加载地址。我们使用objdump查看libddd.so的.text段的偏移信息,如图10所示。

libddd.so的objdump结果

图(10)libddd.so的objdump结果

从图10看到,libddd.so的.text在内存中的偏移为0x0590,所以该模块加载到地址0x2ad1a000之后其代码段会被加载到0x2ad1a590处。

我们用"info sharedlibrary”查看GDB解析的结果,和我们的分析是一致的,如图11所示。

GDB的info sharedlibrary结果

图(11)GDB的info sharedlibrary结果

 至此,整个so库信息加载过程就完成了。

0

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

反汇编定位代码崩溃位置_4 的相关文章

  • Fatfs文件系统,f_open函数返回值为FR_DISK_ERR解决方法

    最近在操作TF卡 xff0c 芯片stm32f103c8t6 xff0c 编译环境KEIL xff0c 金士顿32G卡 xff0c 用Fatfs文件系统向卡中写入数据 出现的问题 xff1a f open函数返回值为FR DISK ERR
  • Fatfs文件系统向文件写内容出现f_write返回值为1的问题

    f write返回值为1 xff0c 则就是FR DISK ERR 1 A hard error occurred in the low level disk I O layer 低级磁盘I O层中发生硬错误 问题解决方式 xff1a 1
  • vl53l1x激光测距讲解

    使用模块 ATK VL53L0X激光测距模块或者淘宝其他模块 通信方式 xff1a IIC xff0c 接口SHUT用于开机启动时序中 xff0c int是中断模式中的引脚 xff08 触发中断 xff09 参考资料 xff1a https
  • 如何完成一篇发明专利

    专利的组成部分 xff1a 说明书摘要摘要附图权利要求书说明书说明书附图 参考的文献有 专利法 专利审查指南 xff0c 大致写完一篇发明专利需要半个月的时间 xff1b 参考网址 xff0c http www soopat com htt
  • cmd python 缩进 3个点

    问题描述 xff1a indentationerror expected an indented block for 语句和if语句都会遇到 xff0c 解决方法是for 语句和if语句冒号后 xff0c 按enter切换下一行 xff0c
  • 陀螺仪和加速度计MPU6050的单位换算方法

    对于四轴的初学者 xff0c 可能无法理解四轴源代码里面陀螺仪和加速度数据的那些数学转换方法 下面我们来具体描述下这些转换方法 我们首先来看陀螺仪数据 在MPU6050的手册里面 xff0c 提供了一个陀螺仪数据表如下 xff1a 在表格里
  • 【Final Project】Kitti的双目视觉里程计(1)

    1 从CMake文件了解整体结构 xff08 1 xff09 前置工作 0 xff09 文件结构 app CMakeLists txt run kitti stereo cpp CMakeLists txt cmake modules Fi
  • 觅香

    立于浮华之世 奏响天籁之音
  • 多旋翼无人机推荐书

    惯性仪器测试与数据分析 惯性导航 xff08 秦永元 xff09 先进 PID 控制 MATLAB 仿真 多旋翼飞行器设计与控制
  • 飞控PID详解

    串级PID xff1a 单极PID适合线性系统 xff0c 当输出量和被控制量呈线性关系时单极PID能获得较好的效果 xff0c 但是四轴不是线性系统 xff0c 现代学者认为 xff0c 四轴通常可以简化为一个二阶阻尼系统 为什么四轴不是
  • Keil:ST-LINK USB communication error

    error flash download failed target dll has been cancelled 1 USB口的问题 xff1a USB供电不好 xff0c 或则USB驱动程序或ST Link驱动程序有问题 我的解决方案就
  • Cadence OrCAD BOM如何输出封装信息

    Cadence OrCAD 如何输出带封装信息的BOM 1 选中DSN文件 xff0c 打开Tools菜单中 选择Bill of materials选项 2 Bill of materials对话框设置如下 3 ORCAD输出的BOM表是文
  • 随机排列算法及《算法导论》5.3节习题解答

    随机排列算法及 算法导论 5 3节习题解答 算法导论 介绍了两种随机排列数组的算法 第一种算法是为数组的每个元素A i 赋一个随机的优先级P i xff0c 然后依据优先级对数组A中的元素进行排序 例如 xff0c 如果初始数组A 61 1
  • 【Ubuntu-Tensorflow】GPU设置及显存资源分配

    最近笔者在做GPU显存资源分配的研究 xff0c 发现在tf中gpu一些实用的方法和接口 xff0c 共享出来 xff0c 供大家参考学习 xff0c 如有问题 xff0c 欢迎留言讨论 1 运行程序时 xff0c 控制台设置GPU运行参数
  • 为了解决jetson tx2的内存不足。挂载sd卡,并且使用docker在sd中安装jetPack的镜像。

    1 xff0c 使用nvidia官方的sdkmanager工具给jetson tx2刷机 xff0c 并且将sd卡挂载在系统目录下 参考ubuntu18 04主机 43 Jetson TX2 NX刷机 lgh15897723511的博客 C
  • 链表反转 - 链表排序 算法

    链表反转 xff1a 想象有1个新链表 xff0c 每次从旧链表取出一个元素 xff0c 然后插入到新链表的头部 链表排序 xff1a 先将链表拆分为2个子链表 使用快慢指针 xff0c 快指针每次走2步 xff0c 当快指针走到尾部时 x
  • 如何在Ubuntu服务器上安装桌面环境(GUI)

    本文转载至网络 原作者Chris Patrick Carias 你想在你的 Ubuntu 服务器上安装 GUI 吗 xff1f 大部分情况下你是可以安装的 xff0c 在本教程中我会详细介绍安装的步骤 在正式开始之前 xff0c 我来告诉你
  • 平衡小车PID学习

    离散式PID xff1a 位置环 xff1a 入口参数 xff1a 位置测量值 xff0c 编码器的位置测量值 系统的参数调定要求 xff1a 目标 xff1a 准确性 xff0c 稳定性 xff0c 快速性 指标 xff1a 最大超调量
  • arm64汇编篇-04堆与栈的关系

    栈 xff1a 栈是一种具有特殊的访问方式的存储空间 xff08 后进先出 xff0c Last In Out Firt xff0c LIFO xff09 堆 xff1a 堆用于动态分配和释放程序所使用的对象 xff08 这边不详细介绍堆
  • 关于STM32平衡小车的几个关键疑问

    引用文章 一 关于姿态融合 首先需要提一点关键的 xff0c 如果是采用卡尔曼滤波或者互补滤波 xff0c 尽可能把采样时间调小 xff0c 这样可以增加滤波的效果 建议采样时间不要高于10ms xff0c 起初我在测试的时候采样时间设置为

随机推荐

  • STM32F103C8T6系统板

    目录 组成1 电源部分2 复位3 晶振4 电源部分电源电路 防反接电源电路 电源芯片输入滤波电容的作用 xff1a 输出滤波电容的作用 xff1a 大电容和小电容并联的作用 xff1a 电源电路 MCU引脚部分等VCC3 3与VCC3 3M
  • PyTorch实现 | 车牌OCR识别,《PyTorch深度学习之目标检测》

    注 xff1a 本文选自中国水利水电出版社出版 PyTorch深度学习之目标检测 一书 xff0c 有改动 福利 xff01 免费寄送图书 xff01 xff01 公众号 机器学习与AI生成创作 后台回复 xff1a 168 即可参与免费寄
  • putty不能连接vultr服务器,提示network error:connection refused怎么解决

    部署服务器时第四个选项的第1个和第4个选中重新部署 如下图
  • 推荐一些IEDA,Eclipse十分好用的插件

    推荐一些IEDA xff0c Eclipse十分好用的插件 一 IDEA插件1 常用插件 xff1a 2 代码质量插件 xff1a 3 其他插件 xff1a 二 Eclipse 插件 xff1a 一 IDEA插件 1 常用插件 xff1a
  • VSCode git 配置心得

    一 Git是一个分布式的代码版本管理工具 类似的常用工具还有SVN CVS 最大的特点也是优点在于提供分布式的代码管理 1 分支代码只有一份 xff01 使用过svn的童鞋想必都知道 xff0c 当我们要开发一个新功能或者增加一个新版本或者
  • AD、PADS、Cadence哪个最好用?

    用哪个工具不是你说了算 案例1 xff1a 老板 xff0c Allegro我不会用啊 xff0c 我只会用Power PCB 好 xff0c 你去找财务结算工资吧 xff01 案例2 xff1a 小伙子 xff0c 你会用什么工具啊 xf
  • git 使用规则

    Linus为Linux Kernel Project发起的版本控制项目 HEAD代表当前最新状态 tag为某个状态的标签 SHA1为每个提交日志的唯一标识 install apt get install git core git clone
  • 两分钟教你学会用示波器测量串口波特率

    首先接好线 xff0c 黑表笔接地线 xff0c 灰表笔接串口TX数据线 接着打开示波器 xff0c 按下AUTO xff0c 自动测量波形 接着按下AUTO上面的STOP键 xff0c 冻结画面 按下CURSORS xff0c 打开光标模
  • Visual Studio 与Visual C++ 有什么区别

    Visual C 43 43 是 Visual Studio的一个部分 xff0c 此外还有 Visual Basic xff08 VB xff09 Visual C 等 VC 43 43 6 0 是VS6的 VC 43 43 2003 是
  • STM32Cube的串口设置(一)即学即用

    串口系列 STM32Cube的串口设置 xff08 一 xff09 即学即用 STM32Cube的串口设置 xff08 二 xff09 一个串口接收另一个串口发送 串口实战 STM32Cube的串口实战 xff08 一 xff09 GPS
  • C++中构造函数后面接单冒号是什么意思?

    构造函数后 xff0c 接单冒号表示初始化列表 具体形式为 对于class TEST xff0c 存在成员变量int a b c 那么 TEST int x int y a x b y c 0 的效果就是用括号内的值 xff0c 来初始化成
  • 训练神经网络中最基本的三个概念:Epoch, Batch, Iteration

    转载地址 xff1a https zhuanlan zhihu com p 29409502 原作者 xff1a Michael Yuan 作者主页 xff1a https www zhihu com people mikeyuan 今天让
  • 使用makefile编译freeRTOS

    freeRTOS的文件结构 FreeRTOS LabsFreeRTOS Plus 包含freeRTOS 43 的组件和demo项目FreeRTOS 包含内核和demo项目 Source目录 xff1a 三个必须文件list c queue
  • 2013 一路走过

    2013 一路走过 想起当初找工作的时候 xff0c 一个人早上坐火车跑到其他大学的招聘会上去逛一圈 xff0c 了解招聘情况 然后下午又坐火车回学校 记得那天我投了十几份简历出去 xff0c 本打算投着试试 xff0c 没想到回来后有几家
  • 编译vs2008的samples程序总是跳过

    编译vs2008的samples程序总是跳过 xff0c 要配置属性还显示 未能完成操作 未指定的错误 的解决办法 作者 admin 分类 开发问题 发布时间 2013 03 12 09 22 974 浏览数 6 没有评论 文章转自王牌软件
  • MFC 用户界面线程:界面线程的退出 窗口关闭的流程

    原文链接 xff1a http wenku baidu com link url 61 6CFkWbLOeFgNoUsJniCX3ksw6 RztxMr9Z e6t7uu3e vV7UTKThUEkyRkq8IXwxIw5qYctN8gIx
  • MFC用户界面多线程实例2

    以下是 MFC 用户界面线程相关知识 由于用户界面线程含有自己的消息循环 xff0c 可以出来 Windows 消息 xff0c 并可创建和管理诸如窗口和控件等用户界面 元素 因此 xff0c 这 种线程较工程线程更为复杂 创建用户界面线程
  • 反汇编定位代码崩溃位置_1

    原帖 xff1a http blog csdn net gwzz1228 article details 9045853 利用map xff0c cod文件定位崩溃代码行 利用vs2010 新建一个空的控制台项目 xff0c 添加文件gtg
  • 反汇编定位代码崩溃位置_3

    原帖 xff1a http blog sina com cn s blog 141f234870102van8 html win7 43 vs2010通过map文件和cod文件找到崩溃的代码行 2015 01 11 11 31 04 转载
  • 反汇编定位代码崩溃位置_4

    原帖 xff1a http blog csdn net xiao article details 23177577 GDB如何从Coredump文件恢复动态库信息 标签 xff1a GDBcoredumpso调试动态库 2014 04 08