Skip to content

Commit 73268be

Browse files
cherrymuidmitshur
authored andcommitted
[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 <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Than McIntosh <[email protected]> (cherry picked from commit 7799756) Reviewed-on: https://go-review.googlesource.com/c/go/+/240511 Reviewed-by: Austin Clements <[email protected]>
1 parent db4890e commit 73268be

File tree

5 files changed

+82
-14
lines changed

5 files changed

+82
-14
lines changed

misc/cgo/testshared/shared_test.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,15 @@ var testWork = flag.Bool("testwork", false, "if true, log and do not delete the
3838

3939
// run runs a command and calls t.Errorf if it fails.
4040
func run(t *testing.T, msg string, args ...string) {
41+
runWithEnv(t, msg, nil, args...)
42+
}
43+
44+
// runWithEnv runs a command under the given environment and calls t.Errorf if it fails.
45+
func runWithEnv(t *testing.T, msg string, env []string, args ...string) {
4146
c := exec.Command(args[0], args[1:]...)
47+
if len(env) != 0 {
48+
c.Env = append(os.Environ(), env...)
49+
}
4250
if output, err := c.CombinedOutput(); err != nil {
4351
t.Errorf("executing %s (%s) failed %s:\n%s", strings.Join(args, " "), msg, err, output)
4452
}
@@ -1030,3 +1038,11 @@ func TestGeneratedHash(t *testing.T) {
10301038
goCmd(nil, "install", "-buildmode=shared", "-linkshared", "./issue30768/issue30768lib")
10311039
goCmd(nil, "test", "-linkshared", "./issue30768")
10321040
}
1041+
1042+
// Test that GC data are generated correctly by the linker when it needs a type defined in
1043+
// a shared library. See issue 39927.
1044+
func TestGCData(t *testing.T) {
1045+
goCmd(t, "install", "-buildmode=shared", "-linkshared", "./gcdata/p")
1046+
goCmd(t, "build", "-linkshared", "./gcdata/main")
1047+
runWithEnv(t, "running gcdata/main", []string{"GODEBUG=clobberfree=1"}, "./main")
1048+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2020 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+
// Test that GC data is generated correctly for global
6+
// variables with types defined in a shared library.
7+
// See issue 39927.
8+
9+
// This test run under GODEBUG=clobberfree=1. The check
10+
// *x[i] == 12345 depends on this debug mode to clobber
11+
// the value if the object is freed prematurely.
12+
13+
package main
14+
15+
import (
16+
"fmt"
17+
"runtime"
18+
"testshared/gcdata/p"
19+
)
20+
21+
var x p.T
22+
23+
func main() {
24+
for i := range x {
25+
x[i] = new(int)
26+
*x[i] = 12345
27+
}
28+
runtime.GC()
29+
runtime.GC()
30+
runtime.GC()
31+
for i := range x {
32+
if *x[i] != 12345 {
33+
fmt.Printf("x[%d] == %d, want 12345\n", i, *x[i])
34+
panic("FAIL")
35+
}
36+
}
37+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Copyright 2020 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 p
6+
7+
type T [10]*int

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

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"cmd/link/internal/sym"
1212
"debug/elf"
1313
"fmt"
14+
"log"
1415
)
1516

1617
// Decoding the type.* symbols. This has to be in sync with
@@ -93,7 +94,7 @@ func decodetypeHasUncommon(arch *sys.Arch, p []byte) bool {
9394
func findShlibSection(ctxt *Link, path string, addr uint64) *elf.Section {
9495
for _, shlib := range ctxt.Shlibs {
9596
if shlib.Path == path {
96-
for _, sect := range shlib.File.Sections {
97+
for _, sect := range shlib.File.Sections[1:] { // skip the NULL section
9798
if sect.Addr <= addr && addr <= sect.Addr+sect.Size {
9899
return sect
99100
}
@@ -112,9 +113,15 @@ func decodetypeGcprog(ctxt *Link, s *sym.Symbol) []byte {
112113
// A gcprog is a 4-byte uint32 indicating length, followed by
113114
// the actual program.
114115
progsize := make([]byte, 4)
115-
sect.ReadAt(progsize, int64(addr-sect.Addr))
116+
_, err := sect.ReadAt(progsize, int64(addr-sect.Addr))
117+
if err != nil {
118+
log.Fatal(err)
119+
}
116120
progbytes := make([]byte, ctxt.Arch.ByteOrder.Uint32(progsize))
117-
sect.ReadAt(progbytes, int64(addr-sect.Addr+4))
121+
_, err = sect.ReadAt(progbytes, int64(addr-sect.Addr+4))
122+
if err != nil {
123+
log.Fatal(err)
124+
}
118125
return append(progsize, progbytes...)
119126
}
120127
Exitf("cannot find gcprog for %s", s.Name)
@@ -124,14 +131,6 @@ func decodetypeGcprog(ctxt *Link, s *sym.Symbol) []byte {
124131
}
125132

126133
func decodetypeGcprogShlib(ctxt *Link, s *sym.Symbol) uint64 {
127-
if ctxt.Arch.Family == sys.ARM64 {
128-
for _, shlib := range ctxt.Shlibs {
129-
if shlib.Path == s.File {
130-
return shlib.gcdataAddresses[s]
131-
}
132-
}
133-
return 0
134-
}
135134
return decodeInuxi(ctxt.Arch, s.P[2*int32(ctxt.Arch.PtrSize)+8+1*int32(ctxt.Arch.PtrSize):], ctxt.Arch.PtrSize)
136135
}
137136

@@ -141,8 +140,15 @@ func decodetypeGcmask(ctxt *Link, s *sym.Symbol) []byte {
141140
ptrdata := decodetypePtrdata(ctxt.Arch, s.P)
142141
sect := findShlibSection(ctxt, s.File, addr)
143142
if sect != nil {
144-
r := make([]byte, ptrdata/int64(ctxt.Arch.PtrSize))
145-
sect.ReadAt(r, int64(addr-sect.Addr))
143+
bits := ptrdata / int64(ctxt.Arch.PtrSize)
144+
r := make([]byte, (bits+7)/8)
145+
// ldshlibsyms avoids closing the ELF file so sect.ReadAt works.
146+
// If we remove this read (and the ones in decodetypeGcprog), we
147+
// can close the file.
148+
_, err := sect.ReadAt(r, int64(addr-sect.Addr))
149+
if err != nil {
150+
log.Fatal(err)
151+
}
146152
return r
147153
}
148154
Exitf("cannot find gcmask for %s", s.Name)

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2000,7 +2000,9 @@ func ldshlibsyms(ctxt *Link, shlib string) {
20002000
Errorf(nil, "cannot open shared library: %s", libpath)
20012001
return
20022002
}
2003-
defer f.Close()
2003+
// Keep the file open as decodetypeGcprog needs to read from it.
2004+
// TODO: fix. Maybe mmap the file.
2005+
//defer f.Close()
20042006

20052007
hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG)
20062008
if err != nil {

0 commit comments

Comments
 (0)