如何检查Golang二进制文件是否使用了"--ldflags="-s -w""编译选项

6

我知道--ldflags="-s -w"可以使Go二进制文件大小更小,但是如果没有将其与未使用ldflags="-s -w"编译的二进制文件进行比较,我们怎么知道这个Go二进制文件是使用还是未使用ldflags="-s -w"编译的呢?


你为什么在意? - abbot
1个回答

12

首先声明:我不是编译工具链和可执行文件格式的专家。我会尽量避免说错话,但如果您发现任何错误,请纠正我!

我在一台装有 x86_64 架构的 ArchLinux 笔记本电脑上运行这些测试。Go 版本为 1.8.1。

首先,我们需要知道这些标志实际上在哪里使用:

$ go help build
...
-ldflags 'flag list'
    arguments to pass on each go tool link invocation
...

显然,标志只是传递给go tool link调用。查看帮助信息,我们有更多的信息:

$ go tool link
...
-s  disable symbol table
...
-w  disable DWARF generation
...

这段内容来自我在这里找到的有关ELF简短介绍,其中包含了有关符号表的标题文字:

对象文件的符号表保存了定位和重新定位程序符号定义和引用所需的信息。

至于DWARF,则是一种调试数据格式。

那么,我们如何知道一个二进制文件是否具有符号表或DWARF功能?答案在于ELF节。可能还有其他方法,但这是我发现并且易于检查的方法。下面是我用来确定Go二进制文件是否使用了-ldflags='-s'-ldflags='-w'编译的规则:

  • 如果一个Go二进制文件具有符号表,则该文件中的.symtab节存在。
  • 如果一个Go二进制文件具有DWARF调试,则该文件中的.debug_info节存在。该节不是唯一的,但是可以作为指示符。

在Linux上,有一些工具可以读取节名称以提取这些信息。例如readelfnm等工具,可能还有其他工具。

同时,Go也提供了一个debug/elf包,可用于获取这些信息。下面是一个执行此任务的示例程序:

package main

import "fmt"
import "os"
import "debug/elf"

func main() {
    fileName := "path/to/main"
    fp, err := elf.Open(fileName)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }

    symtab := fp.Section(".symtab")
    if symtab == nil {
        fmt.Println("No Symbol Table : compiled with -ldflags='-s'")
    }

    debugInfo := fp.Section(".debug_info")
    if debugInfo == nil {
        fmt.Println("No DWARF data : compiled with -ldflags='-w'")
    }
}

我使用了基本的 Golang Hello World 程序进行测试,分别使用没有标志、只使用 -s 标志、只使用 -w 标志以及同时使用 -s -w 标志进行编译。我注意到,仅使用 -s 进行编译时,它还会移除 DWARF 数据,但我没有查找原因。除此之外,结果和预期相符。

虽然 ELF 是重点格式,但是同样的方法也适用于 Windows 可执行文件(Golang 中有一个名为 debug/pe 的包)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接