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:\windows\symbols
0:010> .sympath
Symbol search path is: srv*
Expanded Symbol search path is: SRV*c:\windows\symbols*http://msdl.microsoft.com/download/symbols

  以上設置后,當需要用到符號,Windbg將自動到服務器上去下載,然后保存在c:\windows\symbols。

  當然,我們也可以在計算機上面設置,設置方式是:

  我的電腦=》高級系統設置=》高級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選項其效果是一樣的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM