1
0
mirror of https://github.com/golang/go synced 2024-11-07 19:56:10 -07:00

cmd/link: support internal linking on darwin/arm64

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>
This commit is contained in:
Cherry Zhang 2020-10-12 13:44:21 -04:00
parent 2ec71e5732
commit e4ec30965b
8 changed files with 79 additions and 24 deletions

View File

@ -1948,6 +1948,10 @@ func LinkerDeps(p *Package) []string {
// externalLinkingForced reports whether external linking is being // externalLinkingForced reports whether external linking is being
// forced even for programs that do not use cgo. // forced even for programs that do not use cgo.
func externalLinkingForced(p *Package) bool { func externalLinkingForced(p *Package) bool {
if !cfg.BuildContext.CgoEnabled {
return false
}
// Some targets must use external linking even inside GOROOT. // Some targets must use external linking even inside GOROOT.
switch cfg.BuildContext.GOOS { switch cfg.BuildContext.GOOS {
case "android": 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 // Currently build modes c-shared, pie (on systems that do not
// support PIE with internal linking mode (currently all // support PIE with internal linking mode (currently all
// systems: issue #18968)), plugin, and -linkshared force // systems: issue #18968)), plugin, and -linkshared force

View File

@ -118,7 +118,7 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
func InternalLinkPIESupported(goos, goarch string) bool { func InternalLinkPIESupported(goos, goarch string) bool {
switch goos + "/" + goarch { switch goos + "/" + goarch {
case "darwin/amd64", case "darwin/amd64", "darwin/arm64",
"linux/amd64", "linux/arm64", "linux/amd64", "linux/arm64",
"android/arm64", "android/arm64",
"windows-amd64", "windows-386", "windows-arm": "windows-amd64", "windows-386", "windows-arm":

View File

@ -219,6 +219,15 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
// External linker will do this relocation. // External linker will do this relocation.
return true 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: case objabi.R_ADDR:
if ldr.SymType(s) == sym.STEXT && target.IsElf() { 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). // (e.g. go version).
return true 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 return false
} }
@ -812,6 +833,34 @@ func addpltsym(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
rela.AddUint64(target.Arch, 0) rela.AddUint64(target.Arch, 0)
ldr.SetPlt(s, int32(plt.Size()-16)) 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 { } else {
ldr.Errorf(s, "addpltsym: unsupported binary format") ldr.Errorf(s, "addpltsym: unsupported binary format")
} }

View File

@ -102,7 +102,7 @@ func archinit(ctxt *ld.Link) {
case objabi.Hdarwin: /* apple MACH */ case objabi.Hdarwin: /* apple MACH */
ld.HEADR = ld.INITIAL_MACHO_HEADR ld.HEADR = ld.INITIAL_MACHO_HEADR
if *ld.FlagTextAddr == -1 { if *ld.FlagTextAddr == -1 {
*ld.FlagTextAddr = 4096 + int64(ld.HEADR) *ld.FlagTextAddr = 1<<32 + int64(ld.HEADR)
} }
if *ld.FlagRound == -1 { if *ld.FlagRound == -1 {
*ld.FlagRound = 16384 // 16K page alignment *ld.FlagRound = 16384 // 16K page alignment

View File

@ -35,6 +35,10 @@ func (mode *BuildMode) Set(s string) error {
default: default:
return fmt.Errorf("invalid buildmode: %q", s) return fmt.Errorf("invalid buildmode: %q", s)
case "exe": case "exe":
if objabi.GOOS == "darwin" && objabi.GOARCH == "arm64" {
*mode = BuildModePIE // On darwin/arm64 everything is PIE.
break
}
*mode = BuildModeExe *mode = BuildModeExe
case "pie": case "pie":
switch objabi.GOOS { 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) 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" { if iscgo && objabi.GOOS == "android" {
return true, objabi.GOOS + " does not support internal cgo" 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 // When the race flag is set, the LLVM tsan relocatable file is linked
// into the final binary, which means external linking is required because // 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 { switch objabi.GOOS + "/" + objabi.GOARCH {
case "linux/amd64", "linux/arm64", "android/arm64": case "linux/amd64", "linux/arm64", "android/arm64":
case "windows/386", "windows/amd64", "windows/arm": case "windows/386", "windows/amd64", "windows/arm":
case "darwin/amd64": case "darwin/amd64", "darwin/arm64":
default: default:
// Internal linking does not support TLS_IE. // Internal linking does not support TLS_IE.
return true, "buildmode=pie" return true, "buildmode=pie"
@ -263,6 +270,8 @@ func determineLinkMode(ctxt *Link) {
default: default:
if extNeeded || (iscgo && externalobj) { if extNeeded || (iscgo && externalobj) {
ctxt.LinkMode = LinkExternal ctxt.LinkMode = LinkExternal
} else if ctxt.IsDarwin() && ctxt.IsARM64() {
ctxt.LinkMode = LinkExternal // default to external linking for now
} else { } else {
ctxt.LinkMode = LinkInternal ctxt.LinkMode = LinkInternal
} }

View File

@ -751,11 +751,9 @@ func asmbMacho(ctxt *Link) {
ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32) ml.data[2+32+1] = uint32(Entryvalue(ctxt) >> 32)
case sys.ARM64: case sys.ARM64:
ml := newMachoLoad(ctxt.Arch, LC_UNIXTHREAD, 68+2) ml := newMachoLoad(ctxt.Arch, LC_MAIN, 4)
ml.data[0] = 6 /* thread type */ ml.data[0] = uint32(uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR)))
ml.data[1] = 68 /* word count */ ml.data[1] = uint32((uint64(Entryvalue(ctxt)) - (Segtext.Vaddr - uint64(HEADR))) >> 32)
ml.data[2+64] = uint32(Entryvalue(ctxt)) /* start pc */
ml.data[2+64+1] = uint32(Entryvalue(ctxt) >> 32)
} }
} }

View File

@ -169,7 +169,7 @@ func Main(arch *sys.Arch, theArch Arch) {
startProfile() startProfile()
if ctxt.BuildMode == BuildModeUnset { if ctxt.BuildMode == BuildModeUnset {
ctxt.BuildMode = BuildModeExe ctxt.BuildMode.Set("exe")
} }
if ctxt.BuildMode != BuildModeShared && flag.NArg() != 1 { if ctxt.BuildMode != BuildModeShared && flag.NArg() != 1 {

View File

@ -4,11 +4,14 @@
#include "textflag.h" #include "textflag.h"
// No need for _rt0_arm64_darwin as darwin/arm64 only
// supports external linking.
TEXT _rt0_arm64_darwin(SB),NOSPLIT|NOFRAME,$0 TEXT _rt0_arm64_darwin(SB),NOSPLIT|NOFRAME,$0
MOVD $42, R0 MOVD $runtime·rt0_go(SB), R2
BL libc_exit(SB) 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, // When linking with -buildmode=c-archive or -buildmode=c-shared,
// this symbol is called from a global initialization function. // 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 DATA _rt0_arm64_darwin_lib_argv<>(SB)/8, $0
GLOBL _rt0_arm64_darwin_lib_argv<>(SB),NOPTR, $8 GLOBL _rt0_arm64_darwin_lib_argv<>(SB),NOPTR, $8
// external linking entry point.
TEXT main(SB),NOSPLIT|NOFRAME,$0 TEXT main(SB),NOSPLIT|NOFRAME,$0
MOVD $runtime·rt0_go(SB), R2 JMP _rt0_arm64_darwin(SB)
BL (R2)
exit:
MOVD $0, R0
MOVD $1, R16 // sys_exit
SVC $0x80
B exit