【问题标题】:Easy to read Golang assembly output?易于阅读的 Golang 汇编输出?
【发布时间】:2014-07-10 11:52:38
【问题描述】:

我有兴趣检查标准 Go 编译器的 x86 汇编输出,看看我的代码是否真的被转换为相当有效的汇编代码;希望通过分析和检查程序集输出,我可以获得关于我应该在哪里/如何重写我的 Go 代码以获得最佳性能的线索。但是当我使用 -S 标志检查代码时,Go 吐出一团糟!我想要两件事:

  1. 有没有办法让 Go 编译器将程序集输出转储到文件中,而不仅仅是在终端上打印出来?

  2. 另外,有没有办法让 Go 编译器将汇编代码分离成带有标签的单独函数?我知道某些函数可能是内联的,因此不会出现在汇编代码中。我所看到的只是一个同质的组装块,几乎无法理解。

【问题讨论】:

  • 1) 使用> 或您的命令shell 支持的任何东西将命令的输出重定向到文件怎么样?
  • 另外,通过查看汇编代码进行分析的方法可能不是最有效的方法。您是否查看过可用于 Go 代码的分析工具? (例如blog.golang.org/profiling-go-programs
  • 我同意你的观点,汇编输出需要一些改进。问题是 Go 编译器不会生成实际的程序集。它生成的东西看起来很像汇编,但包含许多将由链接器扩展的伪指令。只有在链接完成后,才会产生实际的组装(并立即组装)。
  • 另外,看看how the Plan 9 assembler works。 Go 的参考实现使用了这个汇编器。
  • PS:我正在寻找类似于 GCC 命令的东西:-fverbose-asm

标签: assembly go


【解决方案1】:
  1. 您可以将输出重定向到这样的文件:

     go tool compile -S file.go > file.s
    
  2. 您可以使用 -N 禁用优化:

     go tool compile -S -N file.go
    

或者,您可以使用 gccgo:

gccgo -S -O0 -masm=intel test.go

这将生成 test.s。您可以使用 -O0/1/2/3 来查看不同的优化。

【讨论】:

  • go build -gcflags -S test.go > test.s 也应该可以工作(根据golang.org/doc/asm
  • go tool comile -S file.go > file.Sgo1.5
  • @IvanBlack 您的评论值得顶级回应。
  • 试图添加原件以添加@IvanBlack所说的内容(仅从go.15开始用于“go tool compile”),但编辑队列已满。也许作者可能想要。不过没什么大不了的,这些版本很快就会变旧......
【解决方案2】:

我不建议使用-S 的输出,因为 Go 链接器可以极大地改变写入目标代码的内容。它确实让您对正在发生的事情有所了解。

go 汇编器的输出也相当不标准。

当我想这样做时,我总是使用objdump,它会给你一个很好的标准汇编输出。

例如 x86 / amd64

objdump -d executable > disassembly

对于 ARM(使寄存器名称与 Go 使用的相同)

objdump -M reg-names-raw -d executable > disassembly

【讨论】:

    【解决方案3】:

    在生成的可执行文件上运行go tool objdump

    要将输出限制为感兴趣的函数,请使用其-s 选项。

    【讨论】:

      【解决方案4】:

      将输出转储到文件:

      go tool objdump EXECUTABLE_FILE > ASSEMBLY_FILE
      

      如果你想包含源 Go 代码(假设你有一个工作的 golang 设置,并且你自己构建了可执行文件):

      go tool objdump -S EXECUTABLE_FILE
      

      为了使输出更容易查看,我使用了small hacky wrapper,它产生以下内容(简而言之,它为改变控制流的指令着色 - 蓝色表示跳转,绿色表示调用/返回,红色表示陷阱,紫色用于填充 - 并在无条件控制流跳转后添加新行):

      如果您使用上面的包装器,您可能希望在管道到less 时使用-R 开关(或通过将其添加到环境中,例如在.bashrcexport LESS="$LESS -R"):

      go-objdump EXECUTABLE_FILE | less -R
      

      另外,godbolt.org 可能具有最易读的输出,并允许您在编译器(gc、gccgo)和版本之间非常轻松地切换。

      【讨论】:

        【解决方案5】:

        我对其他答案有疑问,因为生成的程序集提供的信息比我想要的多得多,但仍然没有足够的细节。让我解释一下:它为 go 内部导入的所有库提供了程序集,但没有提供我的代码所在的行(我的代码都在文件的底部)

        这是我从官方文档中找到的:

        $ GOOS=linux GOARCH=amd64 go tool compile -S x.go # or: go build -gcflags -S x.go

        文件:

        package main
        
        func main() {
            println(3)
        }
        

        生产:

        --- prog list "main" ---
        0000 (x.go:3) TEXT    main+0(SB),$8-0
        0001 (x.go:3) FUNCDATA $0,gcargs·0+0(SB)
        0002 (x.go:3) FUNCDATA $1,gclocals·0+0(SB)
        0003 (x.go:4) MOVQ    $3,(SP)
        0004 (x.go:4) PCDATA  $0,$8
        0005 (x.go:4) CALL    ,runtime.printint+0(SB)
        0006 (x.go:4) PCDATA  $0,$-1
        0007 (x.go:4) PCDATA  $0,$0
        0008 (x.go:4) CALL    ,runtime.printnl+0(SB)
        0009 (x.go:4) PCDATA  $0,$-1
        0010 (x.go:5) RET     ,
        

        所以我所做的基本上是:

        go tool compile -S hello.go > hello.s

        它得到了我想要的结果!

        【讨论】:

        • 您可能会发现 Godbolt 编译器资源管理器可用于颜色突出显示以将源代码行映射到 asm。 godbolt.org/g/yQ4gbQ 具有 gc1.10.1 和 gccgo 7.2 的简单函数的输出。
        • 这是一个非常好的工具
        【解决方案6】:

        我发现 Mac 用户使用 XCode 开发工具最简单的方法是使用 otool

        $ otool -tV <executable>
        

        Source

        【讨论】:

          猜你喜欢
          • 2011-02-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-10-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多