github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_windows.go (about) 1 package runtime 2 3 import "unsafe" 4 5 //export abort 6 func abort() 7 8 //export exit 9 func libc_exit(code int) 10 11 //export putchar 12 func libc_putchar(c int) int 13 14 //export VirtualAlloc 15 func _VirtualAlloc(lpAddress unsafe.Pointer, dwSize uintptr, flAllocationType, flProtect uint32) unsafe.Pointer 16 17 //export QueryUnbiasedInterruptTime 18 func _QueryUnbiasedInterruptTime(UnbiasedTime *uint64) bool 19 20 // The parameter is really a LPFILETIME, but *uint64 should be compatible. 21 // 22 //export GetSystemTimeAsFileTime 23 func _GetSystemTimeAsFileTime(lpSystemTimeAsFileTime *uint64) 24 25 //export LoadLibraryExW 26 func _LoadLibraryExW(lpLibFileName *uint16, hFile uintptr, dwFlags uint32) uintptr 27 28 //export Sleep 29 func _Sleep(milliseconds uint32) 30 31 const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800 32 33 //export GetProcAddress 34 func getProcAddress(handle uintptr, procname *byte) uintptr 35 36 //export _configure_narrow_argv 37 func _configure_narrow_argv(int32) int32 38 39 //export __p___argc 40 func __p___argc() *int32 41 42 //export __p___argv 43 func __p___argv() **unsafe.Pointer 44 45 //export mainCRTStartup 46 func mainCRTStartup() int { 47 preinit() 48 49 // Obtain the initial stack pointer right before calling the run() function. 50 // The run function has been moved to a separate (non-inlined) function so 51 // that the correct stack pointer is read. 52 stackTop = getCurrentStackPointer() 53 runMain() 54 55 // For libc compatibility. 56 return 0 57 } 58 59 // Must be a separate function to get the correct stack pointer. 60 // 61 //go:noinline 62 func runMain() { 63 run() 64 } 65 66 var args []string 67 68 //go:linkname os_runtime_args os.runtime_args 69 func os_runtime_args() []string { 70 if args == nil { 71 // Obtain argc/argv from the environment. 72 _configure_narrow_argv(2) 73 argc := *__p___argc() 74 argv := *__p___argv() 75 76 // Make args slice big enough so that it can store all command line 77 // arguments. 78 args = make([]string, argc) 79 80 // Initialize command line parameters. 81 for i := 0; i < int(argc); i++ { 82 // Convert the C string to a Go string. 83 length := strlen(*argv) 84 arg := (*_string)(unsafe.Pointer(&args[i])) 85 arg.length = length 86 arg.ptr = (*byte)(*argv) 87 // This is the Go equivalent of "argv++" in C. 88 argv = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(argv), unsafe.Sizeof(argv))) 89 } 90 } 91 return args 92 } 93 94 func putchar(c byte) { 95 libc_putchar(int(c)) 96 } 97 98 var heapSize uintptr = 128 * 1024 // small amount to start 99 var heapMaxSize uintptr 100 101 var heapStart, heapEnd uintptr 102 103 func preinit() { 104 // Allocate a large chunk of virtual memory. Because it is virtual, it won't 105 // really be allocated in RAM. Memory will only be allocated when it is 106 // first touched. 107 heapMaxSize = 1 * 1024 * 1024 * 1024 // 1GB for the entire heap 108 const ( 109 MEM_COMMIT = 0x00001000 110 MEM_RESERVE = 0x00002000 111 PAGE_READWRITE = 0x04 112 ) 113 heapStart = uintptr(_VirtualAlloc(nil, heapMaxSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)) 114 heapEnd = heapStart + heapSize 115 } 116 117 type timeUnit int64 118 119 var stackTop uintptr 120 121 func ticksToNanoseconds(ticks timeUnit) int64 { 122 // Interrupt time count works in units of 100 nanoseconds. 123 return int64(ticks) * 100 124 } 125 126 func nanosecondsToTicks(ns int64) timeUnit { 127 // Interrupt time count works in units of 100 nanoseconds. 128 return timeUnit(ns) / 100 129 } 130 131 func sleepTicks(d timeUnit) { 132 // Calcluate milliseconds from ticks (which have a resolution of 100ns), 133 // rounding up. 134 milliseconds := int64(d+9_999) / 10_000 135 for milliseconds != 0 { 136 duration := uint32(milliseconds) 137 _Sleep(duration) 138 milliseconds -= int64(duration) 139 } 140 } 141 142 func ticks() timeUnit { 143 var unbiasedTime uint64 144 _QueryUnbiasedInterruptTime(&unbiasedTime) 145 return timeUnit(unbiasedTime) 146 } 147 148 //go:linkname now time.now 149 func now() (sec int64, nsec int32, mono int64) { 150 // Get the current time in Windows "file time" format. 151 var time uint64 152 _GetSystemTimeAsFileTime(&time) 153 154 // Convert file time to Unix time. 155 // According to the documentation: 156 // > Contains a 64-bit value representing the number of 100-nanosecond 157 // > intervals since January 1, 1601 (UTC). 158 // We'll convert it to 100 nanosecond intervals starting at 1970. 159 const ( 160 // number of 100-nanosecond intervals in a second 161 intervalsPerSecond = 10_000_000 162 secondsPerDay = 60 * 60 * 24 163 // Number of days between the Windows epoch (1 january 1601) and the 164 // Unix epoch (1 january 1970). Source: 165 // https://www.wolframalpha.com/input/?i=days+between+1+january+1601+and+1+january+1970 166 days = 134774 167 ) 168 time -= days * secondsPerDay * intervalsPerSecond 169 170 // Convert the time (in 100ns units) to sec/nsec/mono as expected by the 171 // time package. 172 sec = int64(time / intervalsPerSecond) 173 nsec = int32((time - (uint64(sec) * intervalsPerSecond)) * 100) 174 mono = ticksToNanoseconds(ticks()) 175 return 176 } 177 178 //go:linkname syscall_Exit syscall.Exit 179 func syscall_Exit(code int) { 180 libc_exit(code) 181 } 182 183 func growHeap() bool { 184 if heapSize == heapMaxSize { 185 // Already at the max. If we run out of memory, we should consider 186 // increasing heapMaxSize.. 187 return false 188 } 189 // Grow the heap size used by the program. 190 heapSize = (heapSize * 4 / 3) &^ 4095 // grow by around 33% 191 if heapSize > heapMaxSize { 192 heapSize = heapMaxSize 193 } 194 setHeapEnd(heapStart + heapSize) 195 return true 196 } 197 198 //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary 199 func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) { 200 handle = _LoadLibraryExW(filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32) 201 if handle == 0 { 202 panic("todo: get error") 203 } 204 return 205 } 206 207 //go:linkname syscall_loadlibrary syscall.loadlibrary 208 func syscall_loadlibrary(filename *uint16) (handle, err uintptr) { 209 panic("todo: syscall.loadlibrary") 210 } 211 212 //go:linkname syscall_getprocaddress syscall.getprocaddress 213 func syscall_getprocaddress(handle uintptr, procname *byte) (outhandle, err uintptr) { 214 outhandle = getProcAddress(handle, procname) 215 if outhandle == 0 { 216 panic("todo: get error") 217 } 218 return 219 } 220 221 // TinyGo does not yet support any form of parallelism on Windows, so these can 222 // be left empty. 223 224 //go:linkname procPin sync/atomic.runtime_procPin 225 func procPin() { 226 } 227 228 //go:linkname procUnpin sync/atomic.runtime_procUnpin 229 func procUnpin() { 230 } 231 232 func hardwareRand() (n uint64, ok bool) { 233 var n1, n2 uint32 234 errCode1 := libc_rand_s(&n1) 235 errCode2 := libc_rand_s(&n2) 236 n = uint64(n1)<<32 | uint64(n2) 237 ok = errCode1 == 0 && errCode2 == 0 238 return 239 } 240 241 // Cryptographically secure random number generator. 242 // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rand-s?view=msvc-170 243 // errno_t rand_s(unsigned int* randomValue); 244 // 245 //export rand_s 246 func libc_rand_s(randomValue *uint32) int32