Skip to content

Commit

Permalink
cmd/link/internal/ld: copy Mach-O platform version commands to go.o
Browse files Browse the repository at this point in the history
To build for watchOS and tvOS the Apple toolchain requires a Mach-O
load command that matches the platform for all object files in a build.
The go.o object file produced by the Go linker contains no such command.

The loader commands are mutually exclusive so we need to pick the
right one. Fortunately, cgo must be enabled for watchOS and tvOS to
be useful, so we can copy the first loader command we find in the object
files produced by the host compiler.

Add a test that builds a small program for tvOS to test both this CL
and the previous CL that added bitcode support.

Updates #22395

Change-Id: I7a47d19be9d80f0459dc358c600cddd9f236c444
Reviewed-on: https://go-review.googlesource.com/c/go/+/168321
TryBot-Result: Gobot Gobot <[email protected]>
Reviewed-by: Cherry Zhang <[email protected]>
  • Loading branch information
eliasnaur committed Mar 21, 2019
1 parent 0fe1986 commit 277609f
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 2 deletions.
51 changes: 51 additions & 0 deletions src/cmd/link/dwarf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,54 @@ func TestDWARFiOS(t *testing.T) {
testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm", "GOARM=7")
testDWARF(t, "c-archive", true, cc, "CGO_ENABLED=1", "GOOS=darwin", "GOARCH=arm64")
}

func TestBuildFortvOS(t *testing.T) {
testenv.MustHaveCGO(t)
testenv.MustHaveGoBuild(t)

// Only run this on darwin/amd64, where we can cross build for tvOS.
if runtime.GOARCH != "amd64" || runtime.GOOS != "darwin" {
t.Skip("skipping on non-darwin/amd64 platform")
}
if err := exec.Command("xcrun", "--help").Run(); err != nil {
t.Skipf("error running xcrun, required for iOS cross build: %v", err)
}

sdkPath, err := exec.Command("xcrun", "--sdk", "appletvos", "--show-sdk-path").Output()
if err != nil {
t.Fatalf("xcrun --sdk appletvos --show-sdk-path failed: %v", err)
}
CC := []string{
"clang",
"-arch",
"arm64",
"-isysroot", strings.TrimSpace(string(sdkPath)),
"-mtvos-version-min=12.0",
"-fembed-bitcode",
"-framework", "CoreFoundation",
}
lib := filepath.Join("testdata", "lib.go")
tmpDir, err := ioutil.TempDir("", "go-link-TestBuildFortvOS")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tmpDir)

ar := filepath.Join(tmpDir, "lib.a")
cmd := exec.Command(testenv.GoToolPath(t), "build", "-buildmode=c-archive", "-o", ar, lib)
cmd.Env = append(os.Environ(),
"CGO_ENABLED=1",
"GOOS=darwin",
"GOARCH=arm64",
"CC="+strings.Join(CC, " "),
)
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("%v: %v:\n%s", cmd.Args, err, out)
}

link := exec.Command(CC[0], CC[1:]...)
link.Args = append(link.Args, ar, filepath.Join("testdata", "main.m"))
if out, err := link.CombinedOutput(); err != nil {
t.Fatalf("%v: %v:\n%s", link.Args, err, out)
}
}
39 changes: 37 additions & 2 deletions src/cmd/link/internal/ld/macho.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import (
"cmd/link/internal/sym"
"debug/macho"
"encoding/binary"
"fmt"
"io"
"os"
"sort"
"strings"
)
Expand Down Expand Up @@ -691,8 +694,14 @@ func Asmbmacho(ctxt *Link) {
}
}
}

if ctxt.LinkMode == LinkInternal {
load, err := hostobjMachoPlatform(hostobj)
if err != nil {
Exitf("%v", err)
}
if load != nil {
ml := newMachoLoad(ctxt.Arch, load.cmd.type_, uint32(len(load.cmd.data)))
copy(ml.data, load.cmd.data)
} else if ctxt.LinkMode == LinkInternal {
// For lldb, must say LC_VERSION_MIN_MACOSX or else
// it won't know that this Mach-O binary is from OS X
// (could be iOS or WatchOS instead).
Expand Down Expand Up @@ -1017,6 +1026,32 @@ func Machoemitreloc(ctxt *Link) {
}
}

// hostobjMachoPlatform returns the first platform load command found
// in the host objects, if any.
func hostobjMachoPlatform(hostobj []Hostobj) (*MachoPlatformLoad, error) {
for _, h := range hostobj {
f, err := os.Open(h.file)
if err != nil {
return nil, fmt.Errorf("%s: failed to open host object: %v\n", h.file, err)
}
defer f.Close()
sr := io.NewSectionReader(f, h.off, h.length)
m, err := macho.NewFile(sr)
if err != nil {
// Not a valid Mach-O file.
return nil, nil
}
load, err := peekMachoPlatform(m)
if err != nil {
return nil, err
}
if load != nil {
return load, nil
}
}
return nil, nil
}

// peekMachoPlatform returns the first LC_VERSION_MIN_* or LC_BUILD_VERSION
// load command found in the Mach-O file, if any.
func peekMachoPlatform(m *macho.File) (*MachoPlatformLoad, error) {
Expand Down
8 changes: 8 additions & 0 deletions src/cmd/link/testdata/lib.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package main

import "C"

//export GoFunc
func GoFunc() {}

func main() {}
5 changes: 5 additions & 0 deletions src/cmd/link/testdata/main.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extern void GoFunc();

int main(int argc, char **argv) {
GoFunc();
}

0 comments on commit 277609f

Please sign in to comment.