diff --git a/src/cmd/link/doc.go b/src/cmd/link/doc.go index ce0166faa5c..c5f43a29544 100644 --- a/src/cmd/link/doc.go +++ b/src/cmd/link/doc.go @@ -18,6 +18,8 @@ Flags: -B note Add an ELF_NT_GNU_BUILD_ID note when using ELF. The value should start with 0x and be an even number of hex digits. + Alternatively, you can pass "gobuildid" in order to derive the + GNU build ID from the Go build ID. -E entry Set entry symbol name. -H type diff --git a/src/cmd/link/elf_test.go b/src/cmd/link/elf_test.go index 902ce28b104..5dcef1cc22b 100644 --- a/src/cmd/link/elf_test.go +++ b/src/cmd/link/elf_test.go @@ -7,6 +7,10 @@ package main import ( + "bytes" + "cmd/internal/buildid" + "cmd/internal/notsha256" + "cmd/link/internal/ld" "debug/elf" "fmt" "internal/platform" @@ -199,6 +203,39 @@ func TestMinusRSymsWithSameName(t *testing.T) { } } +func TestGNUBuildIDDerivedFromGoBuildID(t *testing.T) { + testenv.MustHaveGoBuild(t) + + t.Parallel() + + goFile := filepath.Join(t.TempDir(), "notes.go") + if err := os.WriteFile(goFile, []byte(goSource), 0444); err != nil { + t.Fatal(err) + } + outFile := filepath.Join(t.TempDir(), "notes.exe") + goTool := testenv.GoToolPath(t) + + cmd := testenv.Command(t, goTool, "build", "-o", outFile, "-ldflags", "-buildid 0x1234 -B gobuildid", goFile) + cmd.Dir = t.TempDir() + + out, err := cmd.CombinedOutput() + if err != nil { + t.Logf("%s", out) + t.Fatal(err) + } + + expectedGoBuildID := notsha256.Sum256([]byte("0x1234")) + + gnuBuildID, err := buildid.ReadELFNote(outFile, string(ld.ELF_NOTE_BUILDINFO_NAME), ld.ELF_NOTE_BUILDINFO_TAG) + if err != nil || gnuBuildID == nil { + t.Fatalf("can't read GNU build ID") + } + + if !bytes.Equal(gnuBuildID, expectedGoBuildID[:20]) { + t.Fatalf("build id not matching") + } +} + func TestMergeNoteSections(t *testing.T) { testenv.MustHaveGoBuild(t) expected := 1 diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index a3f99b19608..be9e22946a3 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -806,6 +806,18 @@ func elfwritefreebsdsig(out *OutBuf) int { } func addbuildinfo(val string) { + if val == "gobuildid" { + buildID := *flagBuildid + if buildID == "" { + Exitf("-B gobuildid requires a Go build ID supplied via -buildid") + } + + hashedBuildID := notsha256.Sum256([]byte(buildID)) + buildinfo = hashedBuildID[:20] + + return + } + if !strings.HasPrefix(val, "0x") { Exitf("-B argument must start with 0x: %s", val) } diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index b978cfc7d49..d916bff5292 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -190,7 +190,7 @@ func Main(arch *sys.Arch, theArch Arch) { flag.Var(&ctxt.LinkMode, "linkmode", "set link `mode`") flag.Var(&ctxt.BuildMode, "buildmode", "set build `mode`") flag.BoolVar(&ctxt.compressDWARF, "compressdwarf", true, "compress DWARF if possible") - objabi.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF", addbuildinfo) + objabi.Flagfn1("B", "add an ELF NT_GNU_BUILD_ID `note` when using ELF; use \"gobuildid\" to generate it from the Go build ID", addbuildinfo) objabi.Flagfn1("L", "add specified `directory` to library path", func(a string) { Lflag(ctxt, a) }) objabi.AddVersionFlag() // -V objabi.Flagfn1("X", "add string value `definition` of the form importpath.name=value", func(s string) { addstrdata1(ctxt, s) })