Windows调试的基石——符号(1)

当应用程序被链接以后,代码被逐一地翻译为一个个的地址,优化以后的代码可能初看起来更是面目全非。每当我们使用vs或者windbg等微软的调试工具进行调试的时候,我们可以方便地使用变量名来查看内存、可以使用函数名称来下断点、甚至可以指定某个文件的某一行来下断点。这一切背后是什么在指导调试器工作呢?答案就是符号——pdb或者dbg文件(.NET自己有元数据,符号不需要元数据已有的信息)。

  程序运行的时候,计算机只需要逐条执行指令即可。而与源代码对应的关系是完全不需要知道的。这就给调试带来了困难,所以无论什么编译都有自己的一套用于对应代码和可执行程序。各种编译器都有自己保存类似这种对应关系的办法,有的直接嵌入可执行文件,有的则是独立出来的。而微软的编译器则是独立产生了这种文件,它就被成为符号文件。

  符号文件的历史有兴趣可以网上查查,dbg文件十分古老,微软在新的产品中也不再使用了。所以今天我们新产生的符号文件一般都是pdb文件。而pdb可以理解成提供给调试器用于对应可执行文件和源代码的东西,这个东西运行的时候是没有任何作用的,但是对于调试器和我们调试则有很大的帮助。

  那么pdb文件里面到底存储了什么东西呢?根据微软官方的解释有:

1、  全局变量;

2、  局部变量;

3、  函数名及入口点;

4、  FPO记录;

5、  源代码行号。

  如果使用vs2010随便写一个本机C语言,那么链接的时候编译器就会帮我们产生一个pdb文件。里面包含大量的符号,包括上面提及的内容。

每次调试程序或者查dmp的时候,我们都必须使用正确的符号。否则我们看到的栈等信息可能不准确。同时我们也无法建立应用程序和源代码之间的关系,没有符号你所面对只有地址。

符号同时又分为两种:public symbols and private symbols。至于他们的区别以后再具体介绍。

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

  很多时候我们对PDB都不够重视。如果足够自信发布的东西一定不会产生bug,而且确实也没有产生bug,或者用户为0。那么PDB对我们确实没有多大作用,但是如果我们需要调试,我们需要查dmp文件,那么请妥善保管好自己的代码和pdb。每次重新编译,即使所有代码均没有变化,他们的GUID也不同(PDB还有age的概念,以后再解释)。

想想每个版本从测试到发布得编译多少次,每次都得辛苦去找PDB那么不是很痛苦啊。所幸我们有符号服务器这种东西。微软有自己HTTP符号服务器,我们自己也可以在20s内迅速搭建(以后会介绍如何搭自己的建符号服务器)。而且较新的vs或者windbg都能智能得对符号服务器进行搜索,避免了自己找符号的麻烦。

 

为了提供一个基本的感性认识,我们看看符号和DLL之间的关系:

 

0:012> !lmi ntdll

Loaded Module Info: [ntdll]

         Module: ntdll

   Base Address: 77040000

     Image Name: ntdll.dll

   Machine Type: 332 (I386)

     Time Stamp: 4a5bdadb Tue Jul 14 09:09:47 2009

           Size: 13c000

       CheckSum: 14033f

Characteristics: 2102  perf

Debug Data Dirs: Type  Size     VA  Pointer

             CODEVIEW    22, d5308,   d4708 RSDS - GUID: {F0164DA7-1FAF-4765-B8F3-DB4F2D7650EA}

               Age: 2, Pdb: ntdll.pdb

                CLSID     4, d5304,   d4704 [Data not mapped]

     Image Type: FILE     - Image read successfully from debugger.

                 C:\Windows\SYSTEM32\ntdll.dll

    Symbol Type: PDB      - Symbols loaded successfully from symbol server.

                 c:\symcache\ntdll.pdb\F0164DA71FAF4765B8F3DB4F2D7650EA2\ntdll.pdb

    Load Report: public symbols , not source indexed

                 c:\symcache\ntdll.pdb\F0164DA71FAF4765B8F3DB4F2D7650EA2\ntdll.pdb

 

  从上面红色的地方我们可以看到ntdll里面的GUID、age等信息。同时我们从微软的符号服务器下载了对应的符号,然后保存到了本地的c:\symcache里。

 

  当我们使用vs进行调试的时候,编译器总是能帮我们找到我们编译的应用程序或者DLL的符号,所以往往我们不会遇到和符号相关的太多麻烦。但是如果我们使用的是其他调试工具,或者查dmp的时候,符号的问题就来了。如果我们给调试器指定了正确符号文件,那么一切都很正常,否则我们将看到令人困惑的东西。

  本文简单介绍了一下符号的概念,以后陆续会对符号做一个比较系统的介绍。

 注:本文所提到的符号均是指微软PDB格式的符号。

 

