Dwarf2結構在gcc中的應用及調試器實現分析
一、查看方法。
通過gcc -S -g 生成的匯編代碼中包含了一些使用樹脂表示的調試信息,但是這些信息本身如果我們一個一個看文檔的話還是比較麻煩的,所以我們只有通過其它的方法來實現。還要readelf提供了-w功能來顯示整個結構中調試信息的格式。我們就可以結合生成的匯編文件和對應的readelf的輸出再結合dwarf的文檔說明理解一下調試信息的格式。
二、類型信息
1、debug_abbrev節,這個節一般在一個文件的最開始,它可以說是調試信息中一個數據結構,並且為這個類型定義一個標號,之後的結構就可以通過這個標號來表示自己的數據使用的類型。為什麽要這麽做呢?這就和我們寫程序一樣,通常先聲明一個數據結構,然後通過這個結構就可以方便的定義大量的實例,這樣就達到了共享的目的
#define DW_CHILDREN_no 0x00
#define DW_CHILDREN_yes 0x01
如果為0表示這個結構是一個葉子結構,不再有子結構;另一方面,一個結構聲明的結束標誌是通過兩個連續的00字節來表示的。這樣也相當於是一種自適應的算法。可以避免復雜的結構定義。
2、debug_info
既然這裏已經定義了類型,在這個debug_info中就可以引用這些類型了,並且每個類型實例結束也需要定義自己的結束標誌,那就是通過一個0字節來表示這個結構已經結束。另外,當一個實例是一個有子結構的實例的時候,這個為零的標誌位也就起了比較重要的作用。因為如果一個結構有子結構,那麽這個子結構將會緊鄰著這個父結構,並且子結構的層數可以通過為零的字節的數目來確定,也就是遇到一個0就表示一個有子結構的結構已經結束
通過這兩個結構,我們就知道了系統中最為重要的類型信息,其實還有符號信息,而這些符號信息則是“符號調試器”的精髓所在。
三、堆棧信息
當進入函數的時候,我們通常需要看函數中局部變量,局部變量的位置是不確定的,只有在運行時才知道這個值。所以就隱身出了CFA Call Frame Address的值,我們只需要記錄下局部變量相對於CFA的位置,然後再給出CFA的計算方法、最終結合前面提供的結構類型信息,可以算出局部變量的內容。
堆棧信息的存放位於debug_frame節中,而frame節的一個重要任務就是要能夠實時的(精確到每一個指令行)計算出CFA的地址。對於powerPC的初始位置,可以看到裏面一些常用的結構。
在其中的Debug Frame Entry DFE中,有一個棧幀的結構信息,一般在這個結構的開始有一個cfa_def_cfa r1,也就是通過強制定義的方法更新棧幀的地址為r1,此時一般已經通過序言 stwu指令還沒有執行,r1就是上個棧幀的位置;當執行了stwu只有,此時r1寄存器的值已經更新,但是寄存器還是r1,此時需要定義偏移量為stwu的值對應。這樣雖然r1的值變換了,加上這個值之後還是相同的,然後通過mr r31,r1之後,由於r1和r31的值相同,所以就可以重新定義寄存器基地址為r31。這樣當我們計算一個CFA的地址的時候就可以通過寄存器r31+offset來得到棧幀的起始地址,然後加上前面在debug_info中定義的局部變量屬性中的偏移量就可以運行時計算出變量的起始地址,再加上成員就可以知道各個成員的位置和值。
四、行號信息
源代碼級調試另一個基本功能。這個需要將匯編指令的若幹行對應到某個文件的某一行,從而可以實現從源代碼到可執行文件以及可執行文件中的某一行到可執行文件之間的關系轉換。
Dwarf2結構在gcc中的應用及調試器實現分析