aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCherry Zhang <cherryyz@google.com>2020-06-29 17:07:17 -0400
committerDmitri Shuralyov <dmitshur@golang.org>2020-08-21 23:57:04 +0000
commit73268bec86787cb8061baf9ab6f0fee3da1be7a4 (patch)
tree9b83dd4a5cba9a7a840884eac26063d7447aa50a
parentdb4890e5044d65d58c9833655507688ecb003f22 (diff)
downloadgo-73268bec86787cb8061baf9ab6f0fee3da1be7a4.tar.gz
go-73268bec86787cb8061baf9ab6f0fee3da1be7a4.zip
[release-branch.go1.14] cmd/link: fix GC data reading from shared library (attempt 2)
This is a backport of CL 240621. This is not a clean cherry-pick, as Go 1.15 switches to the new linker while it is still the old linker here. Backporting is straightforward, though. When linking against a Go shared library, when a global variable in the main module has a type defined in the shared library, the linker needs to pull the GC data from the shared library to build the GC program for the global variable. Currently, this fails silently, as the shared library file is closed too early and the read failed (with no error check), causing a zero GC map emitted for the variable, which in turn causes the runtime to treat the variable as pointerless. For now, fix this by keeping the file open. In the future we may want to use mmap to read from the shared library instead. Also add error checking. And fix a (mostly harmless) mistake in size caluculation. Also remove an erroneous condition for ARM64. ARM64 has a special case to get the addend from the relocation on the gcdata field. But that doesn't actually work. And it's no longer necessary to have any special case, since the addend is now applied directly to the gcdata field on ARM64, like on all the other platforms. Fixes #39955. Updates #39927. Change-Id: I01c82422b9f67e872d833336885935bc509bc91b Reviewed-on: https://go-review.googlesource.com/c/go/+/240621 Run-TryBot: Cherry Zhang <cherryyz@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Than McIntosh <thanm@google.com> (cherry picked from commit 7799756a50f0a4070d66c67e9615375f852f2c04) Reviewed-on: https://go-review.googlesource.com/c/go/+/240511 Reviewed-by: Austin Clements <austin@google.com>
-rw-r--r--misc/cgo/testshared/shared_test.go16
-rw-r--r--misc/cgo/testshared/testdata/gcdata/main/main.go37
-rw-r--r--misc/cgo/testshared/testdata/gcdata/p/p.go7
-rw-r--r--src/cmd/link/internal/ld/decodesym.go32
-rw-r--r--src/cmd/link/internal/ld/lib.go4
5 files changed, 82 insertions, 14 deletions
diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go
index b9ef6dad8e..f6cefa3516 100644
--- a/misc/cgo/testshared/shared_test.go
+++ b/misc/cgo/testshared/shared_test.go
@@ -38,7 +38,15 @@ var testWork = flag.Bool("testwork", false, "if true, log and do not delete the
// run runs a command and calls t.Errorf if it fails.
func run(t *testing.T, msg string, args ...string) {
+ runWithEnv(t, msg, nil, args...)
+}
+
+// runWithEnv runs a command under the given environment and calls t.Errorf if it fails.
+func runWithEnv(t *testing.T, msg string, env []string, args ...string) {
c := exec.Command(args[0], args[1:]...)
+ if len(env) != 0 {
+ c.Env = append(os.Environ(), env...)
+ }
if output, err := c.CombinedOutput(); err != nil {
t.Errorf("executing %s (%s) failed %s:\n%s", strings.Join(args, " "), msg, err, output)
}
@@ -1030,3 +1038,11 @@ func TestGeneratedHash(t *testing.T) {
goCmd(nil, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib")
goCmd(nil, "test", "-linkshared", "./issue30768")
}
+
+// Test that GC data are generated correctly by the linker when it needs a type defined in
+// a shared library. See issue 39927.
+func TestGCData(t *testing.T) {
+ goCmd(t, "install", "-buildmode=shared", "-linkshared", "./gcdata/p")
+ goCmd(t, "build", "-linkshared", "./gcdata/main")
+ runWithEnv(t, "running gcdata/main", []string{"GODEBUG=clobberfree=1"}, "./main")
+}
diff --git a/misc/cgo/testshared/testdata/gcdata/main/main.go b/misc/cgo/testshared/testdata/gcdata/main/main.go
new file mode 100644
index 0000000000..394862fd94
--- /dev/null
+++ b/misc/cgo/testshared/testdata/gcdata/main/main.go
@@ -0,0 +1,37 @@
+// Copyright 2020 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.
+
+// Test that GC data is generated correctly for global
+// variables with types defined in a shared library.
+// See issue 39927.
+
+// This test run under GODEBUG=clobberfree=1. The check
+// *x[i] == 12345 depends on this debug mode to clobber
+// the value if the object is freed prematurely.
+
+package main
+
+import (
+ "fmt"
+ "runtime"
+ "testshared/gcdata/p"
+)
+
+var x p.T
+
+func main() {
+ for i := range x {
+ x[i] = new(int)
+ *x[i] = 12345
+ }
+ runtime.GC()
+ runtime.GC()
+ runtime.GC()
+ for i := range x {
+ if *x[i] != 12345 {
+ fmt.Printf("x[%d] == %d, want 12345\n", i, *x[i])
+ panic("FAIL")
+ }
+ }
+}
diff --git a/misc/cgo/testshared/testdata/gcdata/p/p.go b/misc/cgo/testshared/testdata/gcdata/p/p.go
new file mode 100644
index 0000000000..1fee75429e
--- /dev/null
+++ b/misc/cgo/testshared/testdata/gcdata/p/p.go
@@ -0,0 +1,7 @@
+// Copyright 2020 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 p
+
+type T [10]*int
diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
index 3271c85157..88176a3643 100644
--- a/src/cmd/link/internal/ld/decodesym.go
+++ b/src/cmd/link/internal/ld/decodesym.go
@@ -11,6 +11,7 @@ import (
"cmd/link/internal/sym"
"debug/elf"
"fmt"
+ "log"
)
// Decoding the type.* symbols. This has to be in sync with
@@ -93,7 +94,7 @@ func decodetypeHasUncommon(arch *sys.Arch, p []byte) bool {
func findShlibSection(ctxt *Link, path string, addr uint64) *elf.Section {
for _, shlib := range ctxt.Shlibs {
if shlib.Path == path {
- for _, sect := range shlib.File.Sections {
+ for _, sect := range shlib.File.Sections[1:] { // skip the NULL section
if sect.Addr <= addr && addr <= sect.Addr+sect.Size {
return sect
}
@@ -112,9 +113,15 @@ func decodetypeGcprog(ctxt *Link, s *sym.Symbol) []byte {
// A gcprog is a 4-byte uint32 indicating length, followed by
// the actual program.
progsize := make([]byte, 4)
- sect.ReadAt(progsize, int64(addr-sect.Addr))
+ _, err := sect.ReadAt(progsize, int64(addr-sect.Addr))
+ if err != nil {
+ log.Fatal(err)
+ }
progbytes := make([]byte, ctxt.Arch.ByteOrder.Uint32(progsize))
- sect.ReadAt(progbytes, int64(addr-sect.Addr+4))
+ _, err = sect.ReadAt(progbytes, int64(addr-sect.Addr+4))
+ if err != nil {
+ log.Fatal(err)
+ }
return append(progsize, progbytes...)
}
Exitf("cannot find gcprog for %s", s.Name)
@@ -124,14 +131,6 @@ func decodetypeGcprog(ctxt *Link, s *sym.Symbol) []byte {
}
func decodetypeGcprogShlib(ctxt *Link, s *sym.Symbol) uint64 {
- if ctxt.Arch.Family == sys.ARM64 {
- for _, shlib := range ctxt.Shlibs {
- if shlib.Path == s.File {
- return shlib.gcdataAddresses[s]
- }
- }
- return 0
- }
return decodeInuxi(ctxt.Arch, s.P[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize)
}
@@ -141,8 +140,15 @@ func decodetypeGcmask(ctxt *Link, s *sym.Symbol) []byte {
ptrdata := decodetypePtrdata(ctxt.Arch, s.P)
sect := findShlibSection(ctxt, s.File, addr)
if sect != nil {
- r := make([]byte, ptrdata/int64(ctxt.Arch.PtrSize))
- sect.ReadAt(r, int64(addr-sect.Addr))
+ bits := ptrdata / int64(ctxt.Arch.PtrSize)
+ r := make([]byte, (bits+7)/8)
+ // ldshlibsyms avoids closing the ELF file so sect.ReadAt works.
+ // If we remove this read (and the ones in decodetypeGcprog), we
+ // can close the file.
+ _, err := sect.ReadAt(r, int64(addr-sect.Addr))
+ if err != nil {
+ log.Fatal(err)
+ }
return r
}
Exitf("cannot find gcmask for %s", s.Name)
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 45cc87287c..cd63963d7f 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -2000,7 +2000,9 @@ func ldshlibsyms(ctxt *Link, shlib string) {
Errorf(nil, "cannot open shared library: %s", libpath)
return
}
- defer f.Close()
+ // Keep the file open as decodetypeGcprog needs to read from it.
+ // TODO: fix. Maybe mmap the file.
+ //defer f.Close()
hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG)
if err != nil {