调试信息的历史回顾

  连接二进制指令和源代码之间的纽带——符号是如何被编译器生成的呢?要具体了解这个内容我们需要先简单回顾一下微软调试信息格式的历史。

COFF:

  最早的调试信息格式是COFF格式,这种格式内嵌到可执行文件中的,它能记录函数、变量、行号、FPO等信息。

CodeView:

  随后就是比较熟悉的CodeView了。这种调试信息的格式提供了内嵌和分离两种形式,和PDB唯一的不同就是没有编辑并继续的功能。独立的CodeView调试信息存储在.dbg文件中。

PDB:

  而微软最新的调试信息格式就是PDB(Program Data Base)了。这种调试信息和可执行文件是完全分离的。他记录了很多丰富的信息,同时还提供了调试并继续、增量链接的功能。不过这种调试信息的格式并没有官方的公开文档,要操作它一般只有通过debughelp或者DIA。PDB又分为两种格式,一种是vc6使用的PDB2.0,后来的版本则全是PDB7.0。PDB7.0是不能向下兼容的。

编译器产生符号的过程

  我们看到调试信息是逐步发展的,最新的调试信息格式为PDB7.0。这是一种和可执行文件分离的格式。对于可执行文件,一般只有几百字节的额外负担。下面我们仅讨论PDB这种调试信息格式。

  如果指定生成调试信息,编译器在每次编译完文件以后就会产生一个obj文件,然后同时产生它对应的调试信息。当我们进行连接的时候,编译器就会帮我们把所有obj统一编译为一个可执行文件,然后所有的调试信息统一生成一个PDB文件。

  如果我们是生成静态库,那么编译器编译完各个源代码以后会统一产生lib文件,同时也将所有的调试信息生成到一个pdb中。如果我们在编译可执行文件的同时需要使用某一个静态库,那么编译器也需要使用到静态库的调试信息,最终可执行文件和调试信息都被单独地生成。

 

 

编译器选项

  对于VS系列编译器,我们可以有一个总开关:/debug。如果没有这个链接选项,所有调试信息均不会被生成。/pdb可以指定符号文件的名称;/pdbstripped可以指定是否同时产生一个公共符号(public symbol)。

  编译选项则有:/Z7 /Zi /ZI 3种。其中/Z7表示生成CodeView格式的调试信息;/Zi表示生成不支持编辑并继续的PDB调试信息;/ZI表示生成支持编辑并继续的PDB调试信息。

  上面提到的选项均有项目属性的GUI设置与之对应:

 

 

 

 

静态库的符号问题

  曾经遇到过一个问题,就是使用了vc6编译的静态库,然后在vs2008中进行链接。结果每次链接的时候都产生警告,提示没有找到静态库的符号,结果就像没有调试信息一样。这个问题研究很久无果。

  后来自习研究了一下静态库的编译方式才解决了问题。上面已经提到,静态库的PDB是每个文件的调试信息的集合,而默认情况下静态库生成的PDB文件都是VCX0.PDB,例如vs2008就是VC90.PDB,VS2010就是VC100.PDB。生成静态库以后,最终的可执行程序进行链接时候,就会根据lib中各个obj记录的信息区找VCX0.PDB,而这个文件就是我们需要的。如果我们要链接很多个静态库,可能就需要在编译静态库的时候/FD给静态库的符号重命名了。

  这一点在.NET中解决得很好,所有依赖的程序集符号都会被自动保存,并且程序集之间的符号不会合并为一份。

 

  符号的生成非常简单,几个编译选项就搞定,默认情况下DEBUG模式都会产生编辑并继续的符号,而Release模式建议也使用/Zi来产生对应调试信息。

时间: 2024-08-03 12:20:35

Windows调试的基石——符号(1)的相关文章

在 Visual Studio 调试器中指定符号 (.pdb) 和源文件

查找并指定符号文件和源文件:指定符号加载行为.使用符号和源服务器上:加载符号自动或在要求.   内容 查找符号 (.pdb) 文件 查找源文件   查找符号 (.pdb) 文件 说明 在之前的 Visual Studio 版本与 2012 中,调试在远程计算机上的管理的代码需要符号文件还查找了远程计算机. 现在,这已经不成问题了. 所有符号文件必须本地计算机或位于 Visual Studio 选项 对话框的 调试 / 符号 页上指定的位置. 如果调试器搜索 .pdb 文件 | 符号文件的原因需要

Windows调试工具入门—1

