diff options
| author | Keith Randall <khr@golang.org> | 2023-01-12 20:25:39 -0800 |
|---|---|---|
| committer | Keith Randall <khr@golang.org> | 2023-04-14 16:55:22 +0000 |
| commit | 2b92c39fe08101ed8c9f032d577df4cc882d08d7 (patch) | |
| tree | 1446e0a68fa6fb81fdd141a241e8b818e6343151 /src/runtime | |
| parent | d4bcfe4e834da1d31b7071e83eb045e089271175 (diff) | |
| download | go-2b92c39fe08101ed8c9f032d577df4cc882d08d7.tar.xz | |
cmd/link: establish dependable package initialization order
(This is a retry of CL 462035 which was reverted at 474976.
The only change from that CL is the aix fix SRODATA->SNOPTRDATA
at inittask.go:141)
As described here:
https://github.com/golang/go/issues/31636#issuecomment-493271830
"Find the lexically earliest package that is not initialized yet,
but has had all its dependencies initialized, initialize that package,
and repeat."
Simplify the runtime a bit, by just computing the ordering required
in the linker and giving a list to the runtime.
Update #31636
Fixes #57411
RELNOTE=yes
Change-Id: I28c09451d6aa677d7394c179d23c2c02c503fc56
Reviewed-on: https://go-review.googlesource.com/c/go/+/478916
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Diffstat (limited to 'src/runtime')
| -rw-r--r-- | src/runtime/plugin.go | 10 | ||||
| -rw-r--r-- | src/runtime/proc.go | 60 | ||||
| -rw-r--r-- | src/runtime/symtab.go | 4 |
3 files changed, 40 insertions, 34 deletions
diff --git a/src/runtime/plugin.go b/src/runtime/plugin.go index a61dcc3b5d..312802de00 100644 --- a/src/runtime/plugin.go +++ b/src/runtime/plugin.go @@ -7,7 +7,7 @@ package runtime import "unsafe" //go:linkname plugin_lastmoduleinit plugin.lastmoduleinit -func plugin_lastmoduleinit() (path string, syms map[string]any, errstr string) { +func plugin_lastmoduleinit() (path string, syms map[string]any, initTasks []*initTask, errstr string) { var md *moduledata for pmd := firstmoduledata.next; pmd != nil; pmd = pmd.next { if pmd.bad { @@ -23,13 +23,13 @@ func plugin_lastmoduleinit() (path string, syms map[string]any, errstr string) { throw("runtime: plugin has empty pluginpath") } if md.typemap != nil { - return "", nil, "plugin already loaded" + return "", nil, nil, "plugin already loaded" } for _, pmd := range activeModules() { if pmd.pluginpath == md.pluginpath { md.bad = true - return "", nil, "plugin already loaded" + return "", nil, nil, "plugin already loaded" } if inRange(pmd.text, pmd.etext, md.text, md.etext) || @@ -51,7 +51,7 @@ func plugin_lastmoduleinit() (path string, syms map[string]any, errstr string) { for _, pkghash := range md.pkghashes { if pkghash.linktimehash != *pkghash.runtimehash { md.bad = true - return "", nil, "plugin was built with a different version of package " + pkghash.modulename + return "", nil, nil, "plugin was built with a different version of package " + pkghash.modulename } } @@ -90,7 +90,7 @@ func plugin_lastmoduleinit() (path string, syms map[string]any, errstr string) { } syms[name] = val } - return md.pluginpath, syms, "" + return md.pluginpath, syms, md.inittasks, "" } func pluginftabverify(md *moduledata) { diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 4152aa4852..90b6d81bf8 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -117,11 +117,9 @@ var ( raceprocctx0 uintptr ) -//go:linkname runtime_inittask runtime..inittask -var runtime_inittask initTask - -//go:linkname main_inittask main..inittask -var main_inittask initTask +// This slice records the initializing tasks that need to be +// done to start up the runtime. It is built by the linker. +var runtime_inittasks []*initTask // main_init_done is a signal used by cgocallbackg that initialization // has been completed. It is made before _cgo_notify_runtime_init_done, @@ -196,7 +194,7 @@ func main() { inittrace.active = true } - doInit(&runtime_inittask) // Must be before defer. + doInit(runtime_inittasks) // Must be before defer. // Defer unlock so that runtime.Goexit during init does the unlock too. needUnlock := true @@ -241,7 +239,14 @@ func main() { cgocall(_cgo_notify_runtime_init_done, nil) } - doInit(&main_inittask) + // Run the initializing tasks. Depending on build mode this + // 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() { + doInit(m.inittasks) + } // Disable init tracing after main init done to avoid overhead // of collecting statistics in malloc and newproc @@ -6520,14 +6525,11 @@ func gcd(a, b uint32) uint32 { } // An initTask represents the set of initializations that need to be done for a package. -// Keep in sync with ../../test/initempty.go:initTask +// Keep in sync with ../../test/noinit.go:initTask type initTask struct { - // TODO: pack the first 3 fields more tightly? - state uintptr // 0 = uninitialized, 1 = in progress, 2 = done - ndeps uintptr - nfns uintptr - // followed by ndeps instances of an *initTask, one per package depended on - // followed by nfns pcs, one per init function to run + state uint32 // 0 = uninitialized, 1 = in progress, 2 = done + nfns uint32 + // followed by nfns pcs, uintptr sized, one per init function to run } // inittrace stores statistics for init functions which are @@ -6541,7 +6543,13 @@ type tracestat struct { bytes uint64 // heap allocated bytes } -func doInit(t *initTask) { +func doInit(ts []*initTask) { + for _, t := range ts { + doInit1(t) + } +} + +func doInit1(t *initTask) { switch t.state { case 2: // fully initialized return @@ -6550,17 +6558,6 @@ func doInit(t *initTask) { default: // not initialized yet t.state = 1 // initialization in progress - for i := uintptr(0); i < t.ndeps; i++ { - p := add(unsafe.Pointer(t), (3+i)*goarch.PtrSize) - t2 := *(**initTask)(p) - doInit(t2) - } - - if t.nfns == 0 { - t.state = 2 // initialization done - return - } - var ( start int64 before tracestat @@ -6572,9 +6569,14 @@ func doInit(t *initTask) { before = inittrace } - firstFunc := add(unsafe.Pointer(t), (3+t.ndeps)*goarch.PtrSize) - for i := uintptr(0); i < t.nfns; i++ { - p := add(firstFunc, i*goarch.PtrSize) + if t.nfns == 0 { + // We should have pruned all of these in the linker. + throw("inittask with no functions") + } + + firstFunc := add(unsafe.Pointer(t), 8) + for i := uint32(0); i < t.nfns; i++ { + p := add(firstFunc, uintptr(i)*goarch.PtrSize) f := *(*func())(unsafe.Pointer(&p)) f() } diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index e2533ae250..7bf96c31fc 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -446,6 +446,10 @@ type moduledata struct { pluginpath string pkghashes []modulehash + // This slice records the initializing tasks that need to be + // done to start up the program. It is built by the linker. + inittasks []*initTask + modulename string modulehashes []modulehash |
