Inspecting Binaries
You can inspect Go binaries with go version -m [binary] command. This command is your tool for introspection. It lets
you peek under the hood of a compiled file without access to the source code. Starting with Go 1.13 1, the
compiler automatically embeds compilation metadata in the executable file. This command extracts that information and
displays it in a clear, readable format. It provides a Software Bill of Materials (SBOM) 2 directly from the executable.
$ go build ./cmd/app
$ go version -m app
app: go1.26.1-X:nodwarf5
path GoBlogGen/cmd/app
mod GoBlogGen v1.3.2+dirty
dep github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
dep github.com/adrg/frontmatter v0.2.0 h1:/DgnNe82o03riBd1S+ZDjd43wAmC6W35q67NHeLkPd4=
dep github.com/gomarkdown/markdown v0.0.0-20260217112301-37c66b85d6ab h1:VYNivV7P8IRHUam2swVUNkhIdp0LRRFKe4hXNnoZKTc=
dep github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc=
dep github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
dep github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
dep github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
dep github.com/tdewolff/minify/v2 v2.24.11 h1:JlANsiWaRBXedoYtsiZgY3YFkdr42oF32vp2SLgQKi4=
dep github.com/tdewolff/parse/v2 v2.8.11 h1:SGyjEy3xEqd+W9WVzTlTQ5GkP/en4a1AZNZVJ1cvgm0=
dep golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
dep gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
build -buildmode=exe
build -compiler=gc
build CGO_ENABLED=0
build GOARCH=amd64
build GOEXPERIMENT=nodwarf5
build GOOS=linux
build GOAMD64=v1
build vcs=git
build vcs.revision=779837ee27f5ffa21a8b983c21bced9ed6e91c63
build vcs.time=2026-03-30T22:56:33Z
build vcs.modified=true
Information that is provided by the tool:
- go version: version of the compiler that produced this binary
- path: indicates that this very binary was created using a specific
main.go - list of dependencies: a module name, version and h1 hash (control sum)
- mod: indicates the name of the project from
go.modfile and the git tagv1.3.2+dirty - build: You can see all the environment variables values that were set during the compilation of the binary. The
CGO_ENABLES=0means the CGO was disabled,GOOS=linuxis self-explanatory and theGOARCH=amd64points to the 64bit architecture. TheGOAMD64means the compiler used the lowest possible optimizations (the highest compatibility) for this architrecture 3. TheGOEXPERIMENT=nodwarf5was used 4 and the-compiler=gcmeans the standard Go compiler was used.
Builds may contain additional information about the vcs like type (vcs=git), which commit (vcs.revision) produced
the binary, whether there were local uncommitted changes (vcs.modified) and time (vcs.time). It is the ultimate tool
for debugging production artifacts. It’s important to note here that this information will be embedded if this build flag is
-buildvcs=true is set and the project is build from the root directory (where go.mod resides).
We can also collect additional information through the linux cli command called file:
$ file app | tr , '\n'
app: ELF 64-bit LSB executable
x86-64
version 1 (SYSV)
statically linked
Go BuildID=7cLPZEyMxTr1asPplq23/JQtc3gAxva5Hs-siRRQo/q6llUm_ZsfunS4-YziGz/x3O2Wc1tUmtd3IEUAF7N
BuildID[sha1]=2dd158033c66d0d1b12aaae129ea6c9d0188a683
with debug_info
not stripped
Note that the binary was build with debug info and symbols’ table is not stripped.
With the symbols embedded into the binary, we can inspect them using go tool nm 5:
$ go tool nm app
(...)
6e18d0 D GoBlogGen/internal/app..inittask
72da00 d GoBlogGen/internal/app..typeAssert.0
2e70c0 T GoBlogGen/internal/app.Run
2e7510 T GoBlogGen/internal/app.Run.deferwrap1
2e75e0 T GoBlogGen/internal/app.copyFile
2e7750 T GoBlogGen/internal/app.copyFiles
(...)
The embedded metadata can be reduced or removed depending on build flags.
$ go build -ldflags "-s -w" -o app cmd/app/main.go
$ file app | tr , '\n'
app: ELF 64-bit LSB executable
x86-64
version 1 (SYSV)
statically linked
Go BuildID=8kClYkzobUsVUbNbh229/ecfWlaix6EFEb3Hh_fn3/p8x78aHUeUVddXSBFoB-/M8LEoQ93qdg_vcFr4TrG
BuildID[sha1]=5850198c4352ea4f824beb4b96ebab2c94f17d95
stripped
Note the stipped part and lack of the debug info. It means the binary does not contain DWARF nor a symbol table used for debugging.
The toolchain, by default, produces statically linked native binaries without external Go dependencies, and we can confirm
that this is the case for our binary by seeing text statically linked. And to further confirm that the binary indeed
does not depend on any dynamically linked libraries use ldd command:
$ ldd app
not a dynamic executable
With the go version -m [binary] we can inspect the binary and get various metadata embedded within it. We can view
if the binary was statically or dynamically linked or what packages are compiled-in. At this point I should mention that
the go tries to produce static binary, but it’s not always the case 8.
This metadata is also useful when verifying binaries as part of reproducible builds 9.
- https://go.dev/doc/go1.13#go-command
- https://en.wikipedia.org/wiki/Software_supply_chain
- https://go.dev/wiki/MinimumRequirements#amd64
- https://go.dev/doc/go1.25#dwarf5-support
- https://pkg.go.dev/cmd/[email protected]
- https://en.wikipedia.org/wiki/DWARF
- https://en.wikipedia.org/wiki/Symbol_table
- https://mt165.co.uk/blog/static-link-go/
- https://go.dev/blog/rebuild