使用GDB调试C库

用gdb调试程序时,一般的函数都可以step进去,可是C库函数却直接跳过了。

网上找了些资料,记录一下!

1.安装C库的debug版本

[plain] view plaincopy
print?在CODE上查看代码片派生到我的代码片
  1. sudo apt-get install libc6-dbg  

安装完后,在/usr/lib目录下会多出一个debug目录,里面有安装的debug版c库的动态链接文件


2.编译程序,使用debug版本C库

例如程序test.c,使用如下命令编译。

[html] view plaincopy
print?在CODE上查看代码片派生到我的代码片
  1. gcc -g -Wall test.c -o test -Wl,-rpath=/usr/lib/debug  

可以使用ldd test来查看是否使用了debug版c库。我们可以比较前后的信息

使用debug版C库输出的信息:

[plain] view plaincopy
print?在CODE上查看代码片派生到我的代码片
  1. linux-gate.so.1 =>  (0x00b65000)  
  2. libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00c94000)  
  3. /lib/ld-linux.so.2 (0x00872000)  
未使用debug版C库输出的信息:
[plain] view plaincopy
print?在CODE上查看代码片派生到我的代码片
  1. linux-gate.so.1 =>  (0x00fa9000)  
  2. libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x001af000)  
  3. /lib/ld-linux.so.2 (0x0054a000)  

可以看出成功了!

 

3.调试

[plain] view plaincopy
print?在CODE上查看代码片派生到我的代码片
  1. gdb test  


 

进入gdb后在相应位置下断点,运行到该位置后,使用s,发现能进入c库,但是找不到c库源码,呵呵

原来还要下载对应版本的c库源码。如何查看c库版本呢? 使用如下命令:

[plain] view plaincopy
print?在CODE上查看代码片派生到我的代码片
  1. ll /usr/lib/debug/i386-linux-gnu/libc*  


知道了对应的版本后,去glibc官网去下载吧:http://ftp.gnu.org/gnu/glibc/

 

有了源码,在gdb中用directory命令指定对应文件所在目录,调试时即可看到源码。

 

参考链接: http://blog.csdn.net/summerhust/article/details/5966751


时间: 2015-11-12 10:38:00

最近在研究动态链接原理这块,想通过GDB跟踪动态链接器(ld-Linux.so.2)是如何工作的,发现Ubuntu提供的/usr/lib/debug并不能很好的工作,跟踪进去后,发现源码不是对不上,就是错误的,所以萌发了自己编译C库的想法,以下是我的操作记录,欢迎指正。

开始前,需要确保你的磁盘剩余空间不小于3G空间,你不会想到编译调试版本的C库需要这么大的磁盘空间。

首先下载源码,我的系统是Ubuntu 15.10,使用sudo apt-get source libc6-dbg下载的C库版本是glibc-2.21。我的源码目录是~/libc-dbg/glibc-2.2.1。

在INSTALL编译安装说明中说,C库不能在源码目录安装,所以我在home目录下新建立了一个目录用于编译C库,目录为~/libc。又建立了一个~/lib目录用于最后的C库安装目录。

好,进入~/libc,输入../libc-dbg/glibc-2.21/configure --prefix=/home/astrol/lib CFLAGS="-O1 -g3 -ggdb" CXXFLAGS="-O1 -g3 -ggdb" --disable-werror

注意,我为了调试,所以加了-g3 -ggdb调试选项,-Ox是必须得,因为C库必须要指定,还有最后的--disable-werror也是必须得,否则会将编译过程中的很多警告信息归为错误,那么就没法继续编译了。这里我只是根据我自身的要求加的几个选项,你也可以根据自己的需求自行添加,参考../libc-dbg/glibc-2.21/configure --help的提示帮助。

根据上面命令的结果提示,看能否通过,如果不行就尽量想办法满足它,比如在configure过程中提示我系统需要gawk,那么我就sudo apt-get install gawk来满足它就OK了。

到了这里,就开始编译吧,键入make,接下来就等吧,要很久的。

最后make install,就将编译好的库安装到我指定的~/lib中。

