減少 golang 二進(jìn)制文件大小操作
環(huán)境:
$ go version go version go1.11.2 linux/amd64 $ gcc --version gcc (Ubuntu 5.4.0-6ubuntu1~16.04.10) 5.4.0 20160609
一. Go VS C 二進(jìn)制
hello.go
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
hello.c
#include <stdio.h>
int main() {
printf("hello world\n");
return 0;
}
$ go build -o hello hello.go $ go build -ldflags "-s -w" -o hello2 hello.go $ gcc hello.c
$ ls -l -rwxrwxr-x 1 zengxl zengxl 1902849 11月 27 15:40 hello -rwxrwxr-x 1 zengxl zengxl 1353824 11月 27 15:43 hello2 -rwxrwxr-x 1 zengxl zengxl 8600 11月 27 15:44 a.out
golang 連接的參數(shù):
$ go tool link -h usage: link [options] main.o -s disable symbol table # 去掉符號(hào)表 -w disable DWARF generation # 去掉調(diào)試信息
ELF
先來看下 C 的:
$ readelf -h a.out ELF 頭: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 類別: ELF64 數(shù)據(jù): 2 補(bǔ)碼,小端序 (little endian) 版本: 1 (current) OS/ABI: UNIX - System V ABI 版本: 0 類型: EXEC (可執(zhí)行文件) 系統(tǒng)架構(gòu): Advanced Micro Devices X86-64 版本: 0x1 入口點(diǎn)地址: 0x400430 程序頭起點(diǎn): 64 (bytes into file) Start of section headers: 6616 (bytes into file) 標(biāo)志: 0x0 本頭的大小: 64 (字節(jié)) 程序頭大?。? 56 (字節(jié)) Number of program headers: 9 節(jié)頭大?。? 64 (字節(jié)) 節(jié)頭數(shù)量: 31 字符串表索引節(jié)頭: 28
$ readelf -d a.out Dynamic section at offset 0xe28 contains 24 entries: 標(biāo)記 類型 名稱/值 0x0000000000000001 (NEEDED) 共享庫(kù):[libc.so.6] 0x000000000000000c (INIT) 0x4003c8 0x000000000000000d (FINI) 0x4005b4 0x0000000000000019 (INIT_ARRAY) 0x600e10 0x000000000000001b (INIT_ARRAYSZ) 8 (bytes) 0x000000000000001a (FINI_ARRAY) 0x600e18 0x000000000000001c (FINI_ARRAYSZ) 8 (bytes) 0x000000006ffffef5 (GNU_HASH) 0x400298 0x0000000000000005 (STRTAB) 0x400318 0x0000000000000006 (SYMTAB) 0x4002b8 0x000000000000000a (STRSZ) 61 (bytes) 0x000000000000000b (SYMENT) 24 (bytes) 0x0000000000000015 (DEBUG) 0x0 0x0000000000000003 (PLTGOT) 0x601000 0x0000000000000002 (PLTRELSZ) 48 (bytes) 0x0000000000000014 (PLTREL) RELA 0x0000000000000017 (JMPREL) 0x400398 0x0000000000000007 (RELA) 0x400380 0x0000000000000008 (RELASZ) 24 (bytes) 0x0000000000000009 (RELAENT) 24 (bytes) 0x000000006ffffffe (VERNEED) 0x400360 0x000000006fffffff (VERNEEDNUM) 1 0x000000006ffffff0 (VERSYM) 0x400356 0x0000000000000000 (NULL) 0x0
再來看下 go 的:
$ readelf -h hello ELF 頭: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 類別: ELF64 數(shù)據(jù): 2 補(bǔ)碼,小端序 (little endian) 版本: 1 (current) OS/ABI: UNIX - System V ABI 版本: 0 類型: EXEC (可執(zhí)行文件) 系統(tǒng)架構(gòu): Advanced Micro Devices X86-64 版本: 0x1 入口點(diǎn)地址: 0x451fa0 程序頭起點(diǎn): 64 (bytes into file) Start of section headers: 456 (bytes into file) 標(biāo)志: 0x0 本頭的大小: 64 (字節(jié)) 程序頭大?。? 56 (字節(jié)) Number of program headers: 7 節(jié)頭大小: 64 (字節(jié)) 節(jié)頭數(shù)量: 13 字符串表索引節(jié)頭: 3
$ readelf -d hello
There is no dynamic section in this file.
The linker in the gc toolchain creates statically-linked binaries by default. All Go binaries therefore include the Go runtime, along with the run-time type information necessary to support dynamic type checks, reflection, and even panic-time stack traces.
A simple C “hello, world” program compiled and linked statically using gcc on Linux is around 750 kB, including an implementation of printf. An equivalent Go program using fmt.Printf weighs a couple of megabytes, but that includes more powerful run-time support and type and debugging information.
所以,為什么 go 二進(jìn)制比 C 大很多就比較明顯了。
golang 靜態(tài)編譯,不依賴動(dòng)態(tài)庫(kù)。
二. 如何減小 go 二進(jìn)制文件大小
2.1. -ldflags
上面已經(jīng)提到了過了。
$ go build -ldflags "-s -w" xxx.go
2.2. UPX
https://github.com/upx/upx
Commands: -1 compress faster -9 compress better -d decompress -l list compressed file -t test compressed file -V display version number -h give more help -L display software license Options: -q be quiet -v be verbose -oFILE write output to 'FILE' -f force compression of suspicious files -k keep backup files file.. executables to (de)compress Compression tuning options: --brute try all available compression methods & filters [slow] --ultra-brute try even more compression variants [very slow]
$ upx --brute binaryfile
IDA 逆向分析簡(jiǎn)單看下:
https://www.hex-rays.com/products/ida/support/download.shtml
下面是支持 Go 的 IDA helper
https://github.com/sibears/IDAGolangHelper
原始的 go 二進(jìn)制文件:
可以看到 go 的一些函數(shù)名。

