【windbg】用WinDbg探索ruby的奥秘

写这篇文章是受[url="http://www.iteye.com/topic/740031#1632230"]从main.c开始走进Ruby-登上调试Ruby之旅》[/url]的启发,不同的是该文章用的是GDB,GDB虽然很强大,但是毕竟是命令行,在调试的时候,可能同时需要查看许多信息,比如call statck,汇编代码,源代码等等,命令行就有点力不从心,所以续写一篇,改GDB为同样强大的windbg,以便更方便的探索Ruby的内部奥秘。

这里的主旨不是ruby,而是针对Windbg的调试技巧,所用的方法同样适用于其他C/C++应用,ruby在这里只是做实验用的小白鼠。

[b]从编译开始[/b]

要进行源码调试,必须让编译器或链接器在构建二进制文件时生成符号文件(.pdb文件)。这些符号文件保存了二进制指令和源码行之间的对应关系。

另外,调试器必须能够访问源码文件,因为符号文件中并不包含实际的源代码文本。

如果这些都满足,编译器和链接器还不能对代码进行优化。如果代码经过优化,在源码调试时访问局部变量会变得很困难,有时候几乎是不可能的。如果使用Build 实用程序作为编译器和链接器,可以将MSC_OPTIMIZATION 宏设置为/Od /Oi 来避免优化。

ruby编译过程可以参考:[url="http://raylinn.iteye.com/admin/blogs/650850"]这篇文章[/url]
不同的是需要修改下ruby的makefile以便输出debug信息。
[code]
CFLAGS = /Od -MD $(DEBUGFLAGS) $(OPTFLAGS) $(PROCESSOR_FLAG) /DEBUG /Zi
LDFLAGS = $(LDFLAGS) -manifest /DEBUG
[/code]

指定 /ZI 或 /Zi 而不指定 /Fd 时,VC++最终将生成两个 PDB 文件:
[list]
[*]VCx0.PDB (其中 x 表示 Visual C++ 的版本。)该文件存储各个 OBJ 文件的所有调试信息并与项目生成文件驻留在同一个目录中。
[*]project.PDB 该文件存储 .exe 文件的所有调试信息,它包含了调试中需要用到的各种数据,例如:全局变量、本地变量、函数名、函数类型、源代码行、程序入口地址.....,这些所有的东西都叫做Symbol。
[/list]

[b]PDB文件说明[/b]
PDB文件全称是程序数据库 (PDB) 文件,它保存着调试和项目状态信息,使用这些信息可以对程序的调试配置进行增量链接。

每当创建 OBJ 文件时,C/C++ 编译器都将调试信息合并到 VCx0.PDB 中。插入的信息包括类型信息,但不包括函数定义等符号信息。

链接器将创建 project.PDB,它包含项目的 EXE 文件的调试信息。project.PDB 文件包含完整的调试信息(包括函数原型),而不仅仅是在 VCx0.PDB 中找到的类型信息。这两个 PDB 文件都允许增量更新。

我们知道当应用程序被链接以后,代码被逐一地翻译为一个个的地址,优化以后的代码可能初看起来更是面目全非,如果调试时,充斥着类似NTDLL! 774fe4b6() NTDLL! 774fe489()之类的调用堆栈无疑会增加调试的难度。有了PDB文件之后, 每当我们使用vs或者windbg等微软的调试工具进行调试的时候,我们可以方便地使用变量名来查看内存、可以使用函数名称来下断点、甚至可以指定某个文件的某一行来下断点。

[b]设置WinDbg的符号文件[/b]

启动WinDbg,选择菜单的file -> symbols file path,或者按ctrl+s 然后输入
[code]
srv*c:\symbols*http://msdl.microsoft.com/download/symbols
[/code]
按照这样设置,WinDbg将先从本地文件夹c:\symbols中查找Symbol,如果找不到,则自动从MS的Symbol Server上下载Symbols。

你也可以自己去下载MS提供的Windows符号文件,那么symbols file path就直接指向符号文件安装的目录就可以了。

调试器是如何来判别EXE、DLL等是否和一个pdb文件匹配呢?每次我们链接EXE或者DLL或者SYS的时候,链接器都将产生一个唯一的GUID,然后将其写入到PDB和可执行文件。调试器加载的时候将检查两者的GUID,如果一致就表示他们匹配。

[img]http://dl.iteye.com/upload/attachment/297414/61d17f0a-6e46-34ad-98d7-7968f9e6d5f8.png[/img]

如果出现无法加载符号文件的现象,通常是你的符号文件和你调试的二进制不匹配。

由于链接器在其创建的 .exe 或 .dll 文件中嵌入 .pdb 文件的路径,这里不需要在设置ruby的符号文件路径。

我们只需要为WinDbg设置源代码路径,这样可以很方便地查看ruby的源代码。

[b]常见的符号操作[/b]
查看符号路径:[b].sympath[/b]
列出加载模块: [b]lm[/b]
加载指定模块: [b]ld <module>[/b]
重新加载模块: [b].reload <module>[/b]
重新加载所有模块: [b].reload /n [/b]
列出模块详细信息: [b]!lmi <module>[/b]或[b]!db <module>[/b]
显示模块的符号信息: [b] x <module>!<symbols>[/b]
查看模块的数据结构: [b] dt <module>!<symbols>[/b]

[b]启动应用[/b]
Windbg提供了两种方式来启动应用,这里以ruby的irb为例:
open executable:ruby -x "<path>\irb.bat",这时候我们可以很方便地在任何地方设置断点,包括main函数。

也可以运行irb之后,在WinDbg里选择attach to a process, 在列出的进程中选择ruby.exe。

[img]http://dl.iteye.com/upload/attachment/295423/00d75baf-df90-3ff6-9693-500ae8554e9e.png[/img]

要探索ruby的内部奥秘,最重要的技巧是设置断点,而其中最经常使用的是未定断点。

如果一个断点是设置在某个还未加载的函数名上,则称为延迟、虚拟或未定断点。 (这些术语可交替使用。) 未定断点没有被关联到任何具体被加载的模块上。每当一个新的模块被加载时,会检查该函数名。如果这个函数出现,调试器计算虚拟断点的实际位置并启用它。

在WinDbg里设置断点的几个方法:

bu设置的断点自动被认为是未定断点。如果断点在一个已加载模块中,则会启用并正常生效。但是,如果模块之后被卸载并重新加载,这个断点不会消失。而使用bp设置的断点会立即绑定到某个地址。 如果bp的断点地址在某个已加载模块中找到,并且该模块之后被卸载,则该断点会从断点列表中移除。bm断点用法如同bu,但是bm可以支持正则表达式,用于设置多个断点。

在WinDbg中设置断点的格式有如下几种:
1. 虚拟地址:即给出直接地址,如 12345678
2. 函数偏移量:如DriverEntry+5c.
3. 源代码+行数 :`[[Module!]Filename][:LineNumber]`
4. 可以对模块中的某个类的方法设置断点。


[b]查看ruby入口点[/b]
ruby是个c编写的应用,通常入口点就是main函数,用open executable的方式启动irb
在arguments里填写上 -x "<path>\irb.bat"

[code]
bu main
g
[/code]

windbg会捕获main这个函数的调用,并同时显示出main的源代码:

[img]http://dl.iteye.com/upload/attachment/295448/b844eded-a43c-3da0-b90f-b85e0b31fcb8.png[/img]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值