Skip to content

Commit

Permalink
cmd/link: force eager binding when using plugins on darwin
Browse files Browse the repository at this point in the history
When building/using plugins on darwin, we need to use flat
namespace so the same symbol from the main executable and the
plugin can be resolved to the same address. Apparently, when using
flat namespace the dynamic linker can hang at forkExec when
resolving a lazy binding. Work around it by forcing early bindings.

Fixes #38824.

Change-Id: I983aa0a0960b15bf3f7871382e8231ee244655f4
Reviewed-on: https://go-review.googlesource.com/c/go/+/372798
Trust: Cherry Mui <cherryyz@google.com>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
  • Loading branch information
cherrymui committed Dec 17, 2021
1 parent 5299390 commit c5fee93
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 1 deletion.
28 changes: 28 additions & 0 deletions misc/cgo/testplugin/plugin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,31 @@ func TestIssue44956(t *testing.T) {
goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go")
run(t, "./issue44956.exe")
}

func TestForkExec(t *testing.T) {
// Issue 38824: importing the plugin package causes it hang in forkExec on darwin.

t.Parallel()
goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go")

var cmd *exec.Cmd
done := make(chan int, 1)

go func() {
for i := 0; i < 100; i++ {
cmd = exec.Command("./forkexec.exe", "1")
err := cmd.Run()
if err != nil {
t.Errorf("running command failed: %v", err)
break
}
}
done <- 1
}()
select {
case <-done:
case <-time.After(5 * time.Minute):
cmd.Process.Kill()
t.Fatalf("subprocess hang")
}
}
30 changes: 30 additions & 0 deletions misc/cgo/testplugin/testdata/forkexec/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
"os"
"os/exec"
_ "plugin"
"sync"
)

func main() {
if os.Args[1] != "1" {
return
}

var wg sync.WaitGroup
for i := 0; i < 8; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// does not matter what we exec, just exec itself
cmd := exec.Command("./forkexec.exe", "0")
cmd.Run()
}()
}
wg.Wait()
}
5 changes: 4 additions & 1 deletion src/cmd/link/internal/ld/lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -1269,7 +1269,10 @@ func (ctxt *Link) hostlink() {
if ctxt.DynlinkingGo() && buildcfg.GOOS != "ios" {
// -flat_namespace is deprecated on iOS.
// It is useful for supporting plugins. We don't support plugins on iOS.
argv = append(argv, "-Wl,-flat_namespace")
// -flat_namespace may cause the dynamic linker to hang at forkExec when
// resolving a lazy binding. See issue 38824.
// Force eager resolution to work around.
argv = append(argv, "-Wl,-flat_namespace", "-Wl,-bind_at_load")
}
if !combineDwarf {
argv = append(argv, "-Wl,-S") // suppress STAB (symbolic debugging) symbols
Expand Down

0 comments on commit c5fee93

Please sign in to comment.