减少 golang 二进制文件大小


环境:

$ 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 二进制

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 连接的参数:

$ go tool link -h

usage: link [options] main.o
-s	disable symbol table      # 去掉符号表
-w	disable DWARF generation  # 去掉调试信息

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
  数据:                              2 补码,小端序 (little endian)
  版本:                              1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              EXEC (可执行文件)
  系统架构:                          Advanced Micro Devices X86-64
  版本:                              0x1
  入口点地址:               0x400430
  程序头起点:          64 (bytes into file)
  Start of section headers:          6616 (bytes into file)
  标志:             0x0
  本头的大小:       64 (字节)
  程序头大小:       56 (字节)
  Number of program headers:         9
  节头大小:         64 (字节)
  节头数量:         31
  字符串表索引节头: 28
$ readelf -d a.out 

Dynamic section at offset 0xe28 contains 24 entries:
  标记        类型                         名称/值
 0x0000000000000001 (NEEDED)             共享库:[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
  数据:                              2 补码,小端序 (little endian)
  版本:                              1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              EXEC (可执行文件)
  系统架构:                          Advanced Micro Devices X86-64
  版本:                              0x1
  入口点地址:               0x451fa0
  程序头起点:          64 (bytes into file)
  Start of section headers:          456 (bytes into file)
  标志:             0x0
  本头的大小:       64 (字节)
  程序头大小:       56 (字节)
  Number of program headers:         7
  节头大小:         64 (字节)
  节头数量:         13
  字符串表索引节头: 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 二进制比 C 大很多就比较明显了。

golang 静态编译,不依赖动态库。

二. 如何减小 go 二进制文件大小

2.1. -ldflags

上面已经提到了过了。

$ 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 逆向分析简单看下:

https://www.hex-rays.com/products/ida/support/download.shtml

下面是支持 Go 的 IDA helper

https://github.com/sibears/IDAGolangHelper

原始的 go 二进制文件:
可以看到 go 的一些函数名。
hello

去掉符号表和调试信息的 go 二进制文件:
已经看不到函数名信息,只有类似 sub_47BF70 这样。
在这里插入图片描述

经过 upx 压缩的 go 二进制文件:
信息已经比较少了,入口点也发生了变化。
在这里插入图片描述

2.3. 压缩结果对比

$ 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

参考

https://stackoverflow.com/questions/3861634/how-to-reduce-compiled-file-size

https://golang.org/doc/faq#Why_is_my_trivial_program_such_a_large_binary

https://www.cnxct.com/why-golang-elf-binary-file-is-large-than-c/

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Mac Prometheus 是一种用于监控和警报的开源软件,它提供了广泛的监控功能,可用于监控各种应用程序和服务,包括使用 golang 编写的二进制文件。 要在 Mac 上使用 Prometheus 监控 golang 二进制文件,首先需要安装并配置 Prometheus 和相关的组件。可以通过 Homebrew 或手动从 Prometheus 的官方网站下载安装包。安装完成后,需要在 Prometheus 的配置文件中指定要监控的目标和指标。 在 golang 二进制文件中,可以通过 Prometheus 客户端库来暴露自定义的指标。该库提供了一组函数,可以在代码中调用,以便将自定义的指标暴露给 Prometheus。这些指标可以是应用程序的各个方面,如请求处理时间、内存占用、并发连接数等等。 一旦 golang 二进制文件中的指标暴露出来,Prometheus 就可以通过配置文件中的目标来收集这些指标。Prometheus 会按照配置的时间间隔定期访问这些目标,收集指标数据并存储起来。你可以使用 Prometheus 的查询语言 PromQL 来分析和查询这些指标数据,并使用 PromQL 表达式设置警报规则。 为了在 Mac 上实现 golang 二进制文件的监控,你需要编写相应的 golang 代码来使用 Prometheus 客户端库暴露指标。然后,在 Prometheus 的配置文件中指定该 golang 二进制文件的地址作为监控目标。最后,启动 Prometheus 服务,并使用 Prometheus 的界面或 API 来查看和管理指标数据。 总之,通过 Mac Prometheus 监控 golang 二进制文件,你可以轻松地收集和分析应用程序的各种指标,并设置警报规则以监控应用程序的健康状况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值