如何轻松阅读 Golang 的汇编输出?

huangapple go评论94阅读模式
英文:

Easy to read Golang assembly output?

问题

我对检查标准Go编译器生成的x86汇编代码很感兴趣,以确定我的代码是否真的被转换为相对高效的汇编代码;通过分析和检查汇编输出,希望能够找到重写Go代码以获得最佳性能的线索。但是,当我使用-S标志检查代码时,Go输出的是一团糟!我希望有两个方面的帮助:

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

  2. 另外,是否有办法让Go编译器将汇编代码分离成单独的带有标签的函数?我知道有些函数可能会被内联,因此在汇编代码中不会出现。我现在看到的只是一团杂乱无章的汇编代码,几乎无法理解。

英文:

I'm interested in examining the x86 assembly output of the standard Go compiler to see if my code is really being converted into reasonably efficient assembly code; hopefully, by profiling and examining the assembly output, I could get a clue as to where/how I should rewrite my Go code for maximum performance. But when I examine the code using the -S flag, Go spits out a mess! I'd like two things:

  1. Is there a way to make the Go compiler dump the assembly output into a file, not just print it out on Terminal?

  2. Also, is there a way to make the Go compiler separate out the assembly code into separate functions, with labels? I know some functions may be inlined and hence not appear in the assembly code. What I'm seeing know is just a homogenous blob of assembly which is almost impossible to understand.

答案1

得分: 47

你可以像这样将输出重定向到文件:

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

你可以使用-N选项禁用优化:

    go tool compile -S -N file.go

或者,你可以使用gccgo:

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

这将生成test.s文件。你可以尝试使用-O0/1/2/3选项来查看不同的优化效果。

英文:
  1. You can redirect the output to a file like this:

     go tool compile -S file.go > file.s
    
  2. You can disable the optimization with -N:

     go tool compile -S -N file.go
    

Alternatively, you can use gccgo:

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

which will generate test.s. You can play with the -O0/1/2/3 to see the different optimizations.

答案2

得分: 24

我不建议使用-S的输出作为Go链接器可以对目标代码进行相当大的更改。它确实可以让你对正在发生的事情有一些了解。

Go汇编器的输出也相当非标准。

当我想要做到这一点时,我总是使用objdump,它会给你一个漂亮的标准汇编输出。

例如,对于x86 / amd64:

objdump -d 可执行文件 > 反汇编文件

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

objdump -M reg-names-raw -d 可执行文件 > 反汇编文件
英文:

I don't recommend using the output of -S as the Go linker can change what gets written to the object code quite a lot. It does give you some idea as to what is going on.

The go assembler output is rather non-standard too.

When I want to do this I always use objdump which will give you a nice standard assembler output.

Eg for x86 / amd64

objdump -d executable > disassembly

And for ARM (to get the register names to be the same as Go uses)

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

答案3

得分: 16

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

为了限制输出到感兴趣的函数,使用-s选项。

英文:

Run go tool objdump on the resulting executable file.

To restrict the output to interesting functions, use its -s option.

答案4

得分: 9

将输出转储到文件中:

go tool objdump EXECUTABLE_FILE > ASSEMBLY_FILE

如果你想包含源代码(假设你已经安装了 Go 并且自己构建了可执行文件):

go tool objdump -S EXECUTABLE_FILE

为了使输出更易于查看,我使用一个小小的 hacky 包装器,它产生以下结果(简而言之,它给改变控制流的指令上色-跳转为蓝色,调用/返回为绿色,陷阱为红色,填充为紫色-并在无条件控制流跳转之后添加新行):

如何轻松阅读 Golang 的汇编输出?

如果你使用上述的包装器,当使用管道传输到 less 时,你可能想使用 -R 开关(或者将其添加到环境中,例如在 .bashrc 中:export LESS="$LESS -R"):

go-objdump EXECUTABLE_FILE | less -R

另外,还有一个名为 godbolt.org 的网站,它具有可能是最易读的输出,并且允许你轻松切换编译器(gc、gccgo)和版本。

英文:

To dump the output to file:

go tool objdump EXECUTABLE_FILE > ASSEMBLY_FILE

If you want to include the source Go code (assuming you have a working golang setup, and you built the executable yourself):

go tool objdump -S EXECUTABLE_FILE

To make the output even easier to look at I use a small hacky wrapper that produces the following (in a nutshell, it colorizes instructions that alter the control flow -blue for jumps, green for call/return, red for traps, violet for padding- and adds new lines after unconditional control flow jumps):

如何轻松阅读 Golang 的汇编输出?

If you use the wrapper above you will likely want to use the -R switch when piping to less (or by adding it to the environment, e.g. in .bashrc: export LESS="$LESS -R"):

go-objdump EXECUTABLE_FILE | less -R

Alternatively, there is godbolt.org that has probably the most readable output and allows you to switch between compilers (gc, gccgo) and versions very easily.

答案5

得分: 6

最近的一个替代选择是loov/lensm,它可以查看汇编和源代码。
(来自Egon Elbre

> 要运行程序,请为要检查的符号提供一个正则表达式过滤器。
-watch选项允许在可执行文件和信息发生更改时自动重新加载。
>
> lensm -watch -filter Fibonacci lensm
>
> 注意:该程序需要在您的计算机上构建的二进制文件,否则无法加载函数的源代码。
>
> 结果:
>
> 如何轻松阅读 Golang 的汇编输出?


这可能是godbolt.org的一个不错的补充

如何轻松阅读 Golang 的汇编输出?

英文:

A recent alternative would be loov/lensm, which can view assembly and source.
(From Egon Elbre)

> To run the program, provide a regular expression filter for the symbol you want to inspect.
-watch allows to automatically reload the executable and information when it changes.
>
> lensm -watch -filter Fibonacci lensm
>
> Note: The program requires a binary that is built on your computer, otherwise the source code for the functions cannot be loaded.
>
> Result:
>
> 如何轻松阅读 Golang 的汇编输出?


That could be a nice addition to godbolt.org

如何轻松阅读 Golang 的汇编输出?

答案6

得分: 3

我对其他答案有些问题,因为生成的汇编代码提供了比我想要的更多信息,但仍然没有足够的细节。让我解释一下:它提供了由Go内部导入的所有库的汇编代码,并没有提供我的代码所在的行数(我的代码都在文件底部)。

根据官方文档,我找到了以下内容:

$ GOOS=linux GOARCH=amd64 go tool compile -S x.go # 或者: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

这样就得到了我想要的结果!

英文:

I had problems with the other answers as the assembly produced provided much more information than I wanted and still not enough details. Let me explain: it provided the assembly for all libraries imported by go internally and did not provide the lines of where my code was (my code was all at the bottom of the file)

Here is what I found from the official docs:

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

File:

package main

func main() {
    println(3)
}

Produces:

--- 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     ,

So what I did was basically:

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

and it got the result I wanted!

答案7

得分: 2

我找到的适用于使用XCode开发工具的Mac用户的最简单方法是使用otool命令。

$ otool -tV <可执行文件>

来源

英文:

The easiest way i found around for Mac users with XCode dev tools is with otool

$ otool -tV &lt;executable&gt;

Source

huangapple
  • 本文由 发表于 2014年5月22日 01:20:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/23789951.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定