去掉符號(hào)表和調(diào)試信息的 go 二進(jìn)制文件:
已經(jīng)看不到函數(shù)名信息,只有類似 sub_47BF70 這樣。

經(jīng)過 upx 壓縮的 go 二進(jìn)制文件:
信息已經(jīng)比較少了,入口點(diǎn)也發(fā)生了變化。

2.3. 壓縮結(jié)果對(duì)比
$ go build -o hello hello.go $ go build -ldflags "-s -w" -o hello-strip hello.go $ upx --brute hello
$ ll -h -rwxr-xr-x 1 aland aland 1.9M Dec 6 13:06 hello -rwxr-xr-x 1 aland aland 809K Dec 6 13:07 hello-upx -rwxr-xr-x 1 aland aland 1.3M Dec 6 13:06 hello-strip
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
Go語(yǔ)言實(shí)現(xiàn)23種設(shè)計(jì)模式的使用
設(shè)計(jì)模式是軟件工程中各種常見問題的經(jīng)典解決方案,,本文主要介紹了Go語(yǔ)言實(shí)現(xiàn)23種設(shè)計(jì)模式的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
利用Go語(yǔ)言實(shí)現(xiàn)輕量級(jí)OpenLdap弱密碼檢測(cè)工具
這篇文章主要為大家詳細(xì)介紹了如何利用Go語(yǔ)言實(shí)現(xiàn)輕量級(jí)OpenLdap弱密碼檢測(cè)工具,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以嘗試一下2022-09-09
Golang unsafe.Sizeof函數(shù)代碼示例使用解析
這篇文章主要為大家介紹了Golang unsafe.Sizeof函數(shù)代碼示例使用解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12
解決Golang 中使用WaitGroup的那點(diǎn)坑
這篇文章主要介紹了解決Golang 中使用WaitGroup的那點(diǎn)坑,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-04-04
windows下使用vscode搭建golang環(huán)境并調(diào)試的過程
這篇文章主要介紹了在windows下使用vscode搭建golang環(huán)境并進(jìn)行調(diào)試,主要包括安裝方法及環(huán)境變量配置技巧,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09
golang?gorm框架數(shù)據(jù)庫(kù)的連接操作示例
這篇文章主要為大家介紹了golang?gorm框架數(shù)據(jù)庫(kù)操作示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04
Go語(yǔ)言中三種容器類型的數(shù)據(jù)結(jié)構(gòu)詳解
在?Go?語(yǔ)言中,有三種主要的容器類型用于存儲(chǔ)和操作集合數(shù)據(jù)這篇文章主要為大家介紹了三者的使用與區(qū)別,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-02-02