NetRoc http://www.DbgTech.net 引子 Debugging Tools for Windows是微软发布的一套用于软件调试的工具包(后面如果没有指明,那么我会使用WinDbg来作为这一套调试工具的简称).我第一次接触是在三年前的一个内核驱动项目,由于进行了IDT中键盘鼠标中断的Hook,使用Softice调试时造成会造成影响,只得使用WinDbg通过串口进行双机调试.自此之后这个Windows平台下最为强大的调试工具一直是开发过程中的必备.这里我毫不掩饰的说"最强&qu

.NET应用程序调试—原理、工具、方法

阅读目录: 1.背景介绍 2.基本原理(Windows调试工具箱..NET调试扩展SOS.DLL.SOSEX.DLL) 2.1.Windows调试工具箱 2.2..NET调试扩展包,SOS.DLL.SOSEX.DLL 2.3.调试系统的基本流程及架构(.NETDAC概念.mscordacwks.dll) 2.4.VisualStudio中集成扩展调试(更加细粒度的调试程序) 3.调试程序类型(客户端程序.服务端程序) 4.调试方式及场景 4.1.本机调试(Attach Process,调试器启动

解决VS2012【加载......符号缓慢】的问题

最近在用VS2012调试时,经常出现"加载......符号缓慢的问题",快的时候5分钟能出来,慢的时候就直接死掉了.(超级郁闷) 我的solution里有100多个项目文件,一直以为是项目文件太多导致的,后来找度娘问了一下,才发现不只我一个人遇到这个问题. 而且出现 VS2005.VS2008.VS2010.VS2012.VS2013 均有可能出现这个问题,可能导致的原因是加载符号是需要联网下载,耗费了大量的时间. 具体解决方法如下: 打开VS的[工具]-[选项]-[调试]-[符号],

Linux下定义Windows常用数据类型

  将一个Windows程序移植到Linux上,发现两个操作系统对很多数据类型的命名方式区别很大,比如Windows下对于无符号数据类型一般用U+x的方式,像UINT.UCHAR.ULONG,而Linux上就要中规中矩,都是unsigned +x;另外,还有一些数据类型是Windows有,而Linux没有的,像DWORD.HANDLE.LPSTR等等.于是我写了一个Windows数据类型在Linux下进行转换的头文件 <p> #define INVALID_HANDLE_VALUE -1 &l

在使用CocoaPod的Xcode工程中断点没有调试信息的问题跟进

通常客户端产品至少都会有Debug和Release两种编译配置,在编译Release版本中会进行一些优化,以减少最终产品的体积. 比如,在Release版本中会对代码执行步骤进行优化(如O1.O2等),这会导致有些代码被优化省略掉,有时无法单步调试:另外就是会去掉符号信息,这会导致断点调试时没有符号信息,所以才会有CallStack的捕获和解析内容. 由于千牛iOS端会有不同版本,分别面向内部开发者.外部ISV以及最终用户.尤其是在集成QAP项目后需要提供一个专门的开发调试版本给QAP开发者,所

【转载】/Z7、/Zi、/ZI(调试信息格式)

选择为程序创建的调试信息的类型,并选择是将此信息保存在对象 (.obj) 文件还是程序数据库 (PDB) 中. /Z{7|i|I} 下表描述了这些选项. 无       不产生任何调试信息,因此编译较快. /Z7       生成包含用于调试器的完整符号调试信息的 .obj 文件.符号调试信息包括变量的名称和类型以及函数和行号.不会生成任何 .pdb 文件.       对于第三方库的发布者,不生成 .pdb 文件是一个优点.但是,在链接和调试期间,用于预编译头的 .obj 文件是必需的.如果

visual studio-VS怎么配置才可以调试dll,在dll下断能断下来

问题描述 VS怎么配置才可以调试dll,在dll下断能断下来 VS怎么配置才可以调试dll,在dll下断能断下来,为什么 别人写的dll下断可一断下来,我写的就是进不了里面去? 解决方案 这是在vs2008中 在 C++ 项目中指定调用应用程序 在"解决方案资源管理器"中选择 DLL 项目. 从"视图"菜单中,选择"属性页". 在"Project 属性页"窗口的"配置"下拉列表中,选择"调试&qu

QT Creater/VS2013的使用(快捷键,调试等)

设置QT快捷键:工具à选项à快捷键à键盘,在这里里面进行修改QT快捷键信息. 设置QT字体等信息:工具à选项à文本编辑器à在这里面对字体等信息进行修改.   Ctrl+/:注释代码的快捷键 Ctrl+O:窗口没有了的时候还原原来窗口的快捷键. Ctrl+1,Ctrl+2,Ctrl+3,Ctrl+4:通过这些这些调整编辑视图.   Ctrl+R:运行项目. F5:开始调试. Ctrl+B:构建项目 Ctrl+Alt+pgup:向上复制 Ctrl+Alt+pgdn:向下复制   自定义快捷键: 工具