]> Cypherpunks.ru repositories - gostls13.git/commitdiff
cmd/link, runtime: initialize packages in shared build mode
authorCherry Mui <cherryyz@google.com>
Wed, 16 Aug 2023 23:49:04 +0000 (19:49 -0400)
committerCherry Mui <cherryyz@google.com>
Wed, 20 Sep 2023 14:46:11 +0000 (14:46 +0000)
Currently, for the shared build mode, we don't generate the module
inittasks. Instead, we rely on the main executable to do the
initialization, for both the executable and the shared library.
But, with the model as of CL 478916, the main executable only
has relocations to packages that are directly imported. It won't
see the dependency edges between packages within a shared library.
Therefore indirect dependencies are not included, and thus not
initialized. E.g. main imports a, which imports b, but main
doesn't directly import b. a and b are in a shared object. When
linking main, it sees main depends on a, so it generates main's
inittasks to run a's init before main's, but it doesn't know b,
so b's init doesn't run.

This CL makes it initialize all packages in a shared library when
the library is loaded, as any of them could potentially be
imported, directly or indirectly.

Also, in the runtime, when running the init functions, make sure
to go through the DSOs in dependency order. Otherwise packages
can be initialized in the wrong order.

Fixes #61973.

Change-Id: I2a090336fe9fa0d6c7e43912f3ab233c9c47e247
Reviewed-on: https://go-review.googlesource.com/c/go/+/520375
Reviewed-by: Than McIntosh <thanm@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/cmd/cgo/internal/testshared/testdata/dep2/dep2.go
src/cmd/cgo/internal/testshared/testdata/depBase/dep.go
src/cmd/cgo/internal/testshared/testdata/depBaseInternal/dep.go [new file with mode: 0644]
src/cmd/link/internal/ld/deadcode.go
src/cmd/link/internal/ld/inittask.go
src/runtime/proc.go

index 94f38cf507f3fa1047b75eecfe90ff95638fe5f3..18d774b5fcb07ccb8b9eae444191963a8c20a5e3 100644 (file)
@@ -2,6 +2,12 @@ package dep2
 
 import "testshared/depBase"
 
+func init() {
+       if !depBase.Initialized {
+               panic("depBase not initialized")
+       }
+}
+
 var W int = 1
 
 var hasProg depBase.HasProg
index e7cc7c81eb320d50b0284516555baf013261f40c..a143fe2ff1604599bf2568f4b1914647969ae50d 100644 (file)
@@ -7,8 +7,24 @@ package depBase
 import (
        "os"
        "reflect"
+
+       "testshared/depBaseInternal"
 )
 
+// Issue 61973: indirect dependencies are not initialized.
+func init() {
+       if !depBaseInternal.Initialized {
+               panic("depBaseInternal not initialized")
+       }
+       if os.Stdout == nil {
+               panic("os.Stdout is nil")
+       }
+
+       Initialized = true
+}
+
+var Initialized bool
+
 var SlicePtr interface{} = &[]int{}
 
 var V int = 1
diff --git a/src/cmd/cgo/internal/testshared/testdata/depBaseInternal/dep.go b/src/cmd/cgo/internal/testshared/testdata/depBaseInternal/dep.go
new file mode 100644 (file)
index 0000000..906bff0
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2023 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.
+
+// depBaseInternal is only imported by depBase.
+
+package depBaseInternal
+
+var Initialized bool
+
+func init() {
+       Initialized = true
+}
index a051e43401691575d79116b2f973b30424fdc1af..70b4a7ca30327d752a735b655fc2419a8af2a12b 100644 (file)
@@ -51,6 +51,7 @@ func (d *deadcodePass) init() {
                        s := loader.Sym(i)
                        d.mark(s, 0)
                }
+               d.mark(d.ctxt.mainInittasks, 0)
                return
        }
 
