diff --git a/src/runtime/nonhosted.go b/src/runtime/nonhosted.go index 61f5023d..531c1c1e 100644 --- a/src/runtime/nonhosted.go +++ b/src/runtime/nonhosted.go @@ -11,6 +11,14 @@ package runtime // The primary use case is `tinygo test`, which takes some parameters (such as // -test.v). +// This is the default set of arguments, if nothing else has been set. +var args = []string{"/proc/self/exe"} + +//go:linkname os_runtime_args os.runtime_args +func os_runtime_args() []string { + return args +} + var env []string //go:linkname syscall_runtime_envs syscall.runtime_envs diff --git a/src/runtime/runtime.go b/src/runtime/runtime.go index 55102411..5243d34e 100644 --- a/src/runtime/runtime.go +++ b/src/runtime/runtime.go @@ -23,16 +23,6 @@ func GOROOT() string { return "/usr/local/go" } -// This is the default set of arguments, if nothing else has been set. -// This may be overriden by modifying this global at runtime init (for example, -// on Linux where there are real command line arguments). -var args = []string{"/proc/self/exe"} - -//go:linkname os_runtime_args os.runtime_args -func os_runtime_args() []string { - return args -} - // Copy size bytes from src to dst. The memory areas must not overlap. // Calls to this function are converted to LLVM intrinsic calls such as // llvm.memcpy.p0i8.p0i8.i32(dst, src, size, false). diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index 684063b4..23a41714 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -74,28 +74,9 @@ func postinit() {} func main(argc int32, argv *unsafe.Pointer) int { preinit() - // Make args global big enough so that it can store all command line - // arguments. Unfortunately this has to be done with some magic as the heap - // is not yet initialized. - argsSlice := (*struct { - ptr unsafe.Pointer - len uintptr - cap uintptr - })(unsafe.Pointer(&args)) - argsSlice.ptr = malloc(uintptr(argc) * (unsafe.Sizeof(uintptr(0))) * 3) - argsSlice.len = uintptr(argc) - argsSlice.cap = uintptr(argc) - - // Initialize command line parameters. - for i := 0; i < int(argc); i++ { - // Convert the C string to a Go string. - length := strlen(*argv) - arg := (*_string)(unsafe.Pointer(&args[i])) - arg.length = length - arg.ptr = (*byte)(*argv) - // This is the Go equivalent of "argc++" in C. - argv = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + unsafe.Sizeof(argv))) - } + // Store argc and argv for later use. + main_argc = argc + main_argv = argv // Obtain the initial stack pointer right before calling the run() function. // The run function has been moved to a separate (non-inlined) function so @@ -107,6 +88,34 @@ func main(argc int32, argv *unsafe.Pointer) int { return 0 } +var ( + main_argc int32 + main_argv *unsafe.Pointer + args []string +) + +//go:linkname os_runtime_args os.runtime_args +func os_runtime_args() []string { + if args == nil { + // Make args slice big enough so that it can store all command line + // arguments. + args = make([]string, main_argc) + + // Initialize command line parameters. + argv := main_argv + for i := 0; i < int(main_argc); i++ { + // Convert the C string to a Go string. + length := strlen(*argv) + arg := (*_string)(unsafe.Pointer(&args[i])) + arg.length = length + arg.ptr = (*byte)(*argv) + // This is the Go equivalent of "argv++" in C. + argv = (*unsafe.Pointer)(unsafe.Pointer(uintptr(unsafe.Pointer(argv)) + unsafe.Sizeof(argv))) + } + } + return args +} + // Must be a separate function to get the correct stack pointer. //go:noinline func runMain() { diff --git a/src/runtime/runtime_wasm_wasi.go b/src/runtime/runtime_wasm_wasi.go index 90f62614..96174e80 100644 --- a/src/runtime/runtime_wasm_wasi.go +++ b/src/runtime/runtime_wasm_wasi.go @@ -26,30 +26,38 @@ func _start() { // wasmtime ./program.wasm arg1 arg2 func init() { __wasm_call_ctors() +} - // Read the number of args (argc) and the buffer size required to store all - // these args (argv). - var argc, argv_buf_size uint32 - args_sizes_get(&argc, &argv_buf_size) - if argc == 0 { - return - } +var args []string - // Obtain the command line arguments - argsSlice := make([]unsafe.Pointer, argc) - buf := make([]byte, argv_buf_size) - args_get(&argsSlice[0], unsafe.Pointer(&buf[0])) - - // Convert the array of C strings to an array of Go strings. - args = make([]string, argc) - for i, cstr := range argsSlice { - length := strlen(cstr) - argString := _string{ - length: length, - ptr: (*byte)(cstr), +//go:linkname os_runtime_args os.runtime_args +func os_runtime_args() []string { + if args == nil { + // Read the number of args (argc) and the buffer size required to store + // all these args (argv). + var argc, argv_buf_size uint32 + args_sizes_get(&argc, &argv_buf_size) + if argc == 0 { + return nil + } + + // Obtain the command line arguments + argsSlice := make([]unsafe.Pointer, argc) + buf := make([]byte, argv_buf_size) + args_get(&argsSlice[0], unsafe.Pointer(&buf[0])) + + // Convert the array of C strings to an array of Go strings. + args = make([]string, argc) + for i, cstr := range argsSlice { + length := strlen(cstr) + argString := _string{ + length: length, + ptr: (*byte)(cstr), + } + args[i] = *(*string)(unsafe.Pointer(&argString)) } - args[i] = *(*string)(unsafe.Pointer(&argString)) } + return args } func ticksToNanoseconds(ticks timeUnit) int64 {