• Windbg符号与源码 《第二篇》


      符号文件是一种辅助数据,它包含了对应用程序代码的一些标注信息,这些信息在调试过程中非常有用。如果没有辅助数据,那么能获得的信息就只有应用程序的二进制文件。二进制文件很难调试,因为无法看到代码中的函数名、数据结构名等。这正是符号文件能显示的。符号文件的扩展名通常是pdb,调试器能够很好地解析这种文件格式。

      编译器和链接器在创建二进制镜像文件(诸如exe、dll、sys)时,伴生的后缀名为.dbg、.sym或.pdb的包含镜像文件编译、链接过程中生成的符号信息的文件称为符号文件。具体来说,符号信息包括如下内容:

    • 全局变量;
    • 局部变量;
    • 函数;
    • 变量、结构体类型定义;

      源文件路径以及每个符号对应于源文件中的行号,这是进行源码级别调试的基础。

      有这么多的信息包含在符号文件中,使得符号文件通常要比二进制文件(PE格式文件)本身要大很多。只要正确设置了符号路径,使得调试器能够将调试目标、符号文件以及源码文件一一对应起来,才能够最好地发挥调试器的强大功用。

      

      符号文件有两种类型:私有符号文件和公有符号文件。

    • 私有符号文件:是大多数开发人员在日常工作中使用的符号文件,其中包含了调试会话中需要的所有符号信息。
    • 公有符号文件:只是有选择地包含一些符号信息。

      符号信息隶属于指定的模块,所以只有调试器需要用到某个模块时,它的符号信息才有被加载和分析的必要。

      要在调试器中使用符号,我们必须首先告诉调试器这些符号文件的位置,也就是设置符号路径。符号路径可以是本地文件夹路径、可访问的UNC路径、或者是符号服务器路径。

      符号服务器:在调试过程中,需要涉及成千上万个符号文件,以及同一个符号文件存在不同平台下的不同符号文件版本的时候。一一手动设置符号路径肯定是不现实的,于是引入了符号服务器的概念。符号服务器有一套命名规则,使得调试软件能够正确找到需要的符号文件。一般来说,符号服务器比较大,都是共用的,放在远程主机上。为了降低网络访问的成本,又引入了符号缓存的概念,即将从服务器上下载到的符号文件,保存在本地缓存中,以后调试器需要符号文件的时候,先从缓存中寻找,找不到的时候再到服务器上下载。

      1、设置符号路径

      设置符号路径的语法如下:

    .sympath [+] [路径]

      如要覆盖原来的路径设置,使用新路径即可:

    .sympath <新路径>

      要在原有路径的基础上添加一个新路径,可使用:

    .sympath+ <新增路径>

      如果不带参数,那么输出是当前设置的符号路径:

    0:000> .sympath
    Symbol search path is: <empty>  //尚未设置符号路径

      假如在调试时,我知道需要的符号文件位于一下文件夹"D:MyPdb“。

    0:000> .sympath D:MyPdb  //覆盖原有符号路径
    Symbol search path is: D:MyPdb
    Expanded Symbol search path is: d:mypdb

      此时调试器将记录上面新的符号路径,但并不会从这个路径中加载任何符号,要指示调试器加载符号,可以使用元命令reload。这个命令能枚举出进程地址空间中所有已加载的模块,并且尝试找出与各个模块相关的符号文件。

    0:000> .reload
    Reloading current modules
    .....

      如果调试器在指定目录无法找到文件,那么它会输出错误提示:

    *** ERROR:Symbol file could not be found. Defaulted to export symbols for xxx.dll

      当没有设置本地缓存路径时,那么调试器将使用调试软件的安装路径下的sym文件夹。

      要特别注意的是,使用.sympath改变或新增符号路径后,符号文件并不会自动更新,应再执行.reload命令以更新之。

      延迟加载使得模块的符号表,只在第一次真正使用的时候才被加载。这加快了程序启动,不用在一开始耗费大量时间加载全部的符号文件。

      使用.symopt +4和.symopt -4 来开启或关闭延迟加载设置。

      在已经启动了延迟加载的情况下,如想临时改变策略,立刻将指定模块的符号加载到调试器中,可以使用ld或者.reload /f命令。

      2、符号服务器与符号缓存

      设置符号服务器的基本语法是:

    SRV*[符号缓存]*服务器地址

      语法有SRV引导,符号缓存和服务器地址的前面各有一个星号引导。

      此外,我们总是应该把微软的公用符号库加入到我们的符号路径中:

    .sympath+ srv*<缓存地址>*http://msdl.microsoft.com/download/symbols

      这是一台微软对外公开的服务器,使用http地址访问,不是所有人都能牢记这个网址,所以最好的办法就是使用.symfix命令(自动记忆了上面那个微软符号服务器地址),语法如下:

    .symfix [+] [符号缓存地址]

      下面的命令等价于上面的.sympath命令,而不用输入长长的http地址。

    0:010> .symfix c:windowssymbols
    0:010> .sympath
    Symbol search path is: srv*
    Expanded Symbol search path is: SRV*c:windowssymbols*http://msdl.microsoft.com/download/symbols

      以上设置后,当需要用到符号,Windbg将自动到服务器上去下载,然后保存在c:windowssymbols。

      当然,我们也可以在计算机上面设置,设置方式是:

      我的电脑=》高级系统设置=》高级Tab,点击环境变量,新建一个用户变量如下:

    • 变量名:_NT_SYMBOL_PATH
    • 变量值:SRV*D:PDB*http://msdl.microsoft.com/download/symbols/

      

      3、符号选项

      命令格式如下:

    • 显示当前设置:.symopt
    • 增加选项:.symopt+ Flags
    • 删除选项:.symopt- Flags

      第一个命令没有任何参数,显示当前设置。“+”代表添加一个选项,“-”代表去除一个选项。

    0:000> .symopt
    Symbol options are 0x30237:
      0x00000001 - SYMOPT_CASE_INSENSITIVE
      0x00000002 - SYMOPT_UNDNAME
      0x00000004 - SYMOPT_DEFERRED_LOADS
      0x00000010 - SYMOPT_LOAD_LINES
      0x00000020 - SYMOPT_OMAP_FIND_NEAREST
      0x00000200 - SYMOPT_FAIL_CRITICAL_ERRORS
      0x00010000 - SYMOPT_AUTO_PUBLICS
      0x00020000 - SYMOPT_NO_IMAGE_SEARCH

      可用的符号选项请见下表:

    可读名称

    描述

    0×1

    SYMOPT_CASE_INSENSITIVE

    符号名称不区分大小写

    0×2

    SYMOPT_UNDNAME

    符号名称未修饰

    0×4

    SYMOPT_DEFERRED_LOADS

    延迟加载

    0×8

    SYMOPT_NO_CPP

    关闭C++转换,C++中的::符号将以__显示

    0×10

    SYMOPT_LOAD_LINES

    从源文件中加载行号

    0×20

    SYMOPT_OMAP_FIND_

    NEAREST

    如果由于编译器优化导致找不到对应的符号,就以最近的一个符号代替之

    0×40

    SYMOPT_LOAD_ANYTHING

    使得符号匹配的时候,匹配原则较松散,不那么严格。

    0×80

    SYMOPT_IGNORE_CVREC

    忽略镜像文件头中的CV记录

    0×100

    SYMOPT_NO_UNQUALIFIED_

    LOADS

    只在已加载模块中搜索符号,如果搜索符号失败,不会自动加载新模块。

    0×200

    SYMOPT_FAIL_CRITICAL_

    ERRORS

    不显示文件访问错误对话框。

    0×400

    SYMOPT_EXACT_SYMBOLS

    进行最严格的符号文件检查,只要有微小的差异,符号文件都不会被加载。

    0×800

    SYMOPT_ALLOW_ABSOLUTE_

    SYMBOLS

    允许从内存的一个绝对地址处读取符号信息。

    0×1000

    SYMOPT_IGNORE_NT_

    SYMPATH

    忽视在环境变量中设置的符号路径,也忽视被调试进程的执行路径。也就是说,当搜索符号文件的时候,不会从这些路径中搜索。

    0×2000

    SYMOPT_INCLUDE_32BIT_MODULES

    让运行在安腾系统上的调试器,也枚举32位模块。

    0×4000

    SYMOPT_PUBLICS_ONLY

    仅搜索符号文件的公共(PUBLIC)符号表,忽略私有符号表。

    0×8000

    SYMOPT_NO_PUBLICS

    不搜索符号文件的公共(PUBLIC)符号表

    0×10000

    SYMOPT_AUTO_PUBLICS

    先搜索pdb文件的私有符号表,如果在其中找到对应的符号,就不再搜索公共(PUBLIC)符号表,这可以加快搜索速度。

    0×20000

    SYMOPT_NO_IMAGE_SEARCH

    不搜索镜像拷贝

    0×40000

    SYMOPT_SECURE

    安全模式,让调试器尽量不影响到主机。

    0×80000

    SYMOPT_NO_PROMPTS

    不显示符号代理服务器的认证对话框,将导致某些时候无法访问符号服务器

    0×80000000

    SYMOPT_DEBUG

    显示符号搜索的详细过程和信息

      4、符号加载

      1、立刻加载

      命令格式如下:

    ld 模块名 [/f 符号文件名]

      加载指定模块的符号。调试器默认采用延迟模式加载符号。ld使得延迟模式被打破,让指定模块的符号文件立刻加载到调试器中。此指令可为模块的符号文件设置自定义的匹配名称。

    ld 123 /f abc

      这样一来,abc.pdb将成为123.exe的符号文件。正常情况下,这是不可能的,只能是abc.pdb对应abc.exe。

      2、重新加载

      如果对自己正在使用的符号文件感到疑惑,比如源代码和行号明显不匹配,最好就是重新加载一下符号文件。此命令语法如下:

    • .reload /f /v [模块名]

      .reload命令的作用是删除指定或所有已加载的符号文件,默认情况下,调试器不会立刻根据符号路径重新搜索并加载新的符号文件,而是推迟到调试器下一次使用此文件时。

      使用/f参数将破事调试器立刻搜索并重新加载新的符号文件。

      其它参数解释如下:

    • /v:将搜索过程中的详细信息都显示出来。
    • /i:不检查pdb文件的版本信息;
    • /l:只显示模块信息,内核模式下,和“lm n t”命令类似,但显示内容比后者更多,因为包含了用户模块信息;
    • /n:仅重载内核符号,不重载用户符号;
    • /o:强制覆盖符号库中的符号文件,即使版本相同;
    • /d:用户层模式下使用Windbg时的默认选项,重载调试器模块列表中的所有模块;
    • /s:内核模式下使用Windbg时的默认选项,重载系统模块列表中的所有模块,另外,如果调试器在用户模式下运行,要加载内核模块,也必须使用/s选项,否则调试器将只会在调试器模块列表中搜索而导致找不到内核模块;
    • /u:卸载指定模块。如发现当前符号版本不对,使用/u开关先卸载之再重新加载。

      3、符号验证

      符号文件出现不匹配的情况,这是有可能的,程序员在后期测试的时候可能会将工程多次编译,为了维护多个版本而使得自己也被混淆。可以使用下面的命令验证一个模块的符号文件:

    • !chksym <模块名> [符号名]

      加载选项:!sym

      有两类符号加载选项。第一类是Noisy/Quiet,Noisy选项将打印符号加载的详细信息,Quiet选项则忽略这些信息。第二类是Prompts/Prompts off,即是否允许执行提示(Prompts)对话框。

      一般都是在调用.reload命令之前,执行加载选项命令。

      所谓Noisy是吵闹的意思,调试器在搜索、加载符号的时候,会显示更多与搜索有关的信息。在安静模式下,则不会显示这些信息。不管吵闹与否,都不会影响到最终的搜索、加载结果。
    当从网络上下载符号文件的时候,可能会碰到网络服务器要求客户进行安全认证的情况,如果开启Prompts选项,则弹出认证对话框,让用户输入认证信息;否则,不弹出对话框,并且不会下载符号文件。

      不加任何参数的情况下,显示当前加载选项设置,下面的清单表明当前的设置为Quite及Prompts模式

    0:000> .symopt
    Symbol options are 0x30237:
      0x00000001 - SYMOPT_CASE_INSENSITIVE
      0x00000002 - SYMOPT_UNDNAME
      0x00000004 - SYMOPT_DEFERRED_LOADS
      0x00000010 - SYMOPT_LOAD_LINES
      0x00000020 - SYMOPT_OMAP_FIND_NEAREST
      0x00000200 - SYMOPT_FAIL_CRITICAL_ERRORS
      0x00010000 - SYMOPT_AUTO_PUBLICS
      0x00020000 - SYMOPT_NO_IMAGE_SEARCH
    0:000> !sym
    !sym <noisy/quiet - prompts/prompts off> - quiet mode - symbol prompts on

      5、符号搜索

      符号搜索包括全局搜索与就近搜索两种。

      1、全局搜索

      命令“x”被用来进行符号的全局搜索,你可以把它直接就理解为search。格式如下:

    • x [参数] [模块!符号]

      如果什么参数都没有的话,它将列出当前调试环境下的所有局部变量,前提是要在有局部变量存在的情况下,显示局部变量的另一个命令是dv,后文也会讲到。

    • x kernel32!a*

      上面命令搜索并打印出kernel32模块中所有a开头的符号。x命令支持DML,使用/D选项即以DML格式显示。

    0:000> !sym
    !sym <noisy/quiet - prompts/prompts off> - quiet mode - symbol prompts on
    0:000> x  kernel32!a*
    769836a8 kernel32!AllocContext = <no type information>
    769a4286 kernel32!AdjustHijriYears = <no type information>
    76986698 kernel32!AddLocalAlternateComputerNameW = <no type information>
    76997fab kernel32!AllocateUserPhysicalPages = <no type information>... ... ...太长,省略一部分

      如果你不知道ntcreatefile这个函数是在哪个模块中定义的,可以试着使用下面的命令:

    • x  *!*NtCreateFile* (注:亦请参照!dlls –c命令)

      同名函数在多个系统模块中并定义,这可能出乎你的意料,但却给你带来真正的知识。

      此外,x命令有多个可选参数。建议总是带上/t和/v,可显示更多符号、类型信息。

    • /f:将只显示函数符号;并且会显示函数的详细定义。
    • /d:显示更多的变量类型相关信息。

      2、就近搜索

      如果知道了符号的大概地址,但不能确定确切的符号名称,该怎么处理呢?就近查找命令“ln”能发挥作用,ln是List Nearest的缩写。它的作用是:(根据给定的地址)列出附近一定范围内的所有符号。

      6、源码命令

      如果含有源码信息,可使得调试过程能够以源码模式逐行进行。和源码相关的命令包括下面几个:

      源码路径:

      和符号路径类似,要设置源码路径,使用如下语法格式:

    • .srcpath[+] [路径1;路径2]

      不含任何参数的情况下,显示当前设置的源码路径。

      下面命令将覆盖原设置,设置新的源码搜索路径

    • .srcpath<路径信息>

      使用"+"可以将新的路径添加到原设置中,而不会把原设置覆盖掉:

    • .srcpath+ <路径信息>

      7、源码选项

      这里列出三个源码选项。

      1、noisy

    • 状态:.srcnoisy
    • 开启:.srcnoisy 1
    • 关闭:.srcnoisy 0

      开始“吵闹的源码”选项后,在源码加载、卸载、甚至单步的时候,都会显示丰富的源码信息。

      2、lines

      行号选项,即在符号文件加载过程中,是否将行号也一并加载进来。因为Windbg支持源码级调试,所以它在Windbg中是默认开启(Enable)的。

    • .lines [-d|-e|-t]

      参数d是disable的意思;e是enable的意思;t表示切换的意思,即自动在disable和enable两者之间切换。

      3、代码行选项

      包括行号和内容,语法如下:

    • 打开:l+ [选项]
    • 关闭:l- [选项]

      命令|是line的缩写,和上面的.lines命令不同的是,.lines是加载时选项,l是调试时选项。建议使用"l+*"指令,打开所有的行选项,效果会很不错。这样在单步调试的时候,每一步的代码和行号都会显示出来。显得很醒目!

      值得注意的是,进入源码模式和进入汇编模式的命令分别为:

    • 源码模式:l+t
    • 汇编模式:l-t

      运行这两个命令和在Windbg的Debug菜单下点击source mode选项其效果是一样的。

     
     

    from:https://www.cnblogs.com/kissdodog/p/3729396.html

  • 相关阅读:
    net core 在开发环境IIS程序物理路径指向代码文件
    asp.net core 源码下载以及build
    Autofac学习之三种生命周期:InstancePerLifetimeScope、SingleInstance、InstancePerDependency
    ASP.NET Core 使用 AutoFac 注入 DbContext
    ASP.net core 中控制器直接访问wwwroot的静态文件
    在ASP.NET Core中处置IDisposable的四种方法
    sql语句优化之SQL Server(详细整理)
    sql语句的优化分析
    sql server中如何查看执行效率不高的语句
    [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了
  • 原文地址:https://www.cnblogs.com/lidabo/p/14380997.html
Copyright © 2020-2023  润新知