index 0699107cd019f61aa233b3da610d9fc66ae4eb50..c4c5beb55ed99e4191888217d11fcd658b673091 100644 (file)
@@ -41,15 +41,21 @@ func (ctxt *Link) inittasks() {
        switch ctxt.BuildMode {
        case BuildModeExe, BuildModePIE, BuildModeCArchive, BuildModeCShared:
                // Normally the inittask list will be run on program startup.
-               ctxt.mainInittasks = ctxt.inittaskSym("main..inittask", "go:main.inittasks")
+               ctxt.mainInittasks = ctxt.inittaskSym([]string{"main..inittask"}, "go:main.inittasks")
        case BuildModePlugin:
                // For plugins, the list will be run on plugin load.
-               ctxt.mainInittasks = ctxt.inittaskSym(fmt.Sprintf("%s..inittask", objabi.PathToPrefix(*flagPluginPath)), "go:plugin.inittasks")
+               ctxt.mainInittasks = ctxt.inittaskSym([]string{fmt.Sprintf("%s..inittask", objabi.PathToPrefix(*flagPluginPath))}, "go:plugin.inittasks")
                // Make symbol local so multiple plugins don't clobber each other's inittask list.
                ctxt.loader.SetAttrLocal(ctxt.mainInittasks, true)
        case BuildModeShared:
-               // Nothing to do. The inittask list will be built by
-               // the final build (with the -linkshared option).
+               // For a shared library, all packages are roots.
+               var roots []string
+               for _, lib := range ctxt.Library {
+                       roots = append(roots, fmt.Sprintf("%s..inittask", objabi.PathToPrefix(lib.Pkg)))
+               }
+               ctxt.mainInittasks = ctxt.inittaskSym(roots, "go:shlib.inittasks")
+               // Make symbol local so multiple plugins don't clobber each other's inittask list.
+               ctxt.loader.SetAttrLocal(ctxt.mainInittasks, true)
        default:
                Exitf("unhandled build mode %d", ctxt.BuildMode)
        }
@@ -58,7 +64,7 @@ func (ctxt *Link) inittasks() {
        // initialize the runtime_inittasks variable.
        ldr := ctxt.loader
        if ldr.Lookup("runtime.runtime_inittasks", 0) != 0 {
-               t := ctxt.inittaskSym("runtime..inittask", "go:runtime.inittasks")
+               t := ctxt.inittaskSym([]string{"runtime..inittask"}, "go:runtime.inittasks")
 
                // This slice header is already defined in runtime/proc.go, so we update it here with new contents.
                sh := ldr.Lookup("runtime.runtime_inittasks", 0)
@@ -72,11 +78,17 @@ func (ctxt *Link) inittasks() {
 }
 
 // inittaskSym builds a symbol containing pointers to all the inittasks
-// that need to be run, given the root inittask symbol.
-func (ctxt *Link) inittaskSym(rootName, symName string) loader.Sym {
+// that need to be run, given a list of root inittask symbols.
+func (ctxt *Link) inittaskSym(rootNames []string, symName string) loader.Sym {
        ldr := ctxt.loader
-       root := ldr.Lookup(rootName, 0)
-       if root == 0 {
+       var roots []loader.Sym
+       for _, n := range rootNames {
+               p := ldr.Lookup(n, 0)
+               if p != 0 {
+                       roots = append(roots, p)
+               }
+       }
+       if len(roots) == 0 {
                // Nothing to do
                return 0
        }
@@ -98,13 +110,15 @@ func (ctxt *Link) inittaskSym(rootName, symName string) loader.Sym {
        // p's direct imports that have not yet been scheduled.
        m := map[loader.Sym]int{}
 
-       // Find all reachable inittask records from the root.
+       // Find all reachable inittask records from the roots.
        // Keep track of the dependency edges between them in edges.
        // Keep track of how many imports each package has in m.
        // q is the list of found but not yet explored packages.
        var q []loader.Sym
-       m[root] = 0
-       q = append(q, root)
+       for _, p := range roots {
+               m[p] = 0
+               q = append(q, p)
+       }
        for len(q) > 0 {
                x := q[len(q)-1]
                q = q[:len(q)-1]
index 263945dd6c24780da6b15ae9427b6df6618ecce1..fa76d3250cc8aff89c3b577f6f708e9383a84631 100644 (file)
@@ -244,8 +244,10 @@ func main() {
        // list can arrive a few different ways, but it will always
        // contain the init tasks computed by the linker for all the
        // packages in the program (excluding those added at runtime
-       // by package plugin).
-       for _, m := range activeModules() {
+       // by package plugin). Run through the modules in dependency
+       // order (the order they are initialized by the dynamic
+       // loader, i.e. they are added to the moduledata linked list).
+       for m := &firstmoduledata; m != nil; m = m.next {
                doInit(m.inittasks)
        }