]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: apply -B UUID to external linking on Mach-O
authorCherry Mui <cherryyz@google.com>
Tue, 8 Oct 2024 17:17:47 +0000 (13:17 -0400)
committerCherry Mui <cherryyz@google.com>
Mon, 21 Oct 2024 17:56:41 +0000 (17:56 +0000)
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 <thanm@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
src/cmd/link/internal/ld/lib.go
src/cmd/link/internal/ld/macho_combine_dwarf.go
src/cmd/link/internal/ld/macho_update_uuid.go
src/cmd/link/link_test.go

index e43fc11b12095d4b61e700df260dd1b746f518ae..cbae6dda172ebad2a0818275e8647a67b37546a3 100644 (file)
@@ -1468,6 +1468,9 @@ func (ctxt *Link) hostlink() {
                                argv = append(argv, "-Wl,-x")
                        }
                }
+               if *flagHostBuildid == "none" {
+                       argv = append(argv, "-Wl,-no_uuid")
+               }
        case objabi.Hopenbsd:
                argv = append(argv, "-pthread")
                if ctxt.BuildMode != BuildModePIE {
@@ -2059,7 +2062,7 @@ func (ctxt *Link) hostlink() {
                        uuidUpdated = true
                }
        }
-       if ctxt.IsDarwin() && !uuidUpdated && *flagBuildid != "" {
+       if ctxt.IsDarwin() && !uuidUpdated && len(buildinfo) > 0 {
                updateMachoOutFile("rewriting uuid",
                        func(ctxt *Link, exef *os.File, exem *macho.File, outexe string) error {
                                return machoRewriteUuid(ctxt, exef, exem, outexe)
index 66c405746b359de1647d84b073f3438beb0e4f00..d60755f147d51174fb6d5660fbab9280c88f85e1 100644 (file)
@@ -195,8 +195,9 @@ func machoCombineDwarf(ctxt *Link, exef *os.File, exem *macho.File, dsym, outexe
                case imacho.LC_UUID:
                        var u uuidCmd
                        err = reader.ReadAt(0, &u)
-                       if err == nil {
-                               copy(u.Uuid[:], uuidFromGoBuildId(*flagBuildid))
+                       if err == nil && len(buildinfo) > 0 {
+                               clear(u.Uuid[:])
+                               copy(u.Uuid[:], buildinfo)
                                err = reader.WriteAt(0, &u)
                        }
                case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread,
index 607cdad996a8dd57bc6d81f5016d5f9d63872c74..968bd7bb2554af9d3f6e189982d609469ae20c3c 100644 (file)
@@ -86,7 +86,8 @@ func machoRewriteUuid(ctxt *Link, exef *os.File, exem *macho.File, outexe string
                        if err := reader.ReadAt(0, &u); err != nil {
                                return err
                        }
-                       copy(u.Uuid[:], uuidFromGoBuildId(*flagBuildid))
+                       clear(u.Uuid[:])
+                       copy(u.Uuid[:], buildinfo)
                        if err := reader.WriteAt(0, &u); err != nil {
                                return err
                        }
index 5fb73c4554234e83512e0450f0782dd7f25247d1..deadc7955dbab9a1692b905996e3a039c1262741 100644 (file)
@@ -19,6 +19,7 @@ import (
        "strings"
        "testing"
 
+       imacho "cmd/internal/macho"
        "cmd/internal/sys"
 )
 
@@ -386,7 +387,6 @@ func TestMachOBuildVersion(t *testing.T) {
                t.Fatal(err)
        }
        found := false
-       const LC_BUILD_VERSION = 0x32
        checkMin := func(ver uint32) {
                major, minor, patch := (ver>>16)&0xff, (ver>>8)&0xff, (ver>>0)&0xff
                if major < 11 {
@@ -396,7 +396,7 @@ func TestMachOBuildVersion(t *testing.T) {
        for _, cmd := range exem.Loads {
                raw := cmd.Raw()
                type_ := exem.ByteOrder.Uint32(raw)
-               if type_ != LC_BUILD_VERSION {
+               if type_ != imacho.LC_BUILD_VERSION {
                        continue
                }
                osVer := exem.ByteOrder.Uint32(raw[12:])
@@ -411,6 +411,78 @@ func TestMachOBuildVersion(t *testing.T) {
        }
 }
 
+func TestMachOUUID(t *testing.T) {
+       testenv.MustHaveGoBuild(t)
+       if runtime.GOOS != "darwin" {
+               t.Skip("this is only for darwin")
+       }
+
+       t.Parallel()
+
+       tmpdir := t.TempDir()
+
+       src := filepath.Join(tmpdir, "main.go")
+       err := os.WriteFile(src, []byte(trivialSrc), 0666)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       extractUUID := func(exe string) string {
+               exem, err := macho.Open(exe)
+               if err != nil {
+                       t.Fatal(err)
+               }
+               defer exem.Close()
+               for _, cmd := range exem.Loads {
+                       raw := cmd.Raw()
+                       type_ := exem.ByteOrder.Uint32(raw)
+                       if type_ != imacho.LC_UUID {
+                               continue
+                       }
+                       return string(raw[8:24])
+               }
+               return ""
+       }
+
+       tests := []struct{ name, ldflags, expect string }{
+               {"default", "", "gobuildid"},
+               {"gobuildid", "-B=gobuildid", "gobuildid"},
+               {"specific", "-B=0x0123456789ABCDEF0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
+               {"none", "-B=none", ""},
+       }
+       if testenv.HasCGO() {
+               for _, test := range tests {
+                       t1 := test
+                       t1.name += "_external"
+                       t1.ldflags += " -linkmode=external"
+                       tests = append(tests, t1)
+               }
+       }
+       for _, test := range tests {
+               t.Run(test.name, func(t *testing.T) {
+                       exe := filepath.Join(tmpdir, test.name)
+                       cmd := testenv.Command(t, testenv.GoToolPath(t), "build", "-ldflags="+test.ldflags, "-o", exe, src)
+                       if out, err := cmd.CombinedOutput(); err != nil {
+                               t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
+                       }
+                       uuid := extractUUID(exe)
+                       if test.expect == "gobuildid" {
+                               // Go buildid is not known in source code. Check UUID is present,
+                               // and satisifies UUIDv3.
+                               if uuid == "" {
+                                       t.Fatal("expect nonempty UUID, got empty")
+                               }
+                               // The version number is the high 4 bits of byte 6.
+                               if uuid[6]>>4 != 3 {
+                                       t.Errorf("expect v3 UUID, got %X (version %d)", uuid, uuid[6]>>4)
+                               }
+                       } else if uuid != test.expect {
+                               t.Errorf("UUID mismatch: got %X, want %X", uuid, test.expect)
+                       }
+               })
+       }
+}
+
 const Issue34788src = `
 
 package blah