]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: support internal linking on darwin/arm64
authorCherry Zhang <cherryyz@google.com>
Mon, 12 Oct 2020 17:44:21 +0000 (13:44 -0400)
committerCherry Zhang <cherryyz@google.com>
Wed, 14 Oct 2020 21:32:26 +0000 (21:32 +0000)
Add support of internal linking on darwin/arm64 (macOS).

Still incomplete. Pure Go binaries work. Cgo doesn't. TLS is not
set up when cgo is not used (as before) (so asynchronous
preemption is not enabled).

Internal linking is not enabled by default but can be requested
via -ldflags=-linkmode=internal.

Updates #38485.

Change-Id: I1e0c81b6028edcb1ac26dcdafeb9bb3f788cf732
Reviewed-on: https://go-review.googlesource.com/c/go/+/261643
Trust: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
src/cmd/go/internal/load/pkg.go
src/cmd/internal/sys/supported.go
src/cmd/link/internal/arm64/asm.go
src/cmd/link/internal/arm64/obj.go
src/cmd/link/internal/ld/config.go
src/cmd/link/internal/ld/macho.go
src/cmd/link/internal/ld/main.go
src/runtime/rt0_darwin_arm64.s

index db2434260fa60a3fc19023c1a5790e18dc16287a..f73b79d089b89e4441c7d99e9c8e138762be02f6 100644 (file)
@@ -1948,6 +1948,10 @@ func LinkerDeps(p *Package) []string {
 // externalLinkingForced reports whether external linking is being
 // forced even for programs that do not use cgo.
 func externalLinkingForced(p *Package) bool {
+       if !cfg.BuildContext.CgoEnabled {
+               return false
+       }
+
        // Some targets must use external linking even inside GOROOT.
        switch cfg.BuildContext.GOOS {
        case "android":
@@ -1960,9 +1964,6 @@ func externalLinkingForced(p *Package) bool {
                }
        }
 
-       if !cfg.BuildContext.CgoEnabled {
-               return false
-       }
        // Currently build modes c-shared, pie (on systems that do not
        // support PIE with internal linking mode (currently all
        // systems: issue #18968)), plugin, and -linkshared force
index 07be998035015f3a48b627d3011253eb245fe4dc..c433a872bed27d23f80fd22109c655f6f366f997 100644 (file)
@@ -118,7 +118,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
 
 func InternalLinkPIESupported(goos, goarch string) bool {
        switch goos + "/" + goarch {
-       case "darwin/amd64",
+       case "darwin/amd64", "darwin/arm64",
                "linux/amd64", "linux/arm64",
                "android/arm64",
                "windows-amd64", "windows-386", "windows-arm":
index 1d2aa591d70b379e54cae8379f1b669411a6a598..7bf41c93a6d874d8140ad531b63b5e81dfb32cde 100644 (file)
@@ -219,6 +219,15 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
                        // External linker will do this relocation.
                        return true
                }
+               if target.IsDarwin() { // XXX why we don't need this for ELF?
+                       // Internal linking.
+                       // Build a PLT entry and change the relocation target to that entry.
+                       addpltsym(target, ldr, syms, targ)
+                       su := ldr.MakeSymbolUpdater(s)
+                       su.SetRelocSym(rIdx, syms.PLT)
+                       su.SetRelocAdd(rIdx, int64(ldr.SymPlt(targ)))
+                       return true
+               }
 
        case objabi.R_ADDR:
                if ldr.SymType(s) == sym.STEXT && target.IsElf() {
@@ -313,6 +322,18 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
                        // (e.g. go version).
                        return true
                }
+
+               if target.IsDarwin() {
+                       // Mach-O relocations are a royal pain to lay out.
+                       // They use a compact stateful bytecode representation.
+                       // Here we record what are needed and encode them later.
+                       ld.MachoAddRebase(s, int64(r.Off()))
+                       // Not mark r done here. So we still apply it statically,
+                       // so in the file content we'll also have the right offset
+                       // to the relocation target. So it can be examined statically
+                       // (e.g. go version).
+                       return true
+               }
        }
        return false
 }
@@ -812,6 +833,34 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
                rela.AddUint64(target.Arch, 0)
 
                ldr.SetPlt(s, int32(plt.Size()-16))
+       } else if target.IsDarwin() {
+               ld.AddGotSym(target, ldr, syms, s, 0)
+
+               sDynid := ldr.SymDynid(s)
+               lep := ldr.MakeSymbolUpdater(syms.LinkEditPLT)
+               lep.AddUint32(target.Arch, uint32(sDynid))
+
+               plt := ldr.MakeSymbolUpdater(syms.PLT)
+               ldr.SetPlt(s, int32(plt.Size()))
+
+               // adrp x16, GOT
+               plt.AddUint32(target.Arch, 0x90000010)
+               r, _ := plt.AddRel(objabi.R_ARM64_GOT)
+               r.SetOff(int32(plt.Size() - 4))
+               r.SetSiz(4)
+               r.SetSym(syms.GOT)
+               r.SetAdd(int64(ldr.SymGot(s)))
+
+               // ldr x17, [x16, <offset>]
+               plt.AddUint32(target.Arch, 0xf9400211)
+               r, _ = plt.AddRel(objabi.R_ARM64_GOT)
+               r.SetOff(int32(plt.Size() - 4))
+               r.SetSiz(4)
+               r.SetSym(syms.GOT)
+               r.SetAdd(int64(ldr.SymGot(s)))
+
+               // br x17
+               plt.AddUint32(target.Arch, 0xd61f0220)
        } else {
                ldr.Errorf(s, "addpltsym: unsupported binary format")
        }
