diff --git a/builder/build.go b/builder/build.go index c569bb1d21..54ff4779a1 100644 --- a/builder/build.go +++ b/builder/build.go @@ -1051,9 +1051,10 @@ func createEmbedObjectFile(data, hexSum, sourceFile, sourceDir, tmpdir string, c // needed to convert a program to its final form. Some transformations are not // optional and must be run as the compiler expects them to run. func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues map[string]map[string]string) error { - err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA()) - if err != nil { - return err + if config.Options.InterpTimeout != 0 { + if err := interp.Run(mod, config.Options.InterpTimeout, config.DumpSSA()); err != nil { + return err + } } if config.VerifyIR() { // Only verify if we really need it. @@ -1069,7 +1070,7 @@ func optimizeProgram(mod llvm.Module, config *compileopts.Config, globalValues m } // Insert values from -ldflags="-X ..." into the IR. - err = setGlobalValues(mod, globalValues) + err := setGlobalValues(mod, globalValues) if err != nil { return err } diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index a93260ba85..f6e9a44ccd 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -12,6 +12,12 @@ const Compiler = "tinygo" // package. func initAll() +var runtimeInitialized = false + +func runtime_is_initialized() bool { + return runtimeInitialized +} + //go:linkname callMain main.main func callMain() diff --git a/src/runtime/runtime_wasip1.go b/src/runtime/runtime_wasip1.go index 595cab9bf0..60707769af 100644 --- a/src/runtime/runtime_wasip1.go +++ b/src/runtime/runtime_wasip1.go @@ -15,10 +15,28 @@ func __wasm_call_ctors() //export _start func _start() { + runtimeInitialize() + run() +} + +//export runtime.initialize +func runtimeInitialize() { + if runtimeInitialized { + // Second time initialization is happening. Refresh environment + // to whatever our current host gives us instead of whatever + // libc cached. + reset_libc_environment() + return + } // These need to be initialized early so that the heap can be initialized. heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize) - run() + initHeap() + initAll() + if hasScheduler { + go func() {}() + } + runtimeInitialized = true } // Read the command line arguments from WASI. @@ -29,6 +47,17 @@ func init() { __wasm_call_ctors() } +//export __wasilibc_deinitialize_environ +func __wasilibc_deinitialize_environ() + +//export __wasilibc_initialize_environ +func __wasilibc_initialize_environ() + +func reset_libc_environment() { + __wasilibc_deinitialize_environ() + __wasilibc_initialize_environ() +} + var args []string //go:linkname os_runtime_args os.runtime_args @@ -39,7 +68,10 @@ func os_runtime_args() []string { var argc, argv_buf_size uint32 args_sizes_get(&argc, &argv_buf_size) if argc == 0 { - return nil + // Most things expect os.Args to have at least the + // program name. We don't have one, but also don't + // return just a nil slice. + return []string{""} } // Obtain the command line arguments diff --git a/src/runtime/scheduler_any.go b/src/runtime/scheduler_any.go index 0911a2dc73..e657a77bf8 100644 --- a/src/runtime/scheduler_any.go +++ b/src/runtime/scheduler_any.go @@ -19,9 +19,14 @@ func sleep(duration int64) { // run is called by the program entry point to execute the go program. // With a scheduler, init and the main function are invoked in a goroutine before starting the scheduler. func run() { - initHeap() + if !runtimeInitialized { + initHeap() + } go func() { - initAll() + if !runtimeInitialized { + initAll() + runtimeInitialized = true + } callMain() schedulerDone = true }() diff --git a/src/runtime/scheduler_none.go b/src/runtime/scheduler_none.go index 5079a80853..4fbdce235c 100644 --- a/src/runtime/scheduler_none.go +++ b/src/runtime/scheduler_none.go @@ -20,8 +20,11 @@ func getSystemStackPointer() uintptr { // run is called by the program entry point to execute the go program. // With the "none" scheduler, init and the main function are invoked directly. func run() { - initHeap() - initAll() + if !runtimeInitialized { + initHeap() + initAll() + runtimeInitialized = true + } callMain() } diff --git a/src/syscall/build_asserts.go b/src/syscall/build_asserts.go new file mode 100644 index 0000000000..beefe08097 --- /dev/null +++ b/src/syscall/build_asserts.go @@ -0,0 +1,7 @@ +//go:build syscall_asserts + +package syscall + +// enable assertions for checking Environ during runtime preinitialization. +// This is to catch people using os.Environ() during package inits with wizer. +const panicOnEnvironDuringInitAll = true diff --git a/src/syscall/build_noasserts.go b/src/syscall/build_noasserts.go new file mode 100644 index 0000000000..48ab0fb6af --- /dev/null +++ b/src/syscall/build_noasserts.go @@ -0,0 +1,7 @@ +//go:build !syscall_asserts + +package syscall + +// enable assertions for checking Environ during runtime preinitialization. +// This is to catch people using os.Environ() during package init with wizer. +const panicOnEnvironDuringInitAll = false diff --git a/src/syscall/syscall_libc.go b/src/syscall/syscall_libc.go index fb2e23968e..9133853167 100644 --- a/src/syscall/syscall_libc.go +++ b/src/syscall/syscall_libc.go @@ -260,7 +260,13 @@ func Mprotect(b []byte, prot int) (err error) { return } +//go:linkname runtime_is_initialized runtime.runtime_is_initialized +func runtime_is_initialized() bool + func Environ() []string { + if panicOnEnvironDuringInitAll && !runtime_is_initialized() { + panic("syscall.Environ() called during runtime preinitialization") + } // This function combines all the environment into a single allocation. // While this optimizes for memory usage and garbage collector