Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,18 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
result.PackagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
}

// Strip default initializers for -X globals from the type info before
// building SSA. This prevents go/ssa from emitting init stores for them,
// so that makeGlobalsModule can supply the correct values at final link
// time without any runtime init overwriting them. The -X values themselves
// are kept out of the per-package build cache; only the variable names
// appear in the cache key.
for _, pkg := range lprogram.Sorted() {
for name := range globalValues[pkg.Pkg.Path()] {
pkg.StripVarInitializer(name)
}
}

// Create the *ssa.Program. This does not yet build the entire SSA of the
// program so it's pretty fast and doesn't need to be parallelized.
program := lprogram.LoadSSA()
Expand Down
23 changes: 23 additions & 0 deletions loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,29 @@ func (p *Program) Sorted() []*Package {
return p.sorted
}

// StripVarInitializer removes the package-level initializer for the named
// variable from the type info. This prevents go/ssa from emitting an init
// store for it, leaving the global zero-initialized in the IR. An external
// value (e.g. from -ldflags -X via makeGlobalsModule) can then be linked in
// at final link time without any runtime init overwriting it.
//
// Must be called after Parse() (typechecking populates InitOrder) and before
// LoadSSA() (which consumes InitOrder to build the package init function).
//
// Only 1:1 var initializers (var x = expr) are matched. Multi-variable
// initializers (var x, y = f()) are left untouched.
func (p *Package) StripVarInitializer(name string) {
n := 0
for _, init := range p.info.InitOrder {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of removing them from the AST but I guess this also works 🤷‍♀️

Also TIL about InitOrder which might come in handy for other stuff!

if len(init.Lhs) == 1 && init.Lhs[0].Name() == name {
continue // drop this initializer
}
p.info.InitOrder[n] = init
n++
}
p.info.InitOrder = p.info.InitOrder[:n]
}

// MainPkg returns the last package in the Sorted() slice. This is the main
// package of the program.
func (p *Program) MainPkg() *Package {
Expand Down
13 changes: 13 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,19 @@ func TestBuild(t *testing.T) {
}
runTestWithConfig("ldflags.go", t, opts, nil, nil)
})

// Same as ldflags, but the global has a default value in source.
// -ldflags -X must override it, matching standard Go behaviour.
t.Run("ldflags-initialized", func(t *testing.T) {
t.Parallel()
opts := optionsFromTarget("", sema)
opts.GlobalValues = map[string]map[string]string{
"main": {
"someGlobal": "foobar",
},
}
runTestWithConfig("ldflags-initialized.go", t, opts, nil, nil)
})
})

if testing.Short() {
Expand Down
9 changes: 9 additions & 0 deletions testdata/ldflags-initialized.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package main

// This global has a default value. It should be overridable via
// -ldflags="-X main.someGlobal=value" just like an uninitialized global.
var someGlobal = "default"

func main() {
println("someGlobal:", someGlobal)
}
1 change: 1 addition & 0 deletions testdata/ldflags-initialized.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
someGlobal: foobar
3 changes: 1 addition & 2 deletions testdata/ldflags.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

// These globals can be changed using -ldflags="-X main.someGlobal=value".
// At the moment, only globals without an initializer can be replaced this way.
// This global can be changed using -ldflags="-X main.someGlobal=value".
var someGlobal string

func main() {
Expand Down
Loading