在 Golang 中使用 -w 和 -s 標誌
今天的博客文章來自 Valery,這是 Spiral Scout[1] 的一名高級軟件工程師,專門從事 Golang(Go)開發。
在軟件測試中,標記也被稱爲參數。當從命令行運行程序時,它們用於標識特定的狀態或條件。標記可以打開或關閉,並且在整個軟件開發過程中大量語言和框架都採用這種方式。
本文致力於說明在 Go 中實現 -w
和 -s
標誌的效果,並提供可以更有效地使用它們的方法。
本文是 Go 語言中文網組織的 GCTT 翻譯,發佈在 Go 語言中文網公衆號,轉載請聯繫我們授權。
-w
和 -s
標誌如何與 DWARF 和 ELF 配合使用
在討論何時以及如何使用 -w
和 -s
標誌之前,先簡要介紹一下我的測試環境。我使用的硬件 / 軟件組合包括:
-
A Dell XPS 9570 laptop
-
Manjaro Linux OS
-
Testing branch
-w
和 -s
標誌通常用在 App 鏈接階段和 Go 編譯階段 -ldflags
指令結合使用 (參見 Go 命令文檔 [2] )。有關標誌的更多信息,請參見:https://golang.org/cmd/link/。
在我們仔細查看 -w
標誌並拆解二進制代碼以檢查 DWARF 符號表是否消失之前,我建議先明確 DWARF 符號表的定義。
DWARF 是一種可以包含在二進制文件中的調試數據格式。根據維基百科 DWARF 條目 [3],此格式是與稱爲 ELF(可執行和可鏈接格式)的標準通用文件格式一起開發的。這篇文章 [4] 很好地解釋了調試器如何與 DWARF 表配合工作。
Golang 的創建者們在 Go DWARF 源代碼 [5] 中分享了更多信息,包括有關如何形成此表並將其嵌入以 Go 編寫的二進制文件的詳細信息。
我將通過示例代碼介紹以下一些要點。
首先,我們要使用以下步驟讀取 DWARF 信息:
- 編譯 Go 程序(開始我們僅使用 Go build 命令)
go build -o simple_build cmd/main.go
-
讀取符號表。使用
readelf -Ws
可以方便的實現符號表讀取。但是你也可以使用其他更熟悉的工具讀取文件頭(比如objdump -h
)。 -
請注意生成的程序的頭部內容。
我們可以看到這個二進制文件中包含了用於調試的數據(從第 24 節到第 32 節),並且還有一個符號表和字符串表。(如下所述)
- 使用如下命令進行讀表
> objdump - dwarf=info main
輸出看起來有些長,所以我用下面的命令把輸出保存到文件中。
> objdump - dwarf=info main &> main.txt
你可以在下面看到輸出的一部分:
爲了根據地址查找必要的函數,我們需要知道 PC (程序計數器)。你可以在 EIP 寄存器中找到 PC 值,它由 DW_AT_low_pc 和 DW_AT_high_pc 表示。舉個例子,對於 main.main
函數(main
是 Go 運行時的函數)使用 low_pc
,並嘗試使用 objdump -d
在二進制文件的位置 0x44f930
找到它。
很棒。現在我們使用 -w
標誌編譯程序並且和不使用標誌編譯出來的程序進行比較。
- 運行下面的命令
go build -ldflags=”-w” -o build_with_w cmd/main.go
然後看看生成文件的頭部發生了什麼變化:
正如我們看到的,.zdebug
部分完全消失了。通過對頂部低位(Off 列)地址相減,我們可以精確計算二進制文件減小了多少。當你把這個差值從 Bytes 轉換到 KB 時,可以對實際情況有更直觀的體會。
在這個案例中,二進制文件的總大小大約 25MB,這意味着我們節省了大約 3.7KB。這讓我好奇如果我嘗試使用 Delve Go 調試器工具 [6] 運行 dvl 時會發生什麼?
- 運行
dlv — listen=:43671 — headless=true — api-version=2 — accept-multiclient exec ./build_with_w
... 返回了你所期待的結果:
API server listening at: [::]:43671
could not launch process: could not open debug info
好了,現在關於 DWARF 表和 -w
標誌的作用變得更加清楚了。
讓我們繼續看看 -s
標誌。根據文檔,-s
標誌不僅刪除了調試信息,同時還刪除了指定的符號表。不過,它與 -s
標誌有何不同呢?
首先,快速瞭解一下 — 符號表包含了局部變量、全局變量和函數名等的信息。在上圖中,這些信息在第 26 和第 27 節(.symtab 和 .strtab)給出。更多關於符號表的詳細信息,可以在這裏找到:http://refspecs.linuxbase.org/elf/gabi4+/ch4.symtab.html[7] 和 http://refspecs.linuxbase.org/elf/gabi4+/ch4.strtab.html[8].
這次我們試試用 -s
標誌編譯一個二進制文件:
如同我們期待的一樣,關於 DWARF 的信息以及符號表和字符串表(一種發佈標誌)的內容都消失不見了。
這意味着什麼?
如果只想刪除調試信息,只使用 -w
標誌是最合適的。如果要另外刪除符號和字符串表以減小二進制文件的大小,請使用 -s
標誌。
下面是在 Golang 中使用這些 flag 的的反面教材,不建議大家這樣使用。儘管兩個標誌似乎比一個標誌好,但是對於 -w
和 -s
標誌,情況卻並非如此:
via: https://blog.spiralscout.com/using-w-and-s-flags-in-golang-97ae59b50e26
作者:John Griffin[9] 譯者:befovy[10] 校對:polaris1119[11]
本文由 GCTT[12] 原創編譯,Go 中文網 [13] 榮譽推出,發佈在 Go 語言中文網公衆號,轉載請聯繫我們授權。
參考資料
[1]
Spiral Scout: https://spiralscout.com/
[2]
Go 命令文檔: https://golang.org/src/cmd/go/alldocs.go
[3]
DWARF 條目: https://en.wikipedia.org/wiki/DWARF
[4]
這篇文章: https://eli.thegreenplace.net/2011/02/07/how-debuggers-work-part-3-debugging-information/
[5]
Go DWARF 源代碼: https://golang.org/src/cmd/link/internal/ld/dwarf.go
[6]
Delve Go 調試器工具: https://github.com/go-delve/delve
[7]
http://refspecs.linuxbase.org/elf/gabi4+/ch4.symtab.html: http://refspecs.linuxbase.org/elf/gabi4+/ch4.symtab.html
[8]
http://refspecs.linuxbase.org/elf/gabi4+/ch4.strtab.html: http://refspecs.linuxbase.org/elf/gabi4+/ch4.strtab.html
[9]
John Griffin: https://blog.spiralscout.com/@johnwgriffin
[10]
befovy: https://github.com/befovy
[11]
polaris1119: https://github.com/polaris1119
[12]
GCTT: https://github.com/studygolang/GCTT
[13]
Go 中文網: https://studygolang.com/
福利
我爲大家整理了一份從入門到進階的 Go 學習資料禮包,包含學習建議:入門看什麼,進階看什麼。關注公衆號 「polarisxu」,回覆 ebook 獲取;還可以回覆「進羣」,和數萬 Gopher 交流學習。
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/aPP1Eb6jEJS7oKy4KZLI4g