strip や gzexe コマンドで Go 言語の実行ファイルを圧縮してみた
Go 言語はシングルバイナリを簡単に生成できるのだが、以下のような Hello World を出力するだけの簡単なファイルでも 1.7MB ほどになったりする。
- 参考 : Neos21/practice-go-lang: Go 言語の学習
- 以前作ったコードをほぼそのままビルドしている
# こんな Go 言語のソースコードを書いてみた
$ cat ./practice-01.go
package main
import "fmt"
func main() {
fmt.Printf("hello, world!")
}
# ビルドしてみる
$ go build ./practice-01.go
# ビルドした実行ファイルは 1.7MB 程度になる
$ ls -l ./practice-01
-rwxr-xr-x 1 neo neo 1771121 2022-06-28 01:28 ./practice-01*
$ ls -lh ./practice-01
-rwxr-xr-x 1 neo neo 1.7M 2022-06-28 01:28 ./practice-01*
バイナリファイルってこんなに重たいもんなの?と思ってちょっと調べてみたところ、strip
というコマンドを知った。このコマンドは実行ファイル中にあるデバッグ用の情報を削除することで、実行ファイルのサイズを圧縮してくれるようだ。
- 参考 : tokuhirom blog
- 参考 : stripコマンドの使い方: UNIX/Linuxの部屋
file
コマンドで事前のバイナリファイルを確認すると、not stripped
と出力されている。
$ file ./practice-01
./practice-01: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=If9zmA0q8uak25Vtwi9t/M61qxQqNB8bJSYiRbM2T/aAR70uYax_77fI8PV8sU/khQyoJPhKjrEfwhzUIKi, not stripped
それではこのファイルを strip
にかけてみる。
# 元のバイナリファイルを上書きする形で圧縮される
$ strip ./practice-01
$ ls -l ./practice-01
-rwxr-xr-x 1 neo neo 1188632 2022-06-28 01:29 ./practice-01*
$ ls -lh ./practice-01
-rwxr-xr-x 1 neo neo 1.2M 2022-06-28 01:29 ./practice-01*
# file コマンドでの結果が stripped に変わっている
$ file ./practice-01
./practice-01: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, Go BuildID=If9zmA0q8uak25Vtwi9t/M61qxQqNB8bJSYiRbM2T/aAR70uYax_77fI8PV8sU/khQyoJPhKjrEfwhzUIKi, stripped
すると、1.7MB → 1.2MB ということで、Hello World のみ出力する簡単なプログラムだが、500KB ほどの圧縮ができた。勿論、圧縮した後のファイルも正常に Hello World を出力してくれる。
他に、gzexe
というコマンドでもバイナリファイルを圧縮できるようだ。
- 参考 : gzexeコマンドの使い方: UNIX/Linuxの部屋
- 参考 : gzexe(1) manページ
上述の strip
コマンドを使って、圧縮前後で2バージョンの実行ファイルを用意してみた。
$ ls -l ./practice-01-*
-rwxr-xr-x 1 neo neo 1771121 2022-06-28 01:33 practice-01-not-stripped*
-rwxr-xr-x 1 neo neo 1188632 2022-06-28 01:32 practice-01-stripped*
$ ls -lh ./practice-01-*
-rwxr-xr-x 1 neo neo 1.7M 2022-06-28 01:33 practice-01-not-stripped*
-rwxr-xr-x 1 neo neo 1.2M 2022-06-28 01:32 practice-01-stripped*
それぞれのファイルで gzexe
コマンドを使ってみる。
$ gzexe ./practice-01-not-stripped
./practice-01-not-stripped: 41.0%
$ gzexe ./practice-01-stripped
./practice-01-stripped: 55.5%
$ ls -l practice-01-*
-rwxr-xr-x 1 neo neo 1045374 2022-06-28 01:33 practice-01-not-stripped*
-rwxr-xr-x 1 neo neo 1771121 2022-06-28 01:33 practice-01-not-stripped~*
-rwxr-xr-x 1 neo neo 530002 2022-06-28 01:34 practice-01-stripped*
-rwxr-xr-x 1 neo neo 1188632 2022-06-28 01:32 practice-01-stripped~*
$ ls -lh practice-01-*
-rwxr-xr-x 1 neo neo 1021K 2022-06-28 01:33 practice-01-not-stripped*
-rwxr-xr-x 1 neo neo 1.7M 2022-06-28 01:33 practice-01-not-stripped~*
-rwxr-xr-x 1 neo neo 518K 2022-06-28 01:34 practice-01-stripped*
-rwxr-xr-x 1 neo neo 1.2M 2022-06-28 01:32 practice-01-stripped~*
すると、末尾にチルダ ~
が付いているファイルが別途生成されている。コチラが元ファイルのバックアップなので、チルダが付いている方は動作確認が済んだら削除してしまっても良い。
圧縮されているファイルを見ると、
- Not Stripped : 1.7MB → 1.0MB
- Stripped : 1.2MB → 518KB
という感じで、3分の1から半分近くファイルサイズが削減されている。当然ながら、圧縮後のファイルも正常に Hello World が出力できた。一番最初の 1.7MB のバイナリから比べると、最高で 518KB まで圧縮できるというワケだ。
gzexe
コマンドで圧縮したファイルを開いてみると、前半はシェルスクリプトになっており、その場で自身を解凍・展開しながら後半のバイナリデータ部分を実行しているようだ。そのため、実行時に若干パフォーマンス影響がある。Hello World 程度のプログラムではパフォーマンス影響を感じられなかったが、削減できるファイルサイズとのトレードオフで試してみると良いだろう。
こうした圧縮系のツールは他にも色々あるようで、なかなか面白い。
- 雑多な圧縮ツールについて(前半) - 試験運用中なLinux備忘録・旧記事
- 雑多な圧縮ツールについて(後半) - 試験運用中なLinux備忘録・旧記事
- UPX : Ultimate Packer for eXecutables とか