From 5428bfd270d9adb6acef7d535c235a076bf53950 Mon Sep 17 00:00:00 2001 From: Damian Gryski Date: Wed, 30 Apr 2025 10:31:15 -0700 Subject: [PATCH] runtime,os: add os.Executable() for Darwin --- src/os/executable_darwin.go | 10 ++++++++++ src/os/executable_other.go | 2 +- src/runtime/os_darwin.go | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 src/os/executable_darwin.go diff --git a/src/os/executable_darwin.go b/src/os/executable_darwin.go new file mode 100644 index 000000000..7c65a90c3 --- /dev/null +++ b/src/os/executable_darwin.go @@ -0,0 +1,10 @@ +//go:build darwin + +package os + +// via runtime because we need argc/argv ptrs +func runtime_executable_path() string + +func Executable() (string, error) { + return runtime_executable_path(), nil +} diff --git a/src/os/executable_other.go b/src/os/executable_other.go index 5da50ede1..ce99c1300 100644 --- a/src/os/executable_other.go +++ b/src/os/executable_other.go @@ -1,4 +1,4 @@ -//go:build !linux || baremetal +//go:build (!linux && !darwin) || baremetal package os diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go index e7f7b368f..df5c59880 100644 --- a/src/runtime/os_darwin.go +++ b/src/runtime/os_darwin.go @@ -229,3 +229,38 @@ func call_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) int32 //export tinygo_syscall6X func call_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) uintptr + +//go:linkname os_runtime_executable_path os.runtime_executable_path +func os_runtime_executable_path() string { + argv := (*unsafe.Pointer)(unsafe.Pointer(main_argv)) + + // skip over argv + argv = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(argv), (uintptr(main_argc)+1)*unsafe.Sizeof(argv))) + + // skip over envv + for (*argv) != nil { + argv = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(argv), unsafe.Sizeof(argv))) + } + + // next string is exe path + argv = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(argv), unsafe.Sizeof(argv))) + + cstr := unsafe.Pointer(*argv) + length := strlen(cstr) + argString := _string{ + length: length, + ptr: (*byte)(cstr), + } + executablePath := *(*string)(unsafe.Pointer(&argString)) + + // strip "executable_path=" prefix if available, it's added after OS X 10.11. + executablePath = stringsTrimPrefix(executablePath, "executable_path=") + return executablePath +} + +func stringsTrimPrefix(s, prefix string) string { + if len(s) >= len(prefix) && s[:len(prefix)] == prefix { + return s[len(prefix):] + } + return s +}