昨日介紹了 objdump 工具程式,今天讓我們開始實作吧!
筆者根據自己的使用經驗與理解,簡化反組譯的過程,列出以下步驟:
.symtab
或是同樣屬性的區段取得標籤
.text
區段內容
BinToInst
所需要的內部資料結構則設計如下:
type label struct {
addr uint64
name string
}
type objdumpUtil struct {
file *elf.File
raw []string
labels []label
}
func New() *objdumpUtil {
return &objdumpUtil{file: nil, raw: make([]string, 0), labels: make([]label, 0)}
}
raw
變數將用以儲存反組譯後的指令結果,而 labels
將用以放置標籤,這些標籤在真正顯示的時候應該要內嵌在正確的地方,以提示使用者理解函式真正存在的位址。
讀取檔案的部份沒有什麼特別的,標籤的部份則可以參考我們實作完的 nm:
func (obu *objdumpUtil) Run(args map[string]interface{}) error {
if *args["d"].(*bool) {
var text int
symtab, _ := obu.file.Symbols()
for _, s := range symtab {
if int(s.Section) >= len(obu.file.Sections) {
continue
}
if obu.file.Sections[s.Section].Name == ".text" {
text = int(s.Section)
var l label
l.name = s.Name
l.addr = s.Value
if l.name != "" {
obu.labels = append(obu.labels, l)
}
}
}
sort.Slice(obu.labels, func(i, j int) bool {
return obu.labels[i].addr < obu.labels[j].addr
})
...
其中,s.Section
是該標籤對應的區段號碼。在迴圈的一開始對這個號碼進行大小的合理性判斷,主要是因為有一個 ABS
型態的區段號碼數字非常大,要是沒有濾除,則會造成執行其的錯誤。後續的條件判斷則顯而易見我們要篩選 .text
區段中的那些標籤。最後一段使用 sort 函式庫以及 能夠排序 slice 物件的 Slice
函數。
Run
函式的後半段仰賴 elf 函式庫幫忙取得區段內容,以及尚未完成的 rvgc 實作:
bin, _ := obu.file.Sections[text].Data()
for len(bin) > 0 {
obu.raw = append(obu.raw, rvgc.BinToInst(bin))
bin = bin[4:]
}
}
go 語言的 for 關鍵字兼有 while 的用途。所以這裡先用定義在區段物件的 Data
方法取得所有內容存在 bin
變數中,然後直接將這個 byte 陣列當作參數傳入 BinToInst
函式。然後是迴圈變數的調整,然後就可以算是結束了。
今天實作了 objdump 的第一個部份,之後筆者將陸續完成 rvgc 中的功能補完,以及最後輸出的部份。各位讀者,我們明日再會!