1
0
mirror of https://github.com/golang/go synced 2024-11-12 01:10:21 -07:00

Revert "cmd/go: remove support for -buildmode=shared"

This reverts CL 359096.

Updates #47788.

Reason for revert: -buildmode=shared may have actually been working in a few very specific cases. We should not remove -buildmode=shared until we have implemented an alternative to support those few cases.

Change-Id: Ia962b06abacc11f6f29fc29d092773be175e32f1
Reviewed-on: https://go-review.googlesource.com/c/go/+/359575
Trust: Bryan C. Mills <bcmills@google.com>
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
Bryan C. Mills 2021-10-29 01:18:24 +00:00
parent 645d07819b
commit a88575d662
44 changed files with 1786 additions and 30 deletions

View File

@ -90,13 +90,6 @@ Do not send CLs removing the interior tags from such phrases.
package.
</p>
<p><!-- golang.org/issue/47788 -->
The <code>go</code> command no longer supports <code>-linkshared</code>
and <code>-buildmode=shared</code>.
(<code>shared<code> building and linking has never worked in module mode or
when <code>GOROOT</code> is not writable.)
<p>
<p>
TODO: complete this section, or delete if not needed
</p>

View File

@ -0,0 +1,78 @@
// Copyright 2019 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 shared_test
import (
"io"
"os"
"path/filepath"
"strings"
)
// overlayDir makes a minimal-overhead copy of srcRoot in which new files may be added.
//
// TODO: Once we no longer need to support the misc module in GOPATH mode,
// factor this function out into a package to reduce duplication.
func overlayDir(dstRoot, srcRoot string) error {
dstRoot = filepath.Clean(dstRoot)
if err := os.MkdirAll(dstRoot, 0777); err != nil {
return err
}
srcRoot, err := filepath.Abs(srcRoot)
if err != nil {
return err
}
return filepath.Walk(srcRoot, func(srcPath string, info os.FileInfo, err error) error {
if err != nil || srcPath == srcRoot {
return err
}
suffix := strings.TrimPrefix(srcPath, srcRoot)
for len(suffix) > 0 && suffix[0] == filepath.Separator {
suffix = suffix[1:]
}
dstPath := filepath.Join(dstRoot, suffix)
perm := info.Mode() & os.ModePerm
if info.Mode()&os.ModeSymlink != 0 {
info, err = os.Stat(srcPath)
if err != nil {
return err
}
perm = info.Mode() & os.ModePerm
}
// Always copy directories (don't symlink them).
// If we add a file in the overlay, we don't want to add it in the original.
if info.IsDir() {
return os.MkdirAll(dstPath, perm|0200)
}
// If the OS supports symlinks, use them instead of copying bytes.
if err := os.Symlink(srcPath, dstPath); err == nil {
return nil
}
// Otherwise, copy the bytes.
src, err := os.Open(srcPath)
if err != nil {
return err
}
defer src.Close()
dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, perm)
if err != nil {
return err
}
_, err = io.Copy(dst, src)
if closeErr := dst.Close(); err == nil {
err = closeErr
}
return err
})
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,15 @@
package dep2
import "testshared/depBase"
var W int = 1
var hasProg depBase.HasProg
type Dep2 struct {
depBase.Dep
}
func G() int {
return depBase.F() + 1
}

View File

@ -0,0 +1,22 @@
package dep3
// The point of this test file is that it references a type from
// depBase that is also referenced in dep2, but dep2 is loaded by the
// linker before depBase (because it is earlier in the import list).
// There was a bug in the linker where it would not correctly read out
// the type data in this case and later crash.
import (
"testshared/dep2"
"testshared/depBase"
)
type Dep3 struct {
dep depBase.Dep
dep2 dep2.Dep2
}
func D3() int {
var x Dep3
return x.dep.X + x.dep2.X
}

View File

@ -0,0 +1,10 @@
// Copyright 2014 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.
// +build gc
#include "textflag.h"
TEXT ·ImplementedInAsm(SB),NOSPLIT,$0-0
RET

View File

@ -0,0 +1,37 @@
// Copyright 2016 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 depBase
import (
"os"
"reflect"
)
var SlicePtr interface{} = &[]int{}
var V int = 1
var HasMask []string = []string{"hi"}
type HasProg struct {
array [1024]*byte
}
type Dep struct {
X int
}
func (d *Dep) Method() int {
// This code below causes various go.itab.* symbols to be generated in
// the shared library. Similar code in ../exe/exe.go results in
// exercising https://golang.org/issues/17594
reflect.TypeOf(os.Stdout).Elem()
return 10
}
func F() int {
defer func() {}()
return V
}

View File

