昨日介紹 nm 的用法,今日則讓我們來實作看看吧!
從 size 與 readelf 工具程式的經驗裡,我們對於 ELF 的檔頭操作已經很熟悉;但是說到標籤的操作,我們就會稍微遲疑一下了:這個東西是在區段裡面的資訊,該怎麼處理才好?筆者這裡先回到 API 中翻找,看看有沒有可用的介面。
從線上手冊看來,elf.File
型別有 Symbols
和 DynamicSymbols
方法,會回傳 []Symbol
的動態長度陣列給呼叫端,而這個 Symbol
型別是
type Symbol struct {
Name string
Info, Other byte
Section SectionIndex
Value, Size uint64
}
相較於我們之前在 as 篇之中纏鬥過的標準規格型別:
ym64 struct {
Name uint32
Info uint8
Other uint8
Shndx uint16
Value uint64
Size uint64
}
最大的差異就是 Name
代表名稱的成員了吧。這個變數原本代表索引,必須搭配 Shndx
成員所指引的區段存取對應的字串作為真正的名稱。簡化過的 Symbol
型別則可以直接將之存為字串。我們要實作 nm 工具程式的話,每個標籤的三個資訊都可以從這裡取得,分別是
Name
Info
變數中蘊含的標籤型態資訊Value
所以結構體的宣告與前日的 size 完全相同即可,但是因為要取得的部份從所有區段檔頭轉變而為只有標籤區段,所以需要作相應的修改。先看 Run
函式的部份:
func (nmu *nmUtil) Run(args map[string]interface{}) error {
+ symtab, _ := nmu.file.Symbols()
+ dynsym, _ := nmu.file.DynamicSymbols()
+ syms := append(symtab, dynsym...)
str := "]"
for _, d := range syms {
raw, err := json.Marshal(d)
if err != nil {
return err
}
str = "," + string(raw) + str
}
re, _ := regexp.Compile("^,")
str = re.ReplaceAllString(str, "[")
nmu.raw = []byte(str)
return nil
}
為了組成所有的標籤,加號後面的三行就是相對應的功能實作。其中第三行特別有趣,因為 golang 對於動態陣列的加入函數 append
同樣可以用於連接的用途,但是語法上其實是先透過 ...
運算子展開動態陣列(正確的 go 語言術語是 slice),然後再使用 append 的多重參數特性去存取。
至於輸出顯示的部份則稍微調整了輸出的欄位:
func (nmu *nmUtil) Output(args map[string]interface{}) error {
w := new(tabwriter.Writer)
w.Init(os.Stdout, 0, 8, 1, ' ', 0)
+ var output []elf.Symbol
json.Unmarshal(nmu.raw, &output)
+ fmt.Fprintln(w, "Offset/Address\tType\tName")
+ for _, s := range output {
+ fmt.Fprintf(w, "%016x\t%s\t%s\n", s.Value,
+ elf.SymType(elf.ST_TYPE(s.Info)).GoString(), s.Name)
+ }
fmt.Fprintln(w)
w.Flush()
return nil
可以看到 output
變數在這裡必須改變型別(readelf 和 size 時這裡都是 elf.Section64
的陣列型別)。特別值得一提的是,第二欄的標籤型別內容的存取十分迂迴,那是因為 Info
本身是複合成員變數,我們必須透過 ST_TYPE
函數來取得它型別部份的資訊。
最後的結果類似這樣子:
Offset/Address Type Name
0000000000010360 elf.STT_FUNC __libc_start_main
0000000000010350 elf.STT_FUNC printf
0000000000012000 elf.STT_OBJECT __TMC_END__
0000000000010360 elf.STT_FUNC __libc_start_main@@GLIBC_2.26
0000000000010428 elf.STT_FUNC main
0000000000012038 elf.STT_NOTYPE __bss_start
0000000000012828 elf.STT_NOTYPE __global_pointer$
0000000000010370 elf.STT_FUNC _start
0000000000012040 elf.STT_NOTYPE _end
0000000000010452 elf.STT_FUNC __libc_csu_init
0000000000012028 elf.STT_OBJECT _IO_stdin_used
0000000000012030 elf.STT_OBJECT __dso_handle
0000000000012000 elf.STT_NOTYPE __data_start
0000000000010350 elf.STT_FUNC printf@@GLIBC_2.26
0000000000012038 elf.STT_NOTYPE _edata
000000000001041c elf.STT_FUNC add
0000000000012000 elf.STT_NOTYPE data_start
00000000000104aa elf.STT_FUNC __libc_csu_fini
0000000000012020 elf.STT_OBJECT _GLOBAL_OFFSET_TABLE_
00000000000104b4 elf.STT_NOTYPE __GNU_EH_FRAME_HDR
0000000000011e20 elf.STT_NOTYPE __init_array_start
0000000000011e30 elf.STT_OBJECT _DYNAMIC
0000000000011e28 elf.STT_NOTYPE __init_array_end
0000000000010330 elf.STT_OBJECT _PROCEDURE_LINKAGE_TABLE_
0000000000000000 elf.STT_FILE
00000000000104f0 elf.STT_OBJECT __FRAME_END__
0000000000000000 elf.STT_FILE crtstuff.c
0000000000000000 elf.STT_FILE elf-init.c
0000000000000000 elf.STT_FILE main.c
0000000000011e20 elf.STT_OBJECT __frame_dummy_init_array_entry
0000000000010418 elf.STT_FUNC frame_dummy
0000000000011e28 elf.STT_OBJECT __do_global_dtors_aux_fini_array_entry
0000000000012038 elf.STT_OBJECT completed.3093
00000000000103f8 elf.STT_FUNC __do_global_dtors_aux
00000000000103ca elf.STT_FUNC register_tm_clones
00000000000103a6 elf.STT_FUNC deregister_tm_clones
0000000000000000 elf.STT_FILE crtstuff.c
0000000000000000 elf.STT_FILE init.c
後面其實還有一堆 Name
無內容的標籤是 Section
型態的標籤。
今天實作了昨天介紹的 nm 工具程式。strip 比之前想像的還要複雜許多,筆者打算接下來也是先介紹數組工具程式之後,有餘力再跟著實作。各位讀者,我們明日再會!