iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 21
0

前情提要


昨日介紹了 objdump 工具程式,今天讓我們開始實作吧!

設計大綱


筆者根據自己的使用經驗與理解,簡化反組譯的過程,列出以下步驟:

  1. 讀取 ELF 檔案
  2. .symtab 或是同樣屬性的區段取得標籤
    1. 依位址排序完成
  3. 取出 .text 區段內容
    1. 將相對應的區段的內容餵給 rvgc 函式庫的 BinToInst
  4. 印出回傳的內容,將標籤嵌入在正確的位置

所需要的內部資料結構則設計如下:

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 中的功能補完,以及最後輸出的部份。各位讀者,我們明日再會!


上一篇
第二十日:objdump 工具介紹
下一篇
第二十二日:objdump 實作之完結
系列文
與妖精共舞:在 RISC-V 架構上使用 GO 語言實作 binutils 工具包30

尚未有邦友留言

立即登入留言