index a980cfee52b56c72d2176953bc44a8d59e2a7701..ab3dfd99f73211cf5b39da8a1c7e3fdfe5b66f5a 100644 (file)
@@ -102,7 +102,7 @@ func archinit(ctxt *ld.Link) {
        case objabi.Hdarwin: /* apple MACH */
                ld.HEADR = ld.INITIAL_MACHO_HEADR
                if *ld.FlagTextAddr == -1 {
-                       *ld.FlagTextAddr = 4096 + int64(ld.HEADR)
+                       *ld.FlagTextAddr = 1<<32 + int64(ld.HEADR)
                }
                if *ld.FlagRound == -1 {
                        *ld.FlagRound = 16384 // 16K page alignment
index c680d11c1d6f82ee4a9e158e0c9b30b8d557b061..f55e4fc027d39a38a6a2684140705e04fd19c162 100644 (file)
@@ -35,6 +35,10 @@ func (mode *BuildMode) Set(s string) error {
        default:
                return fmt.Errorf("invalid buildmode: %q", s)
        case "exe":
+               if objabi.GOOS == "darwin" && objabi.GOARCH == "arm64" {
+                       *mode = BuildModePIE // On darwin/arm64 everything is PIE.
+                       break
+               }
                *mode = BuildModeExe
        case "pie":
                switch objabi.GOOS {
@@ -187,7 +191,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
                }()
        }
 
-       if sys.MustLinkExternal(objabi.GOOS, objabi.GOARCH) {
+       if sys.MustLinkExternal(objabi.GOOS, objabi.GOARCH) && !(objabi.GOOS == "darwin" && objabi.GOARCH == "arm64") { // XXX allow internal linking for darwin/arm64 but not change the default
                return true, fmt.Sprintf("%s/%s requires external linking", objabi.GOOS, objabi.GOARCH)
        }
 
@@ -204,6 +208,9 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
        if iscgo && objabi.GOOS == "android" {
                return true, objabi.GOOS + " does not support internal cgo"
        }
+       if iscgo && objabi.GOOS == "darwin" && objabi.GOARCH == "arm64" {
+               return true, objabi.GOOS + "/" + objabi.GOARCH + " does not support internal cgo"
+       }
 
        // When the race flag is set, the LLVM tsan relocatable file is linked
        // into the final binary, which means external linking is required because
@@ -222,7 +229,7 @@ func mustLinkExternal(ctxt *Link) (res bool, reason string) {
                switch objabi.GOOS + "/" + objabi.GOARCH {
                case "linux/amd64", "linux/arm64", "android/arm64":
                case "windows/386", "windows/amd64", "windows/arm":
-               case "darwin/amd64":
+               case "darwin/amd64", "darwin/arm64":
                default:
                        // Internal linking does not support TLS_IE.
                        return true, "buildmode=pie"
@@ -263,6 +270,8 @@ func determineLinkMode(ctxt *Link) {
                default:
                        if extNeeded || (iscgo && externalobj) {
                                ctxt.LinkMode = LinkExternal
+                       } else if ctxt.IsDarwin() && ctxt.IsARM64() {
+                               ctxt.LinkMode = LinkExternal // default to external linking for now
                        } else {
                                ctxt.LinkMode = LinkInternal
                        }
index a19a4afd9a62ec3af45215757fdae85c0db62088..155769c48ffb508b35b6f230c388e891b415b147 100644 (file)
@@ -751,11 +751,9 @@ func asmbMacho(ctxt *Link) {
                        ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32)
 
                case sys.ARM64:
-                       ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 68+2)
-                       ml.data[0] = 6                           /* thread type */
-                       ml.data[1] = 68                          /* word count */
-                       ml.data[2+64] = uint32(Entryvalue(ctxt)) /* start pc */
-                       ml.data[2+64+1] = uint32(Entryvalue(ctxt) >> 32)
+                       ml := newMachoLoad(ctxt.Arch, LC_MAIN, 4)
+                       ml.data[0] = uint32(uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR)))
+                       ml.data[1] = uint32((uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR))) >> 32)
                }
        }
 
