mirror of https://github.com/tinygo-org/tinygo.git
Browse Source
This uses Mingw-w64, which seems to be the de facto standard for porting Unixy programs to Windows.pull/2272/head
Ayke van Laethem
3 years ago
committed by
Ron Evans
26 changed files with 841 additions and 161 deletions
@ -0,0 +1,91 @@ |
|||
package builder |
|||
|
|||
import ( |
|||
"io" |
|||
"os" |
|||
"path/filepath" |
|||
"strings" |
|||
|
|||
"github.com/tinygo-org/tinygo/goenv" |
|||
) |
|||
|
|||
var MinGW = Library{ |
|||
name: "mingw-w64", |
|||
makeHeaders: func(target, includeDir string) error { |
|||
// copy _mingw.h
|
|||
srcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "mingw-w64") |
|||
outf, err := os.Create(includeDir + "/_mingw.h") |
|||
if err != nil { |
|||
return err |
|||
} |
|||
defer outf.Close() |
|||
inf, err := os.Open(srcDir + "/mingw-w64-headers/crt/_mingw.h.in") |
|||
if err != nil { |
|||
return err |
|||
} |
|||
_, err = io.Copy(outf, inf) |
|||
return err |
|||
}, |
|||
cflags: func(target, headerPath string) []string { |
|||
// No flags necessary because there are no files to compile.
|
|||
return nil |
|||
}, |
|||
librarySources: func(target string) []string { |
|||
// We only use the UCRT DLL file. No source files necessary.
|
|||
return nil |
|||
}, |
|||
} |
|||
|
|||
// makeMinGWExtraLibs returns a slice of jobs to import the correct .dll
|
|||
// libraries. This is done by converting input .def files to .lib files which
|
|||
// can then be linked as usual.
|
|||
//
|
|||
// TODO: cache the result. At the moment, it costs a few hundred milliseconds to
|
|||
// compile these files.
|
|||
func makeMinGWExtraLibs(tmpdir string) []*compileJob { |
|||
var jobs []*compileJob |
|||
root := goenv.Get("TINYGOROOT") |
|||
// Normally all the api-ms-win-crt-*.def files are all compiled to a single
|
|||
// .lib file. But to simplify things, we're going to leave them as separate
|
|||
// files.
|
|||
for _, name := range []string{ |
|||
"kernel32.def.in", |
|||
"api-ms-win-crt-conio-l1-1-0.def", |
|||
"api-ms-win-crt-convert-l1-1-0.def", |
|||
"api-ms-win-crt-environment-l1-1-0.def", |
|||
"api-ms-win-crt-filesystem-l1-1-0.def", |
|||
"api-ms-win-crt-heap-l1-1-0.def", |
|||
"api-ms-win-crt-locale-l1-1-0.def", |
|||
"api-ms-win-crt-math-l1-1-0.def.in", |
|||
"api-ms-win-crt-multibyte-l1-1-0.def", |
|||
"api-ms-win-crt-private-l1-1-0.def.in", |
|||
"api-ms-win-crt-process-l1-1-0.def", |
|||
"api-ms-win-crt-runtime-l1-1-0.def.in", |
|||
"api-ms-win-crt-stdio-l1-1-0.def", |
|||
"api-ms-win-crt-string-l1-1-0.def", |
|||
"api-ms-win-crt-time-l1-1-0.def", |
|||
"api-ms-win-crt-utility-l1-1-0.def", |
|||
} { |
|||
outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib") |
|||
inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name) |
|||
job := &compileJob{ |
|||
description: "create lib file " + inpath, |
|||
result: outpath, |
|||
run: func(job *compileJob) error { |
|||
defpath := inpath |
|||
if strings.HasSuffix(inpath, ".in") { |
|||
// .in files need to be preprocessed by a preprocessor (-E)
|
|||
// first.
|
|||
defpath = outpath + ".def" |
|||
err := runCCompiler("-E", "-x", "c", "-Wp,-w", "-P", "-DDEF_X64", "-o", defpath, inpath, "-I"+goenv.Get("TINYGOROOT")+"/lib/mingw-w64/mingw-w64-crt/def-include/") |
|||
if err != nil { |
|||
return err |
|||
} |
|||
} |
|||
return link("ld.lld", "-m", "i386pep", "-o", outpath, defpath) |
|||
}, |
|||
} |
|||
jobs = append(jobs, job) |
|||
} |
|||
return jobs |
|||
} |
@ -0,0 +1,42 @@ |
|||
package rand |
|||
|
|||
import "errors" |
|||
|
|||
func init() { |
|||
Reader = &reader{} |
|||
} |
|||
|
|||
type reader struct { |
|||
} |
|||
|
|||
var errRandom = errors.New("failed to obtain random data from rand_s") |
|||
|
|||
func (r *reader) Read(b []byte) (n int, err error) { |
|||
if len(b) == 0 { |
|||
return |
|||
} |
|||
|
|||
var randomByte uint32 |
|||
for i := range b { |
|||
// Call rand_s every four bytes because it's a C int (always 32-bit in
|
|||
// Windows).
|
|||
if i%4 == 0 { |
|||
errCode := libc_rand_s(&randomByte) |
|||
if errCode != 0 { |
|||
// According to the documentation, it can return an error.
|
|||
return n, errRandom |
|||
} |
|||
} else { |
|||
randomByte >>= 8 |
|||
} |
|||
b[i] = byte(randomByte) |
|||
} |
|||
|
|||
return len(b), nil |
|||
} |
|||
|
|||
// 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 |
@ -0,0 +1,57 @@ |
|||
// Windows on amd64 has a slightly different ABI than other (*nix) systems. |
|||
// Therefore, assembly functions need to be tweaked slightly. |
|||
|
|||
.section .text.tinygo_startTask,"ax" |
|||
.global tinygo_startTask |
|||
tinygo_startTask: |
|||
// Small assembly stub for starting a goroutine. This is already run on the |
|||
// new stack, with the callee-saved registers already loaded. |
|||
// Most importantly, r12 contain the pc of the to-be-started function and |
|||
// r13 contain the only argument it is given. Multiple arguments are packed |
|||
// into one by storing them in a new allocation. |
|||
|
|||
// Set the first argument of the goroutine start wrapper, which contains all |
|||
// the arguments. |
|||
movq %r13, %rcx |
|||
|
|||
// Branch to the "goroutine start" function. |
|||
callq *%r12 |
|||
|
|||
// After return, exit this goroutine. This is a tail call. |
|||
jmp tinygo_pause |
|||
|
|||
.global tinygo_swapTask |
|||
.section .text.tinygo_swapTask,"ax" |
|||
tinygo_swapTask: |
|||
// This function gets the following parameters: |
|||
// %rcx = newStack uintptr |
|||
// %rdx = oldStack *uintptr |
|||
|
|||
// Save all callee-saved registers: |
|||
pushq %r15 |
|||
pushq %r14 |
|||
pushq %r13 |
|||
pushq %r12 |
|||
pushq %rsi |
|||
pushq %rdi |
|||
pushq %rbp |
|||
pushq %rbx |
|||
|
|||
// Save the current stack pointer in oldStack. |
|||
movq %rsp, (%rdx) |
|||
|
|||
// Switch to the new stack pointer. |
|||
movq %rcx, %rsp |
|||
|
|||
// Load saved register from the new stack. |
|||
popq %rbx |
|||
popq %rbp |
|||
popq %rdi |
|||
popq %rsi |
|||
popq %r12 |
|||
popq %r13 |
|||
popq %r14 |
|||
popq %r15 |
|||
|
|||
// Return into the new task, as if tinygo_swapTask was a regular call. |
|||
ret |
@ -0,0 +1,66 @@ |
|||
// +build scheduler.tasks,amd64,windows
|
|||
|
|||
package task |
|||
|
|||
// This is almost the same as task_stack_amd64.go, but with the extra rdi and
|
|||
// rsi registers saved: Windows has a slightly different calling convention.
|
|||
|
|||
import "unsafe" |
|||
|
|||
var systemStack uintptr |
|||
|
|||
// calleeSavedRegs is the list of registers that must be saved and restored when
|
|||
// switching between tasks. Also see task_stack_amd64_windows.S that relies on
|
|||
// the exact layout of this struct.
|
|||
type calleeSavedRegs struct { |
|||
rbx uintptr |
|||
rbp uintptr |
|||
rdi uintptr |
|||
rsi uintptr |
|||
r12 uintptr |
|||
r13 uintptr |
|||
r14 uintptr |
|||
r15 uintptr |
|||
|
|||
pc uintptr |
|||
} |
|||
|
|||
// archInit runs architecture-specific setup for the goroutine startup.
|
|||
func (s *state) archInit(r *calleeSavedRegs, fn uintptr, args unsafe.Pointer) { |
|||
// Store the initial sp for the startTask function (implemented in assembly).
|
|||
s.sp = uintptr(unsafe.Pointer(r)) |
|||
|
|||
// Initialize the registers.
|
|||
// These will be popped off of the stack on the first resume of the goroutine.
|
|||
|
|||
// Start the function at tinygo_startTask (defined in
|
|||
// src/internal/task/task_stack_amd64_windows.S). This assembly code calls a
|
|||
// function (passed in r12) with a single argument (passed in r13). After
|
|||
// the function returns, it calls Pause().
|
|||
r.pc = uintptr(unsafe.Pointer(&startTask)) |
|||
|
|||
// Pass the function to call in r12.
|
|||
// This function is a compiler-generated wrapper which loads arguments out
|
|||
// of a struct pointer. See createGoroutineStartWrapper (defined in
|
|||
// compiler/goroutine.go) for more information.
|
|||
r.r12 = fn |
|||
|
|||
// Pass the pointer to the arguments struct in r13.
|
|||
r.r13 = uintptr(args) |
|||
} |
|||
|
|||
func (s *state) resume() { |
|||
swapTask(s.sp, &systemStack) |
|||
} |
|||
|
|||
func (s *state) pause() { |
|||
newStack := systemStack |
|||
systemStack = 0 |
|||
swapTask(newStack, &s.sp) |
|||
} |
|||
|
|||
// SystemStack returns the system stack pointer when called from a task stack.
|
|||
// When called from the system stack, it returns 0.
|
|||
func SystemStack() uintptr { |
|||
return systemStack |
|||
} |
@ -0,0 +1,114 @@ |
|||
// +build !baremetal,!js
|
|||
|
|||
package os |
|||
|
|||
import ( |
|||
"io" |
|||
"syscall" |
|||
) |
|||
|
|||
func init() { |
|||
// Mount the host filesystem at the root directory. This is what most
|
|||
// programs will be expecting.
|
|||
Mount("/", unixFilesystem{}) |
|||
} |
|||
|
|||
// Stdin, Stdout, and Stderr are open Files pointing to the standard input,
|
|||
// standard output, and standard error file descriptors.
|
|||
var ( |
|||
Stdin = &File{unixFileHandle(syscall.Stdin), "/dev/stdin"} |
|||
Stdout = &File{unixFileHandle(syscall.Stdout), "/dev/stdout"} |
|||
Stderr = &File{unixFileHandle(syscall.Stderr), "/dev/stderr"} |
|||
) |
|||
|
|||
// isOS indicates whether we're running on a real operating system with
|
|||
// filesystem support.
|
|||
const isOS = true |
|||
|
|||
// unixFilesystem is an empty handle for a Unix/Linux filesystem. All operations
|
|||
// are relative to the current working directory.
|
|||
type unixFilesystem struct { |
|||
} |
|||
|
|||
func (fs unixFilesystem) Mkdir(path string, perm FileMode) error { |
|||
return handleSyscallError(syscall.Mkdir(path, uint32(perm))) |
|||
} |
|||
|
|||
func (fs unixFilesystem) Remove(path string) error { |
|||
return handleSyscallError(syscall.Unlink(path)) |
|||
} |
|||
|
|||
func (fs unixFilesystem) OpenFile(path string, flag int, perm FileMode) (FileHandle, error) { |
|||
// Map os package flags to syscall flags.
|
|||
syscallFlag := 0 |
|||
if flag&O_RDONLY != 0 { |
|||
syscallFlag |= syscall.O_RDONLY |
|||
} |
|||
if flag&O_WRONLY != 0 { |
|||
syscallFlag |= syscall.O_WRONLY |
|||
} |
|||
if flag&O_RDWR != 0 { |
|||
syscallFlag |= syscall.O_RDWR |
|||
} |
|||
if flag&O_APPEND != 0 { |
|||
syscallFlag |= syscall.O_APPEND |
|||
} |
|||
if flag&O_CREATE != 0 { |
|||
syscallFlag |= syscall.O_CREAT |
|||
} |
|||
if flag&O_EXCL != 0 { |
|||
syscallFlag |= syscall.O_EXCL |
|||
} |
|||
if flag&O_SYNC != 0 { |
|||
syscallFlag |= syscall.O_SYNC |
|||
} |
|||
if flag&O_TRUNC != 0 { |
|||
syscallFlag |= syscall.O_TRUNC |
|||
} |
|||
fp, err := syscall.Open(path, syscallFlag, uint32(perm)) |
|||
return unixFileHandle(fp), handleSyscallError(err) |
|||
} |
|||
|
|||
// unixFileHandle is a Unix file pointer with associated methods that implement
|
|||
// the FileHandle interface.
|
|||
type unixFileHandle uintptr |
|||
|
|||
// Read reads up to len(b) bytes from the File. It returns the number of bytes
|
|||
// read and any error encountered. At end of file, Read returns 0, io.EOF.
|
|||
func (f unixFileHandle) Read(b []byte) (n int, err error) { |
|||
n, err = syscall.Read(syscallFd(f), b) |
|||
err = handleSyscallError(err) |
|||
if n == 0 && err == nil { |
|||
err = io.EOF |
|||
} |
|||
return |
|||
} |
|||
|
|||
// Write writes len(b) bytes to the File. It returns the number of bytes written
|
|||
// and an error, if any. Write returns a non-nil error when n != len(b).
|
|||
func (f unixFileHandle) Write(b []byte) (n int, err error) { |
|||
n, err = syscall.Write(syscallFd(f), b) |
|||
err = handleSyscallError(err) |
|||
return |
|||
} |
|||
|
|||
// Close closes the File, rendering it unusable for I/O.
|
|||
func (f unixFileHandle) Close() error { |
|||
return handleSyscallError(syscall.Close(syscallFd(f))) |
|||
} |
|||
|
|||
// handleSyscallError converts syscall errors into regular os package errors.
|
|||
// The err parameter must be either nil or of type syscall.Errno.
|
|||
func handleSyscallError(err error) error { |
|||
if err == nil { |
|||
return nil |
|||
} |
|||
switch err.(syscall.Errno) { |
|||
case syscall.EEXIST: |
|||
return ErrExist |
|||
case syscall.ENOENT: |
|||
return ErrNotExist |
|||
default: |
|||
return err |
|||
} |
|||
} |
@ -0,0 +1,7 @@ |
|||
// +build windows
|
|||
|
|||
package os |
|||
|
|||
import "syscall" |
|||
|
|||
type syscallFd = syscall.Handle |
@ -0,0 +1,22 @@ |
|||
.section .text.tinygo_scanCurrentStack,"ax" |
|||
.global tinygo_scanCurrentStack |
|||
tinygo_scanCurrentStack: |
|||
// Save callee-saved registers. |
|||
pushq %rbx |
|||
pushq %rbp |
|||
pushq %rdi |
|||
pushq %rsi |
|||
pushq %r12 |
|||
pushq %r13 |
|||
pushq %r14 |
|||
pushq %r15 |
|||
|
|||
// Scan the stack. |
|||
subq $8, %rsp // adjust the stack before the call to maintain 16-byte alignment |
|||
movq %rsp, %rdi |
|||
callq tinygo_scanstack |
|||
|
|||
// Restore the stack pointer. Registers do not need to be restored as they |
|||
// were only pushed to be discoverable by the GC. |
|||
addq $72, %rsp |
|||
retq |
@ -0,0 +1,3 @@ |
|||
package runtime |
|||
|
|||
const GOOS = "windows" |
@ -0,0 +1,230 @@ |
|||
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 |
|||
|
|||
func postinit() {} |
|||
|
|||
//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.Pointer(uintptr(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() { |
|||
} |
Loading…
Reference in new issue