@ -0,0 +1,9 @@
// Copyright 2016 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.
// +build gccgo
package depBase
func ImplementedInAsm() {}

View File

@ -0,0 +1,9 @@
// Copyright 2016 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.
// +build gc
package depBase
func ImplementedInAsm()

View File

@ -0,0 +1,17 @@
// Copyright 2017 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
//go:noinline
func div(x, y uint32) uint32 {
return x / y
}
func main() {
a := div(97, 11)
if a != 8 {
panic("FAIL")
}
}

45
misc/cgo/testshared/testdata/exe/exe.go vendored Normal file
View File

@ -0,0 +1,45 @@
package main
import (
"os"
"reflect"
"runtime"
"testshared/depBase"
)
// Having a function declared in the main package triggered
// golang.org/issue/18250
func DeclaredInMain() {
}
type C struct {
}
func F() *C {
return nil
}
var slicePtr interface{} = &[]int{}
func main() {
defer depBase.ImplementedInAsm()
// This code below causes various go.itab.* symbols to be generated in
// the executable. Similar code in ../depBase/dep.go results in
// exercising https://golang.org/issues/17594
reflect.TypeOf(os.Stdout).Elem()
runtime.GC()
depBase.V = depBase.F() + 1
var c *C
if reflect.TypeOf(F).Out(0) != reflect.TypeOf(c) {
panic("bad reflection results, see golang.org/issue/18252")
}
sp := reflect.New(reflect.TypeOf(slicePtr).Elem())
s := sp.Interface()
if reflect.TypeOf(s) != reflect.TypeOf(slicePtr) {
panic("bad reflection results, see golang.org/issue/18729")
}
}

View File

@ -0,0 +1,8 @@
package main
import "testshared/dep2"
func main() {
d := &dep2.Dep2{}
dep2.W = dep2.G() + 1 + d.Method()
}

View File

@ -0,0 +1,7 @@
package main
import "testshared/dep3"
func main() {
dep3.D3()
}

View File

@ -0,0 +1,8 @@
package main
/*
*/
import "C"
func main() {
}

View File

@ -0,0 +1,9 @@
package explicit
import (
"testshared/implicit"
)
func E() int {
return implicit.I()
}

View File

@ -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")
}
}
}

View File

@ -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

View File

@ -0,0 +1,71 @@
// Copyright 2017 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 (
"testshared/globallib"
)
//go:noinline
func testLoop() {
for i, s := range globallib.Data {
if s != int64(i) {
panic("testLoop: mismatch")
}
}
}
//go:noinline
func ptrData() *[1<<20 + 10]int64 {
return &globallib.Data
}
//go:noinline
func testMediumOffset() {
for i, s := range globallib.Data[1<<16-2:] {
if s != int64(i)+1<<16-2 {
panic("testMediumOffset: index mismatch")
}
}
x := globallib.Data[1<<16-1]
if x != 1<<16-1 {
panic("testMediumOffset: direct mismatch")
}
y := &globallib.Data[1<<16-3]
if y != &ptrData()[1<<16-3] {
panic("testMediumOffset: address mismatch")
}
}
//go:noinline
func testLargeOffset() {
for i, s := range globallib.Data[1<<20:] {
if s != int64(i)+1<<20 {
panic("testLargeOffset: index mismatch")
}
}
x := globallib.Data[1<<20+1]
if x != 1<<20+1 {
panic("testLargeOffset: direct mismatch")
}
y := &globallib.Data[1<<20+2]
if y != &ptrData()[1<<20+2] {
panic("testLargeOffset: address mismatch")
}
}
func main() {
testLoop()
// SSA rules commonly merge offsets into addresses. These
// tests access global data in different ways to try
// and exercise different SSA rules.
testMediumOffset()
testLargeOffset()
}

View File

@ -0,0 +1,17 @@
// Copyright 2017 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 globallib
// Data is large enough to that offsets into it do not fit into
// 16-bit or 20-bit immediates. Ideally we'd also try and overrun
// 32-bit immediates, but that requires the test machine to have
// too much memory.
var Data [1<<20 + 10]int64
func init() {
for i := range Data {
Data[i] = int64(i)
}
}

View File

@ -0,0 +1,17 @@
// Copyright 2017 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 "testshared/iface_a"
import "testshared/iface_b"
func main() {
if iface_a.F() != iface_b.F() {
panic("empty interfaces not equal")
}
if iface_a.G() != iface_b.G() {
panic("non-empty interfaces not equal")
}
}

View File

@ -0,0 +1,17 @@
// Copyright 2017 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 iface_a
import "testshared/iface_i"
//go:noinline
func F() interface{} {
return (*iface_i.T)(nil)
}
//go:noinline
func G() iface_i.I {
return (*iface_i.T)(nil)
}

