iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 16
0

前情提要


波瀾壯闊的 rvgc 與 as 篇大致告一段落。今日回到一般的 binutils 工具程式中,目標是:size!

使用方法與功能介紹


size 工具程式的主要功能就是展示各區段的大小,比方說如果直接將這個程式用在 /bin/ls 身上的話:

$ size /bin/ls
   text    data     bss     dec     hex filename
 122923    4688    4832  132443   2055b /bin/ls

說實在的,看起來實在很沒用。除了這幾個區段之外的區段資訊呢?給了這些大小資訊又能怎麼樣呢?能夠至少提供區段在檔案內的偏移量嗎?

這是預設的 BSD size 輸出模式。結果非預設的 SYSV 輸出模式竟然就有提供上述問題的解答了:

$ size -A /bin/ls
/bin/ls  :
section                size      addr
.interp                  28       568
.note.ABI-tag            32       596
.note.gnu.build-id       36       628
.gnu.hash               244       664
.dynsym                3336       912
.dynstr                1524      4248
.gnu.version            278      5772
.gnu.version_r          112      6056
.rela.dyn              7632      6168
.init                    23     13800
.plt                     16     13824
.plt.got                  8     13840
.text                 75529     13856
.fini                     9     89388
.rodata               19948     89408
.eh_frame_hdr          2140    109356
.eh_frame             12028    111496
.init_array               8   2224144
.fini_array               8   2224152
.data.rel.ro           2616   2224160
.dynamic                464   2226776
.got                    976   2227240
.data                   616   2228224
.bss                   4832   2228864
.comment                 26         0
Total                132469

這不是擺明了有用多了嗎!雖然 addr 的量使用十進位,感覺也是很莫名其妙就是了。因此,筆者的實作決定參考後者的格式輸出,然後將 addr 欄位內容改成 16 進位的格式;為求方便轉換,size 欄位的部份輸出 10 進位與 16 進位的格式。

實作

從 readelf 時的程式碼可以參考許多東西,而最主要的元件都已經在那時候展示過了:區段名稱、區段大小、區段在記憶體中的位址、以及區段在檔案中的偏移量。

因為沒有必要針對不同的參數儲存相對應的輸出,所以簡化了內部結構:

type sizeUtil struct {
        file *elf.File
        raw  []byte
}

由於是純粹讀檔就能夠完成的一個工具程式,InitDefineFlags 函數就不需要實作什麼。維持和 readelf 一樣的實作,我們還是在 Run 函式中將所需資訊存成 json 格式,然後在 Output 函式中輸出:

func (siu *sizeUtil) Run(args map[string]interface{}) error {

        str := "]"
        for _, p := range siu.file.Sections {
                raw, err := json.Marshal(p)
                if err != nil {
                        return err
                }

                str = "," + string(raw) + str
        }
        re, _ := regexp.Compile("^,")
        str = re.ReplaceAllString(str, "[")

        siu.raw = []byte(str)
        return nil
}

func (siu *sizeUtil) Output(args map[string]interface{}) error {

        w := new(tabwriter.Writer)
        w.Init(os.Stdout, 0, 8, 2, ' ', tabwriter.AlignRight)

        var output []elf.SectionHeader
        json.Unmarshal(siu.raw, &output)

        fmt.Fprintln(w, "Name\tSize\tAddress\tOffset\t")
        for _, s := range output {
                fmt.Fprintf(w, "%s\t%d(%x)\t%x\t%x\t\n",
                        s.Name, s.Size, s.Size, s.Addr, s.Offset)
        }
        fmt.Fprintln(w)
        w.Flush()
}

最後輸出的結果類似這樣:

$ /tmp/size /bin/ls
                Name          Size  Address  Offset
           .shstrtab       246(f6)        0   20282
            .comment        26(1a)        0   20268
                .bss    4832(12e0)   220280   20268
               .data      616(268)   220000   20000
                .got      976(3d0)   21fc28   1fc28
            .dynamic      464(1d0)   21fa58   1fa58
        .data.rel.ro     2616(a38)   21f020   1f020
         .fini_array          8(8)   21f018   1f018
         .init_array          8(8)   21f010   1f010
           .eh_frame   12028(2efc)    1b388   1b388
       .eh_frame_hdr     2140(85c)    1ab2c   1ab2c
             .rodata   19948(4dec)    15d40   15d40
               .fini          9(9)    15d2c   15d2c
               .text  75529(12709)     3620    3620
            .plt.got          8(8)     3610    3610
                .plt        16(10)     3600    3600
               .init        23(17)     35e8    35e8
           .rela.dyn    7632(1dd0)     1818    1818
      .gnu.version_r       112(70)     17a8    17a8
        .gnu.version      278(116)     168c    168c
             .dynstr     1524(5f4)     1098    1098
             .dynsym     3336(d08)      390     390
           .gnu.hash       244(f4)      298     298
  .note.gnu.build-id        36(24)      274     274
       .note.ABI-tag        32(20)      254     254
             .interp        28(1c)      238     238
                              0(0)        0       0

應該還不錯吧!

小結


的確,相比於系列文開始為止至今,本日的內容的確是稍微單薄了一點。但畢竟筆者也是需要休息的,恰好有幾個小型的工具程式可以充當折衷,也算是喘一口氣吧。明日我們就從 strip 繼續下去吧!


上一篇
第十五日:rvgc 函式庫測試--靜態連結程式
下一篇
第十七日:strip 原理介紹
系列文
與妖精共舞:在 RISC-V 架構上使用 GO 語言實作 binutils 工具包30

尚未有邦友留言

立即登入留言