index 50c643748c73a75bee9295cf905573f76f68d0ac..3f7370b636e33004db3e7ea28222f55fb0057f56 100644 (file)
@@ -169,7 +169,7 @@ func Main(arch *sys.Arch, theArch Arch) {
 
        startProfile()
        if ctxt.BuildMode == BuildModeUnset {
-               ctxt.BuildMode = BuildModeExe
+               ctxt.BuildMode.Set("exe")
        }
 
        if ctxt.BuildMode != BuildModeShared && flag.NArg() != 1 {
index e3972f492429af201dd1af77b04a6b2c97ddef07..0040361215492451a984e1afa63b5acad5788ae6 100644 (file)
@@ -4,11 +4,14 @@
 
 #include "textflag.h"
 
-// No need for _rt0_arm64_darwin as darwin/arm64 only
-// supports external linking.
 TEXT _rt0_arm64_darwin(SB),NOSPLIT|NOFRAME,$0
-       MOVD    $42, R0
-       BL  libc_exit(SB)
+       MOVD    $runtime·rt0_go(SB), R2
+       BL      (R2)
+exit:
+       MOVD    $0, R0
+       MOVD    $1, R16 // sys_exit
+       SVC     $0x80
+       B       exit
 
 // When linking with -buildmode=c-archive or -buildmode=c-shared,
 // this symbol is called from a global initialization function.
@@ -86,11 +89,6 @@ GLOBL _rt0_arm64_darwin_lib_argc<>(SB),NOPTR, $8
 DATA  _rt0_arm64_darwin_lib_argv<>(SB)/8, $0
 GLOBL _rt0_arm64_darwin_lib_argv<>(SB),NOPTR, $8
 
+// external linking entry point.
 TEXT main(SB),NOSPLIT|NOFRAME,$0
-       MOVD    $runtime·rt0_go(SB), R2
-       BL      (R2)
-exit:
-       MOVD    $0, R0
-       MOVD    $1, R16 // sys_exit
-       SVC     $0x80
-       B       exit
+       JMP     _rt0_arm64_darwin(SB)