View File

@ -0,0 +1,17 @@
// Copyright 2017 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 iface_b
import "testshared/iface_i"
//go:noinline
func F() interface{} {
return (*iface_i.T)(nil)
}
//go:noinline
func G() iface_i.I {
return (*iface_i.T)(nil)
}

View File

@ -0,0 +1,17 @@
// Copyright 2017 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 iface_i
type I interface {
M()
}
type T struct {
}
func (t *T) M() {
}
// *T implements I

View File

@ -0,0 +1,5 @@
package implicit
func I() int {
return 42
}

View File

@ -0,0 +1,10 @@
package main
import (
"testshared/explicit"
"testshared/implicit"
)
func main() {
println(implicit.I() + explicit.E())
}

View File

@ -0,0 +1,20 @@
// Copyright 2018 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 issue25065 has a type with a method that is
// 1) referenced in a method expression
// 2) not called
// 3) not converted to an interface
// 4) is a value method but the reference is to the pointer method
// These cases avoid the call to makefuncsym from typecheckfunc, but we
// still need to call makefuncsym somehow or the symbol will not be defined.
package issue25065
type T int
func (t T) M() {}
func F() func(*T) {
return (*T).M
}

View File

@ -0,0 +1,11 @@
// Copyright 2019 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 issue30768lib
// S is a struct that requires a generated hash function.
type S struct {
A string
B int
}

View File

@ -0,0 +1,22 @@
// Copyright 2019 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 issue30768_test
import (
"testing"
"testshared/issue30768/issue30768lib"
)
type s struct {
s issue30768lib.S
}
func Test30768(t *testing.T) {
// Calling t.Log will convert S to an empty interface,
// which will force a reference to the generated hash function,
// defined in the shared library.
t.Log(s{})
}

View File

@ -0,0 +1,9 @@
// 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 a
import "testshared/issue39777/b"
func F() { b.F() }

View File

@ -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 b
func F() {}

View File

@ -0,0 +1,9 @@
// 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 a
type ATypeWithALoooooongName interface { // a long name, so the type descriptor symbol name is mangled
M()
}

View File

@ -0,0 +1,17 @@
// 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 b
import "testshared/issue44031/a"
type T int
func (T) M() {}
var i = a.ATypeWithALoooooongName(T(0))
func F() {
i.M()
}

View File

@ -0,0 +1,20 @@
// 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 "testshared/issue44031/b"
type t int
func (t) m() {}
type i interface{ m() } // test that unexported method is correctly marked
var v interface{} = t(0)
func main() {
b.F()
v.(i).m()
}

View File

@ -0,0 +1,19 @@
// 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 a
type A interface {
M()
}
//go:noinline
func TheFuncWithArgA(a A) {
a.M()
}
type ImplA struct{}
//go:noinline
func (A *ImplA) M() {}

View File

@ -0,0 +1,14 @@
// 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 (
"testshared/issue47837/a"
)
func main() {
var vara a.ImplA
a.TheFuncWithArgA(&vara)
}

View File

@ -0,0 +1,9 @@
package main
func main() {
// This is enough to make sure that the executable references
// a type descriptor, which was the cause of
// https://golang.org/issue/25970.
c := make(chan int)
_ = c
}

View File

