Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd/link: add support for automatically generating .note.gnu.build-id #41004

Closed
segevfiner opened this issue Aug 24, 2020 · 18 comments
Closed

cmd/link: add support for automatically generating .note.gnu.build-id #41004

segevfiner opened this issue Aug 24, 2020 · 18 comments
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. FeatureRequest NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@segevfiner
Copy link
Contributor

What version of Go are you using (go version)?

$ go version
go version go1.15 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build730694799=/tmp/go-build -gno-record-gcc-switches"

What did you do?

I want the ability to automatically generate a .note.gnu.build-id section in Go binaries. The linker currently supports a flag to get one from the outside: -B, but this is supposed to be a CRC that is calculated by the linker rather than something that can reliably be set from the outside as a linker flag.

This is used by delve and other debuggers to find split debug symbols.

What did you expect to see?

I want a flag like the binutils linker has -Wl,-build-id that adds an automatically generated .note.gnu.build-id section.

What did you see instead?

There is only a flag to set it from the outside, but it is meant to be a CRC calculated by the linker to be used reliably to differentiate different debug symbols.

@ALTree ALTree changed the title Add support for automatically generating .note.gnu.build-id cmd/link: add support for automatically generating .note.gnu.build-id Aug 24, 2020
@ALTree ALTree added NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. FeatureRequest labels Aug 24, 2020
@gopherbot gopherbot added the compiler/runtime Issues related to the Go compiler and/or runtime. label Jul 13, 2022
@seankhliao seankhliao added this to the Unplanned milestone Aug 27, 2022
@Foxboron
Copy link
Contributor

Foxboron commented Jan 4, 2023

I hacked a little bit on this by just running sha1 on the output buffer we have in the linker. It gives me a stable sha1 sum and I think(?) it should be good enough? Should we stamp the same build id if we use the -s -w flags maybe?

diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
index a1ae7eab57..3f9f115282 100644
--- a/src/cmd/link/internal/ld/elf.go
+++ b/src/cmd/link/internal/ld/elf.go
@@ -10,6 +10,7 @@ import (
        "cmd/internal/sys"
        "cmd/link/internal/loader"
        "cmd/link/internal/sym"
+       "crypto/sha1"
        "debug/elf"
        "encoding/binary"
        "encoding/hex"
@@ -825,6 +826,11 @@ func elfgobuildid(sh *ElfShdr, startva uint64, resoff uint64) int {
        return elfnote(sh, startva, resoff, n)
 }

+func elfproducebuildinfo(out *OutBuf) {
+       id := sha1.Sum(out.Data())
+       buildinfo = id[:]
+}
+
 func elfwritebuildinfo(out *OutBuf) int {
        sh := elfwritenotehdr(out, ".note.gnu.build-id", ELF_NOTE_BUILDINFO_NAMESZ, uint32(len(buildinfo)), ELF_NOTE_BUILDINFO_TAG)
        if sh == nil {
@@ -1396,9 +1402,7 @@ func (ctxt *Link) doelf() {
        if ctxt.IsFreebsd() {
                shstrtab.Addstring(".note.tag")
        }
-       if len(buildinfo) > 0 {
-               shstrtab.Addstring(".note.gnu.build-id")
-       }
+       shstrtab.Addstring(".note.gnu.build-id")
        if *flagBuildid != "" {
                shstrtab.Addstring(".note.go.buildid")
        }
@@ -1911,6 +1915,9 @@ func asmbElf(ctxt *Link) {
                phsh(pnotei, sh)
        }

+       if len(buildinfo) == 0 {
+               elfproducebuildinfo(ctxt.Out)
+       }
        if len(buildinfo) > 0 {
                sh := elfshname(".note.gnu.build-id")
                resoff -= int64(elfbuildinfo(sh, uint64(startva), uint64(resoff)))
@@ -2285,9 +2292,7 @@ elfobj:
                if ctxt.HeadType == objabi.Hfreebsd {
                        a += int64(elfwritefreebsdsig(ctxt.Out))
                }
-               if len(buildinfo) > 0 {
-                       a += int64(elfwritebuildinfo(ctxt.Out))
-               }
+               a += int64(elfwritebuildinfo(ctxt.Out))
                if *flagBuildid != "" {
                        a += int64(elfwritegobuildid(ctxt.Out))
                }

@Foxboron
Copy link
Contributor

Foxboron commented Feb 17, 2023

@bcmills sorry for the ping here. But has there been any discussions on how to solve this?

Would a sha256 hash over the actionID and/or the .note.go.buildid string be a good enough solution?

Generally this would help debug packages on Linux distros and debuggers like delve do the right thing instead of having to pass the -buildid linker flag explicitly.

@ianlancetaylor
Copy link
Contributor

We already have a Go build ID. We can just write that out as the GNU build ID as well. I think the only question is whether we should always do it. The GNU linker only does it upon request, but on many Linux systems the compiler is configured to request it by default (by passing a --build-id option when invoking the linker).

@Foxboron
Copy link
Contributor

Most (all?) debuggers expect the value for basic functionality and I struggle to see why we would stamp go build id and not the GNU build id along with it?

The only issue is that the GNU build ID needs to either be a md5 or sha1, so I don't think we can pass the Go build id as-is?

Having this as a build option for distributions is non-trivial as it's still hard to override the command line arguments passed to the compiler.

@ianlancetaylor
Copy link
Contributor

To the best of my knowledge the GNU build ID does not have to be an MD5 or SHA1. Those are just common implementations of it. It can be any random relatively unique string.

@ianlancetaylor
Copy link
Contributor

ianlancetaylor commented Feb 17, 2023

For example, for the gold linker the default value (if you just specify --build-id with no specified style) is neither MD5 nor SHA1, it's a tree hash that can be computed in parallel.

@prattmic
Copy link
Member

Linux's perf tool is one such tool that automatically records the GNU buildid (directly in the kernel during mmap event generation), which can then be used by downstream tooling to find the correct binary for symbolization. For pure Go binaries, no build ID is recorded.

@prattmic
Copy link
Member

prattmic commented Apr 19, 2023

We already have a Go build ID. We can just write that out as the GNU build ID as well. I think the only question is whether we should always do it.

FWIW, Linux perf expects the GNU build ID to be 20 bytes or less (https://elixir.bootlin.com/linux/v6.2/source/lib/buildid.c#L30). I'm not sure if this is specified or a convention, but the Go build ID is significantly longer (83 bytes).

@znkr
Copy link
Contributor

znkr commented Apr 19, 2023

Truncating larger hashes seems to be okay: https://reviews.llvm.org/D121531

pks-t added a commit to pks-t/go that referenced this issue Jul 20, 2023
While it is possible to embed a GNU build ID into the linked executable
by passing `-B 0xBUILDID` to the linker, the build ID will need to be
precomputed by the build system somehow. This makes it unnecessarily
complex to generate a deterministic build ID as it either requires the
biuld system to hash all inputs manually or to build the binary twice,
once to compute its hash and once with the GNU build ID derived from
that hash. Despite being complex, it is also inefficient as it requires
the build system to duplicate some of the work that the Go linker
already performs anyway.

Introduce a new argument "actionid" that can be passed to `-B` that
causes the linker to automatically derive the build ID from the Go build
ID's action ID, which is derived from all build inputs. This should thus
both be deterministic while the value also changes whenever the build
inputs change, which is the desired behaviour for the GNU build ID.

Furthermore, given that the `-B` flag currently requires a "0x" prefix
for all values passed to it, using "actionid" as value is a backwards
compatible change.

Fixes golang#41004
pks-t added a commit to pks-t/go that referenced this issue Jul 20, 2023
While it is possible to embed a GNU build ID into the linked executable
by passing `-B 0xBUILDID` to the linker, the build ID will need to be
precomputed by the build system somehow. This makes it unnecessarily
complex to generate a deterministic build ID as it either requires the
biuld system to hash all inputs manually or to build the binary twice,
once to compute its hash and once with the GNU build ID derived from
that hash. Despite being complex, it is also inefficient as it requires
the build system to duplicate some of the work that the Go linker
already performs anyway.

Introduce a new argument "actionid" that can be passed to `-B` that
causes the linker to automatically derive the build ID from the Go build
ID's action ID, which is derived from all build inputs. This should thus
both be deterministic while the value also changes whenever the build
inputs change, which is the desired behaviour for the GNU build ID.

Furthermore, given that the `-B` flag currently requires a "0x" prefix
for all values passed to it, using "actionid" as value is a backwards
compatible change.

Fixes golang#41004
@gopherbot
Copy link

Change https://go.dev/cl/511475 mentions this issue: cmd/link: allow deriving GNU build ID from action ID

pks-t added a commit to pks-t/go that referenced this issue Jul 25, 2023
While it is possible to embed a GNU build ID into the linked executable
by passing `-B 0xBUILDID` to the linker, the build ID will need to be
precomputed by the build system somehow. This makes it unnecessarily
complex to generate a deterministic build ID as it either requires the
biuld system to hash all inputs manually or to build the binary twice,
once to compute its hash and once with the GNU build ID derived from
that hash. Despite being complex, it is also inefficient as it requires
the build system to duplicate some of the work that the Go linker
already performs anyway.

Derive the GNU build ID automatically from the Go build ID whenever the
latter has been provided. Given that the Go build ID is intended to be
deterministic, the resulting GNU build ID should be deterministic as
well, which is the desired behaviour. Users can continue to override the
derived GNU build ID by passing a custom value via the `-B` switch.

Fixes golang#41004
pks-t added a commit to pks-t/go that referenced this issue Jul 27, 2023
While it is possible to embed a GNU build ID into the linked executable
by passing `-B 0xBUILDID` to the linker, the build ID will need to be
precomputed by the build system somehow. This makes it unnecessarily
complex to generate a deterministic build ID as it either requires the
biuld system to hash all inputs manually or to build the binary twice,
once to compute its hash and once with the GNU build ID derived from
that hash. Despite being complex, it is also inefficient as it requires
the build system to duplicate some of the work that the Go linker
already performs anyway.

Introduce a new argument "gobuildid" that can be passed to `-B` that
causes the linker to automatically derive the GNU build ID from the Go
build ID. Given that the Go build ID is deterministically computed from
all of its inputs, the resulting GNU build ID should be deterministic in
the same way, which is the desired behaviour.

Furthermore, given that the `-B` flag currently requires a "0x" prefix
for all values passed to it, using "gobuildid" as value is a backwards
compatible change.

An alternative would be to unconditionally calculate the GNU build ID
unless otherwise specified. This would require some larger rework though
because building the Go toolchain would not converge anymore due the GNU
build ID changing on every stage, which in turn would cause the Go build
ID to change as well.

Fixes golang#41004
@Foxboron
Copy link
Contributor

Foxboron commented Aug 5, 2023

@pks-t any reason why the option can't be enabled by default? Now that things are deterministic it should be do-able.

This would also make it simpler to have proper debug packages for downstreams then trying to pass the option through the Go flags/option shenanigans.

@pks-t
Copy link
Contributor

pks-t commented Aug 7, 2023

pks-t added a commit to pks-t/go that referenced this issue Aug 7, 2023
While it is possible to embed a GNU build ID into the linked
executable by passing `-B 0xBUILDID` to the linker, the build ID will
need to be precomputed by the build system somehow. This makes it
unnecessarily complex to generate a deterministic build ID as it
either requires the build system to hash all inputs manually or to
build the binary twice, once to compute its hash and once with the GNU
build ID derived from that hash. Despite being complex, it is also
inefficient as it requires the build system to duplicate some of the
work that the Go linker already performs anyway.

Introduce a new argument "gobuildid" that can be passed to `-B` that
causes the linker to automatically derive the GNU build ID from the Go
build ID. Given that the Go build ID is deterministically computed
from all of its inputs, the resulting GNU build ID should be
deterministic in the same way, which is the desired behaviour.

Furthermore, given that the `-B` flag currently requires a "0x" prefix
for all values passed to it, using "gobuildid" as value is a backwards
compatible change.

An alternative would be to unconditionally calculate the GNU build ID
unless otherwise specified. This would require some larger rework
though because building the Go toolchain would not converge anymore
due the GNU build ID changing on every stage, which in turn would
cause the Go build ID to change as well.

Fixes golang#41004
pks-t added a commit to pks-t/go that referenced this issue Sep 15, 2023
While it is possible to embed a GNU build ID into the linked
executable by passing `-B 0xBUILDID` to the linker, the build ID will
need to be precomputed by the build system somehow. This makes it
unnecessarily complex to generate a deterministic build ID as it
either requires the build system to hash all inputs manually or to
build the binary twice, once to compute its hash and once with the GNU
build ID derived from that hash. Despite being complex, it is also
inefficient as it requires the build system to duplicate some of the
work that the Go linker already performs anyway.

Introduce a new argument "gobuildid" that can be passed to `-B` that
causes the linker to automatically derive the GNU build ID from the Go
build ID. Given that the Go build ID is deterministically computed
from all of its inputs, the resulting GNU build ID should be
deterministic in the same way, which is the desired behaviour.

Furthermore, given that the `-B` flag currently requires a "0x" prefix
for all values passed to it, using "gobuildid" as value is a backwards
compatible change.

An alternative would be to unconditionally calculate the GNU build ID
unless otherwise specified. This would require some larger rework
though because building the Go toolchain would not converge anymore
due the GNU build ID changing on every stage, which in turn would
cause the Go build ID to change as well.

Fixes golang#41004
pks-t added a commit to pks-t/go that referenced this issue Sep 16, 2023
While it is possible to embed a GNU build ID into the linked
executable by passing `-B 0xBUILDID` to the linker, the build ID will
need to be precomputed by the build system somehow. This makes it
unnecessarily complex to generate a deterministic build ID as it
either requires the build system to hash all inputs manually or to
build the binary twice, once to compute its hash and once with the GNU
build ID derived from that hash. Despite being complex, it is also
inefficient as it requires the build system to duplicate some of the
work that the Go linker already performs anyway.

Introduce a new argument "gobuildid" that can be passed to `-B` that
causes the linker to automatically derive the GNU build ID from the Go
build ID. Given that the Go build ID is deterministically computed
from all of its inputs, the resulting GNU build ID should be
deterministic in the same way, which is the desired behaviour.

Furthermore, given that the `-B` flag currently requires a "0x" prefix
for all values passed to it, using "gobuildid" as value is a backwards
compatible change.

An alternative would be to unconditionally calculate the GNU build ID
unless otherwise specified. This would require some larger rework
though because building the Go toolchain would not converge anymore
due the GNU build ID changing on every stage, which in turn would
cause the Go build ID to change as well.

Fixes golang#41004
@znkr
Copy link
Contributor

znkr commented Sep 19, 2023

I was looking forward to using this to get a Go compiler with a build ID, so I tried using it to build Go. Unfortunately that fails:

 % GO_LDFLAGS="-B gobuildid" ./make.bash
Building Go cmd/dist using /usr/lib/google-golang. (go1.22-20230729-RC00 cl/552016856 +457721cd52 X:fieldtrack,bo
ringcrypto linux/amd64)
Building Go toolchain1 using /usr/lib/google-golang.
Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.
Building Go toolchain2 using go_bootstrap and Go toolchain1.
Building Go toolchain3 using go_bootstrap and Go toolchain2.
Building packages and commands for linux/amd64.
HASH[moduleIndex]
HASH[moduleIndex]: "devel go1.22-eca5a97340 Tue Sep 19 05:51:26 2023 +0000"
HASH[moduleIndex]: "modroot [RECACTED]/dev/go/src/cmd\n"
...
HASH[moduleIndex]: "file template.go 2023-09-19 12:37:51.569044164 +0000 UTC 7335\n"
HASH[moduleIndex]: 5cf632a5d0cc7bcf669b75b3abc54664c46ea44ca853a3d5812f5efccf086406
cmd/link true
go tool dist: unexpected stale targets reported by  [RECACTED]/dev/go/pkg/tool/linux_amd64/go_bootstrap list -gcflags="" -ldflags="-B gobuildid" for [cmd/asm cmd/cgo cmd/compile cmd/link] (consider rerunning with GOMAXPROCS=1 GODEBUG=gocachehash=1):
        STALE cmd/asm: stale dependency: internal/goarch
        STALE cmd/cgo: stale dependency: internal/goarch
        STALE cmd/compile: stale dependency: internal/goarch
        STALE cmd/link: stale dependency: internal/goarch

Any chance this could be made to work?

@pks-t
Copy link
Contributor

pks-t commented Sep 19, 2023

Any chance this could be made to work?

@znkr Making this work for the Go compiler itself is more complicated than the current iteration. This was discussed at https://go-review.googlesource.com/c/go/+/511475/1..9/src/cmd/link/internal/ld/elf.go#b819, where the agreement was to push this out to another iteration.

@znkr
Copy link
Contributor

znkr commented Sep 22, 2023

I see, thanks for the clarification. Is there an issue I can follow for the next iteration?

@pks-t
Copy link
Contributor

pks-t commented Sep 30, 2023 via email

@znkr
Copy link
Contributor

znkr commented Nov 3, 2023

Filed #63934 just now. Sorry for the delay

@gopherbot
Copy link

Change https://go.dev/cl/547515 mentions this issue: doc/go1.22: document linker flag changes

gopherbot pushed a commit that referenced this issue Dec 6, 2023
Updates #41004.
For #61422.

Change-Id: I5ab60d8e9d30986233d7adec400ef059bbe4c1a9
Reviewed-on: https://go-review.googlesource.com/c/go/+/547515
Reviewed-by: Than McIntosh <thanm@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
ezz-no pushed a commit to ezz-no/go-ezzno that referenced this issue Feb 18, 2024
Updates golang#41004.
For golang#61422.

Change-Id: I5ab60d8e9d30986233d7adec400ef059bbe4c1a9
Reviewed-on: https://go-review.googlesource.com/c/go/+/547515
Reviewed-by: Than McIntosh <thanm@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler/runtime Issues related to the Go compiler and/or runtime. FeatureRequest NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
9 participants