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