調試器定位變量的原理

假設咱們確實在do_stuff中的斷點處停了下來。咱們但願調試器可以告訴咱們my_local變量的值,調試器怎麼知道去×××到相關的信息呢?這可比定位函數要難多了,由於變量能夠在全局數據區,能夠在棧上,甚至是在寄存器中。另外,具備相同名稱的變量在不一樣的詞法做用域中可能有不一樣的值。調試信息必須可以反映出全部這些變化,而DWARF確實能作到這些。(用到了ptrace系統調用)
我不會涵蓋全部的可能狀況,做爲例子,我將只展現調試器如何在do_stuff函數中定位到變量my_local。咱們從.debug_info段開始,再次看看do_stuff這一項,這一次咱們也看看其餘的子項:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<1><71>: Abbrev Number: 5 (DW_TAG_subprogram)
     <72>   DW_AT_external    : 1
     <73>   DW_AT_name        : (...): do_stuff
     <77>   DW_AT_decl_file   : 1
     <78>   DW_AT_decl_line   : 4
     <79>   DW_AT_prototyped  : 1
     <7a>   DW_AT_low_pc      : 0x8048604
     <7e>   DW_AT_high_pc     : 0x804863e
     <82>   DW_AT_frame_base  : 0x0      (location list)
     <86>   DW_AT_sibling     : <0xb3>
  <2><8a>: Abbrev Number: 6 (DW_TAG_formal_parameter)
     <8b>   DW_AT_name        : (...): my_arg
     <8f>   DW_AT_decl_file   : 1
     <90>   DW_AT_decl_line   : 4
     <91>   DW_AT_type        : <0x4b>
     <95>   DW_AT_location    : (...)       (DW_OP_fbreg: 0)
  <2><98>: Abbrev Number: 7 (DW_TAG_variable)
     <99>   DW_AT_name        : (...): my_local
     <9d>   DW_AT_decl_file   : 1
     <9e>   DW_AT_decl_line   : 6
     <9f>   DW_AT_type        : <0x4b>
     <a3>   DW_AT_location    : (...)      (DW_OP_fbreg: -20)
<2><a6>: Abbrev Number: 8 (DW_TAG_variable)
     <a7>   DW_AT_name        : i
     <a9>   DW_AT_decl_file   : 1
     <aa>   DW_AT_decl_line   : 7
     <ab>   DW_AT_type        : <0x4b>
<af>   DW_AT_location    : (...)      (DW_OP_fbreg: -24)
注意每個表項中第一個尖括號裏的數字,這表示嵌套層次——在這個例子中帶有<2>的表項都是表項<1>的子項。所以咱們知道變量my_local(以DW_TAG_variable做爲標籤)是函數do_stuff的一個子項。調試器一樣還對變量的類型感興趣,這樣才能正確的顯示變量的值。這裏my_local的類型根據DW_AT_type標籤可知爲<0x4b>。若是查看objdump的輸出,咱們會發現這是一個有符號4字節整數。
要在執行進程的內存映像中實際定位到變量,調試器須要檢查DW_AT_location屬性。對於my_local來講,這個屬性爲DW_OP_fberg: -20。這表示變量存儲在從所包含它的函數的DW_AT_frame_base屬性開始偏移-20處,而DW_AT_frame_base正表明了該函數的棧幀起始點。
函數do_stuff的DW_AT_frame_base屬性的值是0×0(location list),這表示該值必需要在location list段去查詢。咱們看看objdump的輸出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ objdump --dwarf=loc tracedprog2
  
tracedprog2:      file  format  elf32-i386
  
Contents of the .debug_loc section:
  
     Offset   Begin    End      Expression
     00000000 08048604 08048605 (DW_OP_breg4: 4 )
     00000000 08048605 08048607 (DW_OP_breg4: 8 )
     00000000 08048607 0804863e (DW_OP_breg5: 8 )
     00000000 <End of list>
     0000002c 0804863e 0804863f (DW_OP_breg4: 4 )
     0000002c 0804863f 08048641 (DW_OP_breg4: 8 )
     0000002c 08048641 0804865a (DW_OP_breg5: 8 )
0000002c <End of list>
關於位置信息,咱們這裏感興趣的就是第一個。對於調試器可能定位到的每個地址,它都會指定當前棧幀到變量間的偏移量,而這個偏移就是經過寄存器來計算的。對於x86體系結構,bpreg4表明esp寄存器,而bpreg5表明ebp寄存器。
讓咱們再看看do_stuff的開頭幾條指令:
1
2
3
4
5
6
7
08048604 <do_stuff>:
  8048604:       55          push   ebp
  8048605:       89 e5       mov    ebp,esp
  8048607:       83 ec 28    sub    esp,0x28
  804860a:       8b 45 08    mov    eax, DWORD  PTR [ebp+0x8]
  804860d:       83 c0 02    add    eax,0x2
  8048610:       89 45 f4    mov     DWORD  PTR [ebp-0xc],eax
注意,ebp只有在第二條指令執行後才與咱們創建起關聯,對於前兩個地址,基地址由前面列出的位置信息中的esp計算得出。一旦獲得了ebp的有效值,就能夠很方便的計算出與它之間的偏移量。由於以後ebp保持不變,而esp會隨着數據壓棧和出棧不斷移動。
那麼這到底爲咱們定位變量my_local留下了什麼線索?咱們感興趣的只是在地址0×8048610上的指令執行事後my_local的值(這裏my_local的值會經過eax寄存器計算,然後放入內存)。所以調試器須要用到DW_OP_breg5: 8 基址來定位。如今回顧一下my_local的DW_AT_location屬性:DW_OP_fbreg: -20。作下算數:從基址開始偏移-20,那就是ebp – 20,再偏移+8,咱們獲得ebp – 12。如今再看看反彙編輸出,注意到數據確實是從eax寄存器中獲得的,而ebp – 12就是my_local存儲的位置。
http://www.kuqin.com/system-analysis/20120808/324134.html
相關文章
相關標籤/搜索