mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 06:38:42 +00:00
builder,loader: fix -ldflags -X not overriding variables with default values
When a string variable already had a source-level default value, the -ldflags "-X" flag was silently ignored and the variable remained the default value at runtime. Fix by stripping the InitOrder entry for each -X variable before LoadSSA() is called. With no entry in InitOrder, go/ssa emits no init store, so the global stays zero-valued in the IR. makeGlobalsModule still injects the actual -X values at final link time. The -X values remain out of the per-package build cache with only the variable names appearing in the cache key. Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
@@ -255,6 +255,18 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
|
|||||||
result.PackagePathMap[pkg.OriginalDir()] = pkg.Pkg.Path()
|
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
|
// 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 so it's pretty fast and doesn't need to be parallelized.
|
||||||
program := lprogram.LoadSSA()
|
program := lprogram.LoadSSA()
|
||||||
|
|||||||
@@ -304,6 +304,29 @@ func (p *Program) Sorted() []*Package {
|
|||||||
return p.sorted
|
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 {
|
||||||
|
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
|
// MainPkg returns the last package in the Sorted() slice. This is the main
|
||||||
// package of the program.
|
// package of the program.
|
||||||
func (p *Program) MainPkg() *Package {
|
func (p *Program) MainPkg() *Package {
|
||||||
|
|||||||
@@ -155,6 +155,19 @@ func TestBuild(t *testing.T) {
|
|||||||
}
|
}
|
||||||
runTestWithConfig("ldflags.go", t, opts, nil, nil)
|
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() {
|
if testing.Short() {
|
||||||
|
|||||||
Vendored
+9
@@ -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)
|
||||||
|
}
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
someGlobal: foobar
|
||||||
Vendored
+1
-2
@@ -1,7 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
// These globals can be changed using -ldflags="-X main.someGlobal=value".
|
// This global can be changed using -ldflags="-X main.someGlobal=value".
|
||||||
// At the moment, only globals without an initializer can be replaced this way.
|
|
||||||
var someGlobal string
|
var someGlobal string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
Reference in New Issue
Block a user