进入~/lib,哬,文件还真多,咦,怎么没有生成的库呢,仔细一看,原来所有的库都在子目录lib下:


这些都是带有符号信息的动态库。 好了,我们写个hello world看如何使用它们。

gcc -g -o hello hello.c

然后ldd hello,输出如下

        linux-gate.so.1 =>  (0xb7732000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7563000)
        /lib/ld-linux.so.2 (0x80080000)

看来这样编译不行,根本没用上我编译好的哪些库,改变编译参数 gcc -Wl,-rpath,/home/astrol/lib/lib -Wl,--dynamic-linker,/home/astrol/lib/lib/ld-linux.so.2 -g -o hello hello.c

或者gcc -Wl,-rpath=/home/astrol/lib/lib -Wl,--dynamic-linker=/home/astrol/lib/lib/ld-linux.so.2 -g -o hello hello.c,其实都是一样的。

再ldd hello,输出如下:

        linux-gate.so.1 =>  (0xb7732000)
        libc.so.6 => /home/astrol/lib/lib/libc.so.6 (0xb758a000)
        /home/astrol/lib/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x8001d000)

看来OK了,我们再使用readelf确认下,使用readelf --program-headers hello输出:

Elf file type is EXEC (Executable file)
Entry point 0x8048340
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R E 0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00023 0x00023 R   0x1
      [Requesting program interpreter: /home/astrol/lib/lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x005f4 0x005f4 R E 0x1000
  LOAD           0x000f00 0x08049f00 0x08049f00 0x00120 0x00124 RW  0x1000

看来都可以了。

现在使用gdb调试我们的hello。gdb hello -q进入调试。使用set verbose on打开gdb信息打印,可以更好的看到调试信息。

astrol@astrol:~/test$ gdb hello -q
Reading symbols from hello...done.
(gdb) set verbose on
(gdb) start
Temporary breakpoint 1 at 0x804843c: file hello.c, line 5.
Starting program: /home/astrol/test/hello
Reading symbols from /home/astrol/lib/lib/ld-linux.so.2...done.
Reading symbols from system-supplied DSO at 0xb7fdd000...(no debugging symbols found)...done.
Reading in symbols for dl-debug.c...done.
Reading in symbols for rtld.c...done.
Reading symbols from /home/astrol/lib/lib/libc.so.6...done.

Temporary breakpoint 1, main () at hello.c:5
5               printf("hello world\n");
(gdb)

gdb成功加载了两个库和它们的符号信息。那么接下来的调试就能很好的继续了。这里我演示下printf的工作过程,观察下PLT的大致工作过程。

(gdb) disassemble /m
Dump of assembler code for function main:
4       {
   0x0804842b <+0>:     lea    0x4(%esp),%ecx
   0x0804842f <+4>:     and    $0xfffffff0,%esp
   0x08048432 <+7>:     pushl  -0x4(%ecx)
   0x08048435 <+10>:    push   %ebp
   0x08048436 <+11>:    mov    %esp,%ebp
   0x08048438 <+13>:    push   %ecx
   0x08048439 <+14>:    sub    $0x4,%esp


5               printf("hello world\n");
=> 0x0804843c <+17>:    sub    $0xc,%esp
   0x0804843f <+20>:    push   $0x80484e0
   0x08048444 <+25>:    call   0x8048300 <puts@plt>
   0x08048449 <+30>:    add    $0x10,%esp


6               return 0;
   0x0804844c <+33>:    mov    $0x0,%eax


7       }
   0x08048451 <+38>:    mov    -0x4(%ebp),%ecx
   0x08048454 <+41>:    leave
   0x08048455 <+42>:    lea    -0x4(%ecx),%esp
   0x08048458 <+45>:    ret

End of assembler dump.

地址0x8048300就是puts的PLT入口处。跟踪进去

(gdb) disassemble /m 0x8048300
Dump of assembler code for function puts@plt:
   0x08048300 <+0>:     jmp    *0x804a00c
   0x08048306 <+6>:     push   $0x0
   0x0804830b <+11>:    jmp    0x80482f0
End of assembler dump.

继续跟进,最后jmp到0x80482f0,可以通过x命令看到0x80482f0处的指令如下:

(gdb) x/3i $eip
=> 0x80482f0:   pushl  0x804a004
   0x80482f6:   jmp    *0x804a008
   0x80482fc:   add    %al,(%eax)

继续jmp到*0x804a008,这就是_dl_runtime_resolve函数的地址,它是最终进入_dl_fixup函数的“跳板”。继续跟进,看最后进入_dl_fixup函数后效果如何。

最终进入_dl_fixup函数后,发现是很正常的,gdb能很好的进行源码级调试,不会出现Ubuntu提供的/usr/lib/debug出现的哪些情况了,即行号和源码是一一对应的。

好了,本文就到此结束吧。

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

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

相关文章

matlab imfilter对图像进行滤波

功能&#xff1a;对任意类型数组或多维图像进行滤波。 用法&#xff1a;B imfilter(A,H)    B imfilter(A,H,option1,option2,...)    或写作g imfilter(f, w, filtering_mode, boundary_options, size_options) 其中&#xff0c;f为输入图像&#xff0c;w为滤波掩模&…

MapStruct:将数据从一个bean传输到另一个bean

将数据从一种形式转换为另一种形式在IT行业中是一种被高度利用的概念。 MapStruct通过在编译时生成映射器实现&#xff0c;允许基于注释的Bean转换。 这样可以确保在运行时没有性能开销。 什么是MapStruct&#xff1f; MapStruct是一个代码生成器&#xff0c;它基于约定优于配…

eclipse发布rest_在Eclipse中高效运行HTTP / REST集成测试

eclipse发布rest最近&#xff0c;我有机会使用由我亲爱的Holger Staudacher编写的OSGi-JAX-RS-Connector库。 通过连接器&#xff0c;您可以通过将Path注释的类型注册为OSGi服务来轻松发布资源-实际上&#xff0c;它工作得很好。 对我来说&#xff0c;使用普通的JUnit测试编写…

gdb调试命令

本文主要参考自&#xff1a;http://www.cnblogs.com/zzx1045917067/archive/2012/12/26/2834310.html&#xff0c;进行了一点补充和编排&#xff1b;Core dump部分参考了&#xff1a;http://blog.ddup.us/?p176。 gdb是一个在UNIX环境下的命令行调试工具。 如果需要使用gdb调试…

分享一个windows下检测硬件信息的bat脚本

文件名必须以.bat结尾&#xff0c;如果出现闪退&#xff0c;请右击鼠标&#xff0c;以管理身份运行即可 echo offcolor 0atitle 硬件检测 mode con cols90sc config winmgmt start auto >nul 2<&1net start winmgmt 2>1nulsetlocal ENABLEDELAYEDEXPANSIONecho 主…

matlab imfinfo返回图像信息

语法&#xff1a; info imfinfo(filename,fmt) %输入图像名&#xff0c;图像的格式 info imfinfo(filename)%输入图像名 示例程序&#xff1a; info imfinfo(C:\test1.jpg) %返回图像信息&#xff0c;注意&#xff1a;输入必须字符串 info.Width …

Apache Camel 2.18发布–包含内容

本周发布了Apache Camel 2.18.0 。 此版本是重要版本&#xff0c;我将在此博客文章中重点介绍。 Java 8 Camel 2.18是要求Java 1.8的第一个发行版&#xff08;例如&#xff0c;容易记住的Camel 2.18 Java1.8。Camel2.17 Java 1.7&#xff09;。 我们采取了谨慎的方法&…

C# 中 FindControl 方法及使用

FindControl 的使用方法 FindControl (String id)&#xff1a; 在页命名容器中搜索带指定标识符的服务器控件。&#xff08;有点类似javascript中的getElementById(string)&#xff09; 今天做了一个打印的报表 &#xff0c;要求在指定位置显示列表中某字段的内容&#xff0c;…

matlab imresize对图像进行缩小放大

matlab中函数imresize简介&#xff1a; 函数功能&#xff1a;该函数用于对图像做缩放处理。 调用格式&#xff1a; B imresize(A, m) 返回的图像B的长宽是图像A的长宽的m倍&#xff0c;即缩放图像。 m大于1&#xff0c; 则放大图像&#xff1b; m小于1&#xff0c; 缩小图像。…

matlab imrotate图像旋转

B imrotate(A,angle) 将图像A&#xff08;图像的数据矩阵&#xff0c;既可以是灰度图像&#xff0c;也可以是RGB图像&#xff09;绕图像的中心点旋转angle度&#xff0c; 正数表示逆时针旋转&#xff0c; 负数表示顺时针旋转。返回旋转后的图像矩阵。 B imrotate(A,angle,met…

理解爬虫原理

1.简单说明爬虫原理 爬虫就是通过互联网各个沾点组成的节点网&#xff0c;通过代码返回给浏览器&#xff0c;然后解析这部分的代内容&#xff0c;将网页内的内容简洁地呈现在我们的面前。爬虫的流程可以分为&#xff1a;发送请求、获取响应内容、解析内容、保存数据。 2.使用 r…

带有Java DSL的Spring Integration MongoDB适配器

1引言 这篇文章解释了如何使用Spring Integration从MongoDB数据库中保存和检索实体。 为了实现这一点&#xff0c;我们将使用Java DSL配置扩展来配置入站和出站MongoDB通道适配器。 例如&#xff0c;我们将构建一个应用程序&#xff0c;使您可以将订单写入MongoDB存储&#xff…

matlab linspace

用法&#xff1a;linspace(x1,x2,N)   功能&#xff1a;linspace是Matlab中的一个指令&#xff0c;用于产生x1,x2之间的N点行矢量。其中x1、x2、N分别为起始值、中止值、元素个数。若缺省N&#xff0c;默认点数为100。在matlab的命令窗口下输入help linspace或者doc linspac…

Linux strace命令

简介 strace常用来跟踪进程执行时的系统调用和所接收的信号。 在Linux世界&#xff0c;进程不能直接访问硬件设备&#xff0c;当进程需要访问硬件设备(比如读取磁盘文件&#xff0c;接收网络数据等等)时&#xff0c;必须由用户态模式切换至内核态模式&#xff0c;通 过系统调用…

网站发布

1.文件发布 右击工程&#xff0c;选择发布 发布方法选择文件发布&#xff0c;打开你的程式路径&#xff0c;然后一步步操作即可。 转载于:https://www.cnblogs.com/alannxu/p/10613453.html

什么是javax.ws.rs.core.context? [第4部分]

如何使用Context批注 在什么是javax.ws.rs.core.context的第3部分中&#xff1f; 您学习了如何在请求和配置&#xff0c;提供程序和应用程序实例中使用Context批注。 在本文中&#xff0c;您将学习如何使用Context批注注入HttpServletResponse和HttpServletRequest类。 获取对…

matlab im2double

im2double函数&#xff0c;如果输入是 uint8 unit16 或者是二值的logical类型&#xff0c;则函数im2double 将其值归一化到0&#xff5e;1之间。

重学前端(一)

前端知识框架&#xff1a;自己觉得很不错的一个前端知识框架 转载于:https://www.cnblogs.com/angel1254/p/10616065.html

couchbase_Couchbase:使用Twitter和Java创建大型数据集

couchbase在播放/演示Couchbase或任何其他NoSQL引擎时&#xff0c;创建大型数据集的一种简单方法是将Twitter feed注入数据库。 对于这个小应用程序&#xff0c;我正在使用&#xff1a; Couchbase Server 2.0服务器 Couchbase Java SDK &#xff08;将由Maven安装&#xff0…

C编译器、链接器、加载器详解

一、概述 C语言的编译链接过程要把我们编写的一个c程序&#xff08;源代码&#xff09;转换成可以在硬件上运行的程序&#xff08;可执行代码&#xff09;&#xff0c;需要进行编译和链接。编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程。链接是把目标文件、操作…