mirror of
https://github.com/golang/go.git
synced 2026-04-03 17:59:55 +09:00
cmd/link: add -macos and -macsdk flags to set LC_BUILD_VERSION
These are for debugging problems with the build versions in the load commands. We still want to set them correctly by default for most users, provided we can determine what that means. Fixes #58722. Change-Id: Iccab7044ac7b0c58e7e01258a5e374c4155528fc Reviewed-on: https://go-review.googlesource.com/c/go/+/751260 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
@@ -878,7 +878,7 @@ func addbuildinfo(ctxt *Link) {
|
||||
}
|
||||
|
||||
if ctxt.IsDarwin() {
|
||||
buildinfo = uuidFromGoBuildId(buildID)
|
||||
buildinfo = uuidFromHash(hash.Sum32([]byte(buildID)))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ package ld
|
||||
import (
|
||||
"bytes"
|
||||
"cmd/internal/codesign"
|
||||
"cmd/internal/hash"
|
||||
imacho "cmd/internal/macho"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/sys"
|
||||
@@ -19,6 +20,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -404,6 +406,50 @@ func machowrite(ctxt *Link, arch *sys.Arch, out *OutBuf, linkmode LinkMode) int
|
||||
return int(out.Offset() - o1)
|
||||
}
|
||||
|
||||
type macVersionFlag [3]byte
|
||||
|
||||
func (f *macVersionFlag) String() string {
|
||||
return fmt.Sprintf("%d.%d.%d", f[0], f[1], f[2])
|
||||
}
|
||||
|
||||
func (f *macVersionFlag) Set(s string) error {
|
||||
var parsed macVersionFlag
|
||||
nums := strings.Split(s, ".")
|
||||
if len(nums) > 3 {
|
||||
goto Error
|
||||
}
|
||||
for i, num := range nums {
|
||||
n, err := strconv.Atoi(num)
|
||||
if err != nil || n < 0 || n > 0xFF {
|
||||
goto Error
|
||||
}
|
||||
parsed[i] = byte(n)
|
||||
}
|
||||
// success, now modify f
|
||||
*f = parsed
|
||||
return nil
|
||||
|
||||
Error:
|
||||
return fmt.Errorf("invalid version %q", s)
|
||||
}
|
||||
|
||||
func (f *macVersionFlag) version() uint32 {
|
||||
return uint32(f[0])<<16 | uint32(f[1])<<8 | uint32(f[2])
|
||||
}
|
||||
|
||||
var (
|
||||
// On advice from Apple engineers, we keep macOS set to the
|
||||
// oldest supported macOS version but keep macSDK to the newest
|
||||
// tested OS/SDK version. If these defaults are not good enough,
|
||||
// the -macos and -macsdk linker flags can override them.
|
||||
// For past problems involving these values, see
|
||||
// go.dev/issue/30488
|
||||
// go.dev/issue/56784
|
||||
// go.dev/issue/77917
|
||||
macOS = macVersionFlag{12, 0, 0}
|
||||
macSDK = macVersionFlag{26, 2, 0}
|
||||
)
|
||||
|
||||
func (ctxt *Link) domacho() {
|
||||
if *FlagD {
|
||||
return
|
||||
@@ -419,6 +465,10 @@ func (ctxt *Link) domacho() {
|
||||
machoPlatform = load.platform
|
||||
ml := newMachoLoad(ctxt.Arch, load.cmd.type_, uint32(len(load.cmd.data)))
|
||||
copy(ml.data, load.cmd.data)
|
||||
if machoPlatform == PLATFORM_MACOS {
|
||||
ml.data[1] = macOS.version()
|
||||
ml.data[2] = macSDK.version()
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -428,22 +478,11 @@ func (ctxt *Link) domacho() {
|
||||
machoPlatform = PLATFORM_IOS
|
||||
}
|
||||
if ctxt.LinkMode == LinkInternal && machoPlatform == PLATFORM_MACOS {
|
||||
var version uint32
|
||||
switch ctxt.Arch.Family {
|
||||
case sys.ARM64, sys.AMD64:
|
||||
// This must be fairly recent for Apple signing (go.dev/issue/30488).
|
||||
// Having too old a version here was also implicated in some problems
|
||||
// calling into macOS libraries (go.dev/issue/56784).
|
||||
// CL 460476 noted that in general this can be the most recent supported
|
||||
// macOS version, but we haven't tested if going higher than Go's oldest
|
||||
// supported macOS version could cause new problems.
|
||||
version = 12<<16 | 0<<8 | 0<<0 // 12.0.0
|
||||
}
|
||||
ml := newMachoLoad(ctxt.Arch, imacho.LC_BUILD_VERSION, 4)
|
||||
ml.data[0] = uint32(machoPlatform)
|
||||
ml.data[1] = version // OS version
|
||||
ml.data[2] = version // SDK version
|
||||
ml.data[3] = 0 // ntools
|
||||
ml.data[1] = macOS.version()
|
||||
ml.data[2] = macSDK.version()
|
||||
ml.data[3] = 0 // ntools
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,18 +845,24 @@ func asmbMacho(ctxt *Link) {
|
||||
}
|
||||
}
|
||||
|
||||
if ctxt.IsInternal() && len(buildinfo) > 0 {
|
||||
if ctxt.IsInternal() && *flagHostBuildid != "none" {
|
||||
ml := newMachoLoad(ctxt.Arch, imacho.LC_UUID, 4)
|
||||
// Mach-O UUID is 16 bytes
|
||||
if len(buildinfo) < 16 {
|
||||
buildinfo = append(buildinfo, make([]byte, 16)...)
|
||||
var uuid [16]byte
|
||||
if len(buildinfo) >= 16 {
|
||||
copy(uuid[:], buildinfo)
|
||||
} else {
|
||||
// Note: When setting macSDK to 26.2, dyld refuses to run any
|
||||
// binary without an LC_UUID, which makes bootstrap fail.
|
||||
// To work around that situation, if buildinfo is missing we
|
||||
// construct a hash of the binary written so far and use that.
|
||||
// Using -B none will bypass this if desired,
|
||||
// but the resulting binary may not be runnable.
|
||||
copy(uuid[:], uuidFromHash(hash.Sum32(ctxt.Out.Data())))
|
||||
}
|
||||
// By default, buildinfo is already in UUIDv3 format
|
||||
// (see uuidFromGoBuildId).
|
||||
ml.data[0] = ctxt.Arch.ByteOrder.Uint32(buildinfo)
|
||||
ml.data[1] = ctxt.Arch.ByteOrder.Uint32(buildinfo[4:])
|
||||
ml.data[2] = ctxt.Arch.ByteOrder.Uint32(buildinfo[8:])
|
||||
ml.data[3] = ctxt.Arch.ByteOrder.Uint32(buildinfo[12:])
|
||||
ml.data[0] = ctxt.Arch.ByteOrder.Uint32(uuid[0:])
|
||||
ml.data[1] = ctxt.Arch.ByteOrder.Uint32(uuid[4:])
|
||||
ml.data[2] = ctxt.Arch.ByteOrder.Uint32(uuid[8:])
|
||||
ml.data[3] = ctxt.Arch.ByteOrder.Uint32(uuid[12:])
|
||||
}
|
||||
|
||||
if ctxt.IsInternal() && ctxt.NeedCodeSign() {
|
||||
|
||||
@@ -18,7 +18,6 @@ package ld
|
||||
// final executable generated by the external linker.
|
||||
|
||||
import (
|
||||
"cmd/internal/hash"
|
||||
imacho "cmd/internal/macho"
|
||||
|
||||
"debug/macho"
|
||||
@@ -26,27 +25,19 @@ import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// uuidFromGoBuildId hashes the Go build ID and returns a slice of 16
|
||||
// bytes suitable for use as the payload in a Macho LC_UUID load
|
||||
// command.
|
||||
func uuidFromGoBuildId(buildID string) []byte {
|
||||
if buildID == "" {
|
||||
return make([]byte, 16)
|
||||
}
|
||||
hashedBuildID := hash.Sum32([]byte(buildID))
|
||||
rv := hashedBuildID[:16]
|
||||
// uuidFromHash converts a hash to a UUID
|
||||
// suitable for use in the Macho LC_UUID load command.
|
||||
func uuidFromHash(hashed [32]byte) []byte {
|
||||
rv := make([]byte, 16)
|
||||
copy(rv, hashed[:16])
|
||||
|
||||
// RFC 4122 conformance (see RFC 4122 Sections 4.2.2, 4.1.3). We
|
||||
// want the "version" of this UUID to appear as 'hashed' as opposed
|
||||
// to random or time-based. This is something of a fiction since
|
||||
// we're not actually hashing using MD5 or SHA1, but it seems better
|
||||
// to use this UUID flavor than any of the others. This is similar
|
||||
// to how other linkers handle this (for example this code in lld:
|
||||
// https://github.com/llvm/llvm-project/blob/2a3a79ce4c2149d7787d56f9841b66cacc9061d0/lld/MachO/Writer.cpp#L524).
|
||||
rv[6] &= 0x0f
|
||||
rv[6] |= 0x30
|
||||
rv[8] &= 0x3f
|
||||
rv[8] |= 0xc0
|
||||
// Mark the UUID as version 3, variant 1, which is an MD5-hash-based UUID.
|
||||
// We are not actually using MD5, but we're also not using SHA1 (version 5),
|
||||
// and LLVM also uses version 3 [1] so this seems good enough.
|
||||
//
|
||||
// [1] https://github.com/llvm/llvm-project/blob/2a3a79ce4c2149d7787d56f9841b66cacc9061d0/lld/MachO/Writer.cpp#L524
|
||||
rv[6] = (rv[6] &^ 0xF0) | 0x30 // version 3
|
||||
rv[8] = (rv[8] &^ 0xC0) | 0x80 // variant 1
|
||||
|
||||
return rv
|
||||
}
|
||||
@@ -74,7 +65,7 @@ func machoRewriteUuid(ctxt *Link, exef *os.File, exem *macho.File, outexe string
|
||||
|
||||
// Read the load commands, looking for the LC_UUID cmd. If/when we
|
||||
// locate it, overwrite it with a new value produced by
|
||||
// uuidFromGoBuildId.
|
||||
// uuidFromHash.
|
||||
reader := imacho.NewLoadCmdUpdater(outf, exem.ByteOrder, cmdOffset)
|
||||
for i := uint32(0); i < exem.Ncmd; i++ {
|
||||
cmd, err := reader.Next()
|
||||
|
||||
@@ -55,9 +55,11 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...")
|
||||
flag.Var(&flagExtld, "extld", "use `linker` when linking in external mode")
|
||||
flag.Var(&flagExtldflags, "extldflags", "pass `flags` to external linker")
|
||||
flag.Var(&macOS, "macos", "mac OS version to write in build info (only used in internal linking)")
|
||||
flag.Var(&macSDK, "macsdk", "mac SDK version to write in build info (only used in internal linking)")
|
||||
flag.Var(&rpath, "r", "set the ELF dynamic linker search `path` to dir1:dir2:...")
|
||||
flag.Var(&flagW, "w", "disable DWARF generation")
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user