
This function is needed for Go 1.22, and is used from various packages like math/rand. When there is no random number generator available, it falls back to a static sequence of numbers. I think this is fine, because as far as I can see it is only used for non-cryptographic needs.
246 строки
6,4 КиБ
Go
246 строки
6,4 КиБ
Go
package runtime
|
|
|
|
import "unsafe"
|
|
|
|
//export abort
|
|
func abort()
|
|
|
|
//export exit
|
|
func libc_exit(code int)
|
|
|
|
//export putchar
|
|
func libc_putchar(c int) int
|
|
|
|
//export VirtualAlloc
|
|
func _VirtualAlloc(lpAddress unsafe.Pointer, dwSize uintptr, flAllocationType, flProtect uint32) unsafe.Pointer
|
|
|
|
//export QueryUnbiasedInterruptTime
|
|
func _QueryUnbiasedInterruptTime(UnbiasedTime *uint64) bool
|
|
|
|
// The parameter is really a LPFILETIME, but *uint64 should be compatible.
|
|
//
|
|
//export GetSystemTimeAsFileTime
|
|
func _GetSystemTimeAsFileTime(lpSystemTimeAsFileTime *uint64)
|
|
|
|
//export LoadLibraryExW
|
|
func _LoadLibraryExW(lpLibFileName *uint16, hFile uintptr, dwFlags uint32) uintptr
|
|
|
|
//export Sleep
|
|
func _Sleep(milliseconds uint32)
|
|
|
|
const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800
|
|
|
|
//export GetProcAddress
|
|
func getProcAddress(handle uintptr, procname *byte) uintptr
|
|
|
|
//export _configure_narrow_argv
|
|
func _configure_narrow_argv(int32) int32
|
|
|
|
//export __p___argc
|
|
func __p___argc() *int32
|
|
|
|
//export __p___argv
|
|
func __p___argv() **unsafe.Pointer
|
|
|
|
//export mainCRTStartup
|
|
func mainCRTStartup() int {
|
|
preinit()
|
|
|
|
// Obtain the initial stack pointer right before calling the run() function.
|
|
// The run function has been moved to a separate (non-inlined) function so
|
|
// that the correct stack pointer is read.
|
|
stackTop = getCurrentStackPointer()
|
|
runMain()
|
|
|
|
// For libc compatibility.
|
|
return 0
|
|
}
|
|
|
|
// Must be a separate function to get the correct stack pointer.
|
|
//
|
|
//go:noinline
|
|
func runMain() {
|
|
run()
|
|
}
|
|
|
|
var args []string
|
|
|
|
//go:linkname os_runtime_args os.runtime_args
|
|
func os_runtime_args() []string {
|
|
if args == nil {
|
|
// Obtain argc/argv from the environment.
|
|
_configure_narrow_argv(2)
|
|
argc := *__p___argc()
|
|
argv := *__p___argv()
|
|
|
|
// Make args slice big enough so that it can store all command line
|
|
// arguments.
|
|
args = make([]string, 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 "argv++" in C.
|
|
argv = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(argv), unsafe.Sizeof(argv)))
|
|
}
|
|
}
|
|
return args
|
|
}
|
|
|
|
func putchar(c byte) {
|
|
libc_putchar(int(c))
|
|
}
|
|
|
|
var heapSize uintptr = 128 * 1024 // small amount to start
|
|
var heapMaxSize uintptr
|
|
|
|
var heapStart, heapEnd uintptr
|
|
|
|
func preinit() {
|
|
// Allocate a large chunk of virtual memory. Because it is virtual, it won't
|
|
// really be allocated in RAM. Memory will only be allocated when it is
|
|
// first touched.
|
|
heapMaxSize = 1 * 1024 * 1024 * 1024 // 1GB for the entire heap
|
|
const (
|
|
MEM_COMMIT = 0x00001000
|
|
MEM_RESERVE = 0x00002000
|
|
PAGE_READWRITE = 0x04
|
|
)
|
|
heapStart = uintptr(_VirtualAlloc(nil, heapMaxSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE))
|
|
heapEnd = heapStart + heapSize
|
|
}
|
|
|
|
type timeUnit int64
|
|
|
|
var stackTop uintptr
|
|
|
|
func ticksToNanoseconds(ticks timeUnit) int64 {
|
|
// Interrupt time count works in units of 100 nanoseconds.
|
|
return int64(ticks) * 100
|
|
}
|
|
|
|
func nanosecondsToTicks(ns int64) timeUnit {
|
|
// Interrupt time count works in units of 100 nanoseconds.
|
|
return timeUnit(ns) / 100
|
|
}
|
|
|
|
func sleepTicks(d timeUnit) {
|
|
// Calcluate milliseconds from ticks (which have a resolution of 100ns),
|
|
// rounding up.
|
|
milliseconds := int64(d+9_999) / 10_000
|
|
for milliseconds != 0 {
|
|
duration := uint32(milliseconds)
|
|
_Sleep(duration)
|
|
milliseconds -= int64(duration)
|
|
}
|
|
}
|
|
|
|
func ticks() timeUnit {
|
|
var unbiasedTime uint64
|
|
_QueryUnbiasedInterruptTime(&unbiasedTime)
|
|
return timeUnit(unbiasedTime)
|
|
}
|
|
|
|
//go:linkname now time.now
|
|
func now() (sec int64, nsec int32, mono int64) {
|
|
// Get the current time in Windows "file time" format.
|
|
var time uint64
|
|
_GetSystemTimeAsFileTime(&time)
|
|
|
|
// Convert file time to Unix time.
|
|
// According to the documentation:
|
|
// > Contains a 64-bit value representing the number of 100-nanosecond
|
|
// > intervals since January 1, 1601 (UTC).
|
|
// We'll convert it to 100 nanosecond intervals starting at 1970.
|
|
const (
|
|
// number of 100-nanosecond intervals in a second
|
|
intervalsPerSecond = 10_000_000
|
|
secondsPerDay = 60 * 60 * 24
|
|
// Number of days between the Windows epoch (1 january 1601) and the
|
|
// Unix epoch (1 january 1970). Source:
|
|
// https://www.wolframalpha.com/input/?i=days+between+1+january+1601+and+1+january+1970
|
|
days = 134774
|
|
)
|
|
time -= days * secondsPerDay * intervalsPerSecond
|
|
|
|
// Convert the time (in 100ns units) to sec/nsec/mono as expected by the
|
|
// time package.
|
|
sec = int64(time / intervalsPerSecond)
|
|
nsec = int32((time - (uint64(sec) * intervalsPerSecond)) * 100)
|
|
mono = ticksToNanoseconds(ticks())
|
|
return
|
|
}
|
|
|
|
//go:linkname syscall_Exit syscall.Exit
|
|
func syscall_Exit(code int) {
|
|
libc_exit(code)
|
|
}
|
|
|
|
func growHeap() bool {
|
|
if heapSize == heapMaxSize {
|
|
// Already at the max. If we run out of memory, we should consider
|
|
// increasing heapMaxSize..
|
|
return false
|
|
}
|
|
// Grow the heap size used by the program.
|
|
heapSize = (heapSize * 4 / 3) &^ 4095 // grow by around 33%
|
|
if heapSize > heapMaxSize {
|
|
heapSize = heapMaxSize
|
|
}
|
|
setHeapEnd(heapStart + heapSize)
|
|
return true
|
|
}
|
|
|
|
//go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary
|
|
func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) {
|
|
handle = _LoadLibraryExW(filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)
|
|
if handle == 0 {
|
|
panic("todo: get error")
|
|
}
|
|
return
|
|
}
|
|
|
|
//go:linkname syscall_loadlibrary syscall.loadlibrary
|
|
func syscall_loadlibrary(filename *uint16) (handle, err uintptr) {
|
|
panic("todo: syscall.loadlibrary")
|
|
}
|
|
|
|
//go:linkname syscall_getprocaddress syscall.getprocaddress
|
|
func syscall_getprocaddress(handle uintptr, procname *byte) (outhandle, err uintptr) {
|
|
outhandle = getProcAddress(handle, procname)
|
|
if outhandle == 0 {
|
|
panic("todo: get error")
|
|
}
|
|
return
|
|
}
|
|
|
|
// TinyGo does not yet support any form of parallelism on Windows, so these can
|
|
// be left empty.
|
|
|
|
//go:linkname procPin sync/atomic.runtime_procPin
|
|
func procPin() {
|
|
}
|
|
|
|
//go:linkname procUnpin sync/atomic.runtime_procUnpin
|
|
func procUnpin() {
|
|
}
|
|
|
|
func hardwareRand() (n uint64, ok bool) {
|
|
var n1, n2 uint32
|
|
errCode1 := libc_rand_s(&n1)
|
|
errCode2 := libc_rand_s(&n2)
|
|
n = uint64(n1)<<32 | uint64(n2)
|
|
ok = errCode1 == 0 && errCode2 == 0
|
|
return
|
|
}
|
|
|
|
// Cryptographically secure random number generator.
|
|
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rand-s?view=msvc-170
|
|
// errno_t rand_s(unsigned int* randomValue);
|
|
//
|
|
//export rand_s
|
|
func libc_rand_s(randomValue *uint32) int32
|