@ -762,6 +762,9 @@ func (t *tester) registerTests() {
if t.supportedBuildmode("c-shared") {
t.registerHostTest("testcshared", "../misc/cgo/testcshared", "misc/cgo/testcshared", ".")
}
if t.supportedBuildmode("shared") {
t.registerTest("testshared", "../misc/cgo/testshared", t.goTest(), t.timeout(600), ".")
}
if t.supportedBuildmode("plugin") {
t.registerTest("testplugin", "../misc/cgo/testplugin", t.goTest(), t.timeout(600), ".")
}
@ -1044,6 +1047,12 @@ func (t *tester) supportedBuildmode(mode string) bool {
return true
}
return false
case "shared":
switch pair {
case "linux-386", "linux-amd64", "linux-arm", "linux-arm64", "linux-ppc64le", "linux-s390x":
return true
}
return false
case "plugin":
// linux-arm64 is missing because it causes the external linker
// to crash, see https://golang.org/issue/17138

View File

@ -162,6 +162,9 @@
// flags has a similar effect.
// -ldflags '[pattern=]arg list'
// arguments to pass on each go tool link invocation.
// -linkshared
// build code that will be linked against shared libraries previously
// created with -buildmode=shared.
// -mod mode
// module download mode to use: readonly, vendor, or mod.
// By default, if a vendor directory is present and the go version in go.mod
@ -779,6 +782,7 @@
// Name string // package name
// Doc string // package documentation string
// Target string // install path
// Shlib string // the shared library that contains this package (only set when -linkshared)
// Goroot bool // is this package in the Go root?
// Standard bool // is this package part of the standard Go library?
// Stale bool // would 'go install' do anything for this package?
@ -1799,6 +1803,11 @@
// non-main packages are built into .a files (the default
// behavior).
//
// -buildmode=shared
// Combine all the listed non-main packages into a single shared
// library that will be used when building with the -linkshared
// option. Packages named main are ignored.
//
// -buildmode=exe
// Build the listed main packages and everything they import into
// executables. Packages not named main are ignored.

View File

@ -726,6 +726,11 @@ are:
non-main packages are built into .a files (the default
behavior).
-buildmode=shared
Combine all the listed non-main packages into a single shared
library that will be used when building with the -linkshared
option. Packages named main are ignored.
-buildmode=exe
Build the listed main packages and everything they import into
executables. Packages not named main are ignored.

View File

@ -23,8 +23,8 @@ import (
"cmd/go/internal/load"
"cmd/go/internal/modinfo"
"cmd/go/internal/modload"
"cmd/go/internal/str"
"cmd/go/internal/work"
"cmd/go/internal/str"
)
var CmdList = &base.Command{
@ -56,6 +56,7 @@ to -f '{{.ImportPath}}'. The struct being passed to the template is:
Name string // package name
Doc string // package documentation string
Target string // install path
Shlib string // the shared library that contains this package (only set when -linkshared)
Goroot bool // is this package in the Go root?
Standard bool // is this package part of the standard Go library?
Stale bool // would 'go install' do anything for this package?

View File

@ -116,6 +116,9 @@ and test commands:
flags has a similar effect.
-ldflags '[pattern=]arg list'
arguments to pass on each go tool link invocation.
-linkshared
build code that will be linked against shared libraries previously
created with -buildmode=shared.
-mod mode
module download mode to use: readonly, vendor, or mod.
By default, if a vendor directory is present and the go version in go.mod

View File

@ -233,20 +233,16 @@ func buildModeInit() {
}
ldBuildmode = "pie"
case "shared":
if cfg.Goos == "linux" {
switch cfg.Goarch {
case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
// -buildmode=shared was supported on these platforms at one point, but
// never really worked in module mode.
// Support was officially dropped as of Go 1.18.
// (See https://golang.org/issue/47788.)
base.Fatalf("-buildmode=shared no longer supported as of Go 1.18")
// TODO(#47788): Remove supporting code for -buildmode=shared.
// (For the Go 1.18 release, we will keep most of the code around but
// disabled to avoid merge conflicts in case we need to revert quickly.)
}
pkgsFilter = pkgsNotMain
if gccgo {
codegenArg = "-fPIC"
} else {
codegenArg = "-dynlink"
}
if cfg.BuildO != "" {
base.Fatalf("-buildmode=shared and -o not supported together")
}
ldBuildmode = "shared"
case "plugin":
pkgsFilter = oneMainPkg
if gccgo {
@ -265,15 +261,6 @@ func buildModeInit() {
}
if cfg.BuildLinkshared {
if cfg.Goos == "linux" {
switch cfg.Goarch {
case "386", "amd64", "arm", "arm64", "ppc64le", "s390x":
base.Fatalf("-linkshared no longer supported as of Go 1.18")
// TODO(#47788): Remove supporting code for linkshared.
// (For the Go 1.18 release, we will keep most of the code around but
// disabled to avoid merge conflicts in case we need to revert quickly.)
}
}
if !sys.BuildModeSupported(cfg.BuildToolchainName, "shared", cfg.Goos, cfg.Goarch) {
base.Fatalf("-linkshared not supported on %s/%s\n", cfg.Goos, cfg.Goarch)
}

View File

@ -0,0 +1,16 @@
env GO111MODULE=on
# golang.org/issue/35759: 'go list -linkshared'
# panicked if invoked on a test-only package.
[!buildmode:shared] skip
go list -f '{{.ImportPath}}: {{.Target}} {{.Shlib}}' -linkshared .
stdout '^example.com: $'
-- go.mod --
module example.com
go 1.14
-- x.go --
package x

View File

@ -132,6 +132,13 @@ func BuildModeSupported(compiler, buildmode, goos, goarch string) bool {
}
return false
case "shared":
switch platform {
case "linux/386", "linux/amd64", "linux/arm", "linux/arm64", "linux/ppc64le", "linux/s390x":
return true
}
return false
case "plugin":
switch platform {
case "linux/amd64", "linux/arm", "linux/arm64", "linux/386", "linux/s390x", "linux/ppc64le",