Skip to content

Commit c15e589

Browse files
committed
cmd/link: apply -B UUID to external linking on Mach-O
Currently, on Mach-O, the -B UUID setting is only applied in internal linking mode, whereas in external linking mode the UUID is always rewritten to a hash of Go build ID. This CL makes it apply to external linking as well. This makes the behavior consistent on both linkmodes, and also consistent with the -B flag's behavior for GNU build ID on ELF. Add tests. Updates #68678. Cq-Include-Trybots: luci.golang.try:gotip-darwin-amd64_14,gotip-darwin-arm64_13 Change-Id: I276a5930e231141440cdba16e8812df28ac4237b Reviewed-on: https://go-review.googlesource.com/c/go/+/618599 Reviewed-by: Than McIntosh <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent 6a4feb5 commit c15e589

File tree

4 files changed

+83
-6
lines changed

4 files changed

+83
-6
lines changed

src/cmd/link/internal/ld/lib.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,9 @@ func (ctxt *Link) hostlink() {
14681468
argv = append(argv, "-Wl,-x")
14691469
}
14701470
}
1471+
if *flagHostBuildid == "none" {
1472+
argv = append(argv, "-Wl,-no_uuid")
1473+
}
14711474
case objabi.Hopenbsd:
14721475
argv = append(argv, "-pthread")
14731476
if ctxt.BuildMode != BuildModePIE {
@@ -2059,7 +2062,7 @@ func (ctxt *Link) hostlink() {
20592062
uuidUpdated = true
20602063
}
20612064
}
2062-
if ctxt.IsDarwin() && !uuidUpdated && *flagBuildid != "" {
2065+
if ctxt.IsDarwin() && !uuidUpdated && len(buildinfo) > 0 {
20632066
updateMachoOutFile("rewriting uuid",
20642067
func(ctxt *Link, exef *os.File, exem *macho.File, outexe string) error {
20652068
return machoRewriteUuid(ctxt, exef, exem, outexe)

src/cmd/link/internal/ld/macho_combine_dwarf.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,9 @@ func machoCombineDwarf(ctxt *Link, exef *os.File, exem *macho.File, dsym, outexe
195195
case imacho.LC_UUID:
196196
var u uuidCmd
197197
err = reader.ReadAt(0, &u)
198-
if err == nil {
199-
copy(u.Uuid[:], uuidFromGoBuildId(*flagBuildid))
198+
if err == nil && len(buildinfo) > 0 {
199+
clear(u.Uuid[:])
200+
copy(u.Uuid[:], buildinfo)
200201
err = reader.WriteAt(0, &u)
201202
}
202203
case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread,

src/cmd/link/internal/ld/macho_update_uuid.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ func machoRewriteUuid(ctxt *Link, exef *os.File, exem *macho.File, outexe string
8686
if err := reader.ReadAt(0, &u); err != nil {
8787
return err
8888
}
89-
copy(u.Uuid[:], uuidFromGoBuildId(*flagBuildid))
89+
clear(u.Uuid[:])
90+
copy(u.Uuid[:], buildinfo)
9091
if err := reader.WriteAt(0, &u); err != nil {
9192
return err
9293
}

src/cmd/link/link_test.go

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"strings"
2020
"testing"
2121

22+
imacho "cmd/internal/macho"
2223
"cmd/internal/sys"
2324
)
2425

@@ -386,7 +387,6 @@ func TestMachOBuildVersion(t *testing.T) {
386387
t.Fatal(err)
387388
}
388389
found := false
389-
const LC_BUILD_VERSION = 0x32
390390
checkMin := func(ver uint32) {
391391
major, minor, patch := (ver>>16)&0xff, (ver>>8)&0xff, (ver>>0)&0xff
392392
if major < 11 {
@@ -396,7 +396,7 @@ func TestMachOBuildVersion(t *testing.T) {
396396
for _, cmd := range exem.Loads {
397397
raw := cmd.Raw()
398398
type_ := exem.ByteOrder.Uint32(raw)
399-
if type_ != LC_BUILD_VERSION {
399+
if type_ != imacho.LC_BUILD_VERSION {
400400
continue
401401
}
402402
osVer := exem.ByteOrder.Uint32(raw[12:])
@@ -411,6 +411,78 @@ func TestMachOBuildVersion(t *testing.T) {
411411
}
412412
}
413413

414+
func TestMachOUUID(t *testing.T) {
415+
testenv.MustHaveGoBuild(t)
416+
if runtime.GOOS != "darwin" {
417+
t.Skip("this is only for darwin")
418+
}
419+
420+
t.Parallel()
421+
422+
tmpdir := t.TempDir()
423+
424+
src := filepath.Join(tmpdir, "main.go")
425+
err := os.WriteFile(src, []byte(trivialSrc), 0666)
426+
if err != nil {
427+
t.Fatal(err)
428+
}
429+
430+
extractUUID := func(exe string) string {
431+
exem, err := macho.Open(exe)
432+
if err != nil {
433+
t.Fatal(err)
434+
}
435+
defer exem.Close()
436+
for _, cmd := range exem.Loads {
437+
raw := cmd.Raw()
438+
type_ := exem.ByteOrder.Uint32(raw)
439+
if type_ != imacho.LC_UUID {
440+
continue
441+
}
442+
return string(raw[8:24])
443+
}
444+
return ""
445+
}
446+
447+
tests := []struct{ name, ldflags, expect string }{
448+
{"default", "", "gobuildid"},
449+
{"gobuildid", "-B=gobuildid", "gobuildid"},
450+
{"specific", "-B=0x0123456789ABCDEF0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
451+
{"none", "-B=none", ""},
452+
}
453+
if testenv.HasCGO() {
454+
for _, test := range tests {
455+
t1 := test
456+
t1.name += "_external"
457+
t1.ldflags += " -linkmode=external"
458+
tests = append(tests, t1)
459+
}
460+
}
461+
for _, test := range tests {
462+
t.Run(test.name, func(t *testing.T) {
463+
exe := filepath.Join(tmpdir, test.name)
464+
cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags="+test.ldflags, "-o", exe, src)
465+
if out, err := cmd.CombinedOutput(); err != nil {
466+
t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
467+
}
468+
uuid := extractUUID(exe)
469+
if test.expect == "gobuildid" {
470+
// Go buildid is not known in source code. Check UUID is present,
471+
// and satisifies UUIDv3.
472+
if uuid == "" {
473+
t.Fatal("expect nonempty UUID, got empty")
474+
}
475+
// The version number is the high 4 bits of byte 6.
476+
if uuid[6]>>4 != 3 {
477+
t.Errorf("expect v3 UUID, got %X (version %d)", uuid, uuid[6]>>4)
478+
}
479+
} else if uuid != test.expect {
480+
t.Errorf("UUID mismatch: got %X, want %X", uuid, test.expect)
481+
}
482+
})
483+
}
484+
}
485+
414486
const Issue34788src = `
415487
416488
package blah

0 commit comments

Comments
 (0)