Skip to content

Commit c5fee93

Browse files
committed
cmd/link: force eager binding when using plugins on darwin
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>
1 parent 5299390 commit c5fee93

File tree

3 files changed

+62
-1
lines changed

3 files changed

+62
-1
lines changed

misc/cgo/testplugin/plugin_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,31 @@ func TestIssue44956(t *testing.T) {
289289
goCmd(t, "build", "-o", "issue44956.exe", "./issue44956/main.go")
290290
run(t, "./issue44956.exe")
291291
}
292+
293+
func TestForkExec(t *testing.T) {
294+
// Issue 38824: importing the plugin package causes it hang in forkExec on darwin.
295+
296+
t.Parallel()
297+
goCmd(t, "build", "-o", "forkexec.exe", "./forkexec/main.go")
298+
299+
var cmd *exec.Cmd
300+
done := make(chan int, 1)
301+
302+
go func() {
303+
for i := 0; i < 100; i++ {
304+
cmd = exec.Command("./forkexec.exe", "1")
305+
err := cmd.Run()
306+
if err != nil {
307+
t.Errorf("running command failed: %v", err)
308+
break
309+
}
310+
}
311+
done <- 1
312+
}()
313+
select {
314+
case <-done:
315+
case <-time.After(5 * time.Minute):
316+
cmd.Process.Kill()
317+
t.Fatalf("subprocess hang")
318+
}
319+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2021 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"os"
9+
"os/exec"
10+
_ "plugin"
11+
"sync"
12+
)
13+
14+
func main() {
15+
if os.Args[1] != "1" {
16+
return
17+
}
18+
19+
var wg sync.WaitGroup
20+
for i := 0; i < 8; i++ {
21+
wg.Add(1)
22+
go func() {
23+
defer wg.Done()
24+
// does not matter what we exec, just exec itself
25+
cmd := exec.Command("./forkexec.exe", "0")
26+
cmd.Run()
27+
}()
28+
}
29+
wg.Wait()
30+
}

src/cmd/link/internal/ld/lib.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1269,7 +1269,10 @@ func (ctxt *Link) hostlink() {
12691269
if ctxt.DynlinkingGo() && buildcfg.GOOS != "ios" {
12701270
// -flat_namespace is deprecated on iOS.
12711271
// It is useful for supporting plugins. We don't support plugins on iOS.
1272-
argv = append(argv, "-Wl,-flat_namespace")
1272+
// -flat_namespace may cause the dynamic linker to hang at forkExec when
1273+
// resolving a lazy binding. See issue 38824.
1274+
// Force eager resolution to work around.
1275+
argv = append(argv, "-Wl,-flat_namespace", "-Wl,-bind_at_load")
12731276
}
12741277
if !combineDwarf {
12751278
argv = append(argv, "-Wl,-S") // suppress STAB (symbolic debugging) symbols

0 commit comments

Comments
 (0)