From bd7594a0ffdddeb41f868996e746ccb3f47468ca Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Mon, 7 Apr 2025 15:09:32 +0200 Subject: [PATCH] WIP try to use MSVCRT.DLL instead of UCRT Very simple "hello world" style programs work. --- builder/builtins.go | 5 ++ builder/mingw-w64.go | 106 ++++++++++++++++++++++---------- compileopts/config.go | 16 ++++- src/crypto/rand/rand_windows.go | 1 + src/runtime/runtime_windows.go | 23 +++++-- 5 files changed, 112 insertions(+), 39 deletions(-) diff --git a/builder/builtins.go b/builder/builtins.go index b493b6680..67cbd9c33 100644 --- a/builder/builtins.go +++ b/builder/builtins.go @@ -3,6 +3,7 @@ package builder import ( "os" "path/filepath" + "strings" "github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/goenv" @@ -229,6 +230,10 @@ var libCompilerRT = Library{ builtins = append(builtins, avrBuiltins...) case "x86_64", "aarch64", "riscv64": // any 64-bit arch builtins = append(builtins, genericBuiltins128...) + case "i386": + if strings.Split(target, "-")[2] == "windows" { + builtins = append(builtins, "i386/chkstk.S") + } } return builtins, nil }, diff --git a/builder/mingw-w64.go b/builder/mingw-w64.go index 6ea3560f7..a4c91e479 100644 --- a/builder/mingw-w64.go +++ b/builder/mingw-w64.go @@ -32,23 +32,54 @@ var libMinGW = Library{ mingwDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64") return []string{ "-nostdlibinc", + "-isystem", mingwDir + "/mingw-w64-crt/include", "-isystem", mingwDir + "/mingw-w64-headers/crt", + "-isystem", mingwDir + "/mingw-w64-headers/include", "-I", mingwDir + "/mingw-w64-headers/defaults/include", "-I" + headerPath, + "-D__MSVCRT_VERSION__=0x700", // Microsoft Visual C++ .NET 2002 + "-D_WIN32_WINNT=0x0501", // target Windows XP + "-D__LIBMSVCRT_OS__", + "-D_CRTBLD", + "-Wno-pragma-pack", } }, librarySources: func(target string) ([]string, error) { // These files are needed so that printf and the like are supported. - sources := []string{ - "mingw-w64-crt/stdio/ucrt_fprintf.c", - "mingw-w64-crt/stdio/ucrt_fwprintf.c", - "mingw-w64-crt/stdio/ucrt_printf.c", - "mingw-w64-crt/stdio/ucrt_snprintf.c", - "mingw-w64-crt/stdio/ucrt_sprintf.c", - "mingw-w64-crt/stdio/ucrt_vfprintf.c", - "mingw-w64-crt/stdio/ucrt_vprintf.c", - "mingw-w64-crt/stdio/ucrt_vsnprintf.c", - "mingw-w64-crt/stdio/ucrt_vsprintf.c", + var sources []string + if strings.Split(target, "-")[0] == "i386" { + // Old 32-bit x86 systems use msvcrt.dll. + sources = []string{ + "mingw-w64-crt/crt/pseudo-reloc.c", + "mingw-w64-crt/gdtoa/dmisc.c", + "mingw-w64-crt/gdtoa/gdtoa.c", + "mingw-w64-crt/gdtoa/gmisc.c", + "mingw-w64-crt/gdtoa/misc.c", + "mingw-w64-crt/misc/___mb_cur_max_func.c", + "mingw-w64-crt/misc/lc_locale_func.c", + "mingw-w64-crt/misc/mbrtowc.c", + "mingw-w64-crt/misc/strnlen.c", + "mingw-w64-crt/misc/wcrtomb.c", + "mingw-w64-crt/secapi/rand_s.c", + "mingw-w64-crt/stdio/acrt_iob_func.c", + "mingw-w64-crt/stdio/mingw_lock.c", + "mingw-w64-crt/stdio/mingw_pformat.c", + "mingw-w64-crt/stdio/mingw_vfprintf.c", + "mingw-w64-crt/stdio/mingw_vsnprintf.c", + } + } else { + // Anything somewhat modern (amd64, arm64) uses UCRT. + sources = []string{ + "mingw-w64-crt/stdio/ucrt_fprintf.c", + "mingw-w64-crt/stdio/ucrt_fwprintf.c", + "mingw-w64-crt/stdio/ucrt_printf.c", + "mingw-w64-crt/stdio/ucrt_snprintf.c", + "mingw-w64-crt/stdio/ucrt_sprintf.c", + "mingw-w64-crt/stdio/ucrt_vfprintf.c", + "mingw-w64-crt/stdio/ucrt_vprintf.c", + "mingw-w64-crt/stdio/ucrt_vsnprintf.c", + "mingw-w64-crt/stdio/ucrt_vsprintf.c", + } } return sources, nil }, @@ -63,27 +94,40 @@ var libMinGW = Library{ func makeMinGWExtraLibs(tmpdir, goarch 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.in", - "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", - } { + var libs []string + if goarch == "386" { + libs = []string{ + // x86 uses msvcrt.dll instead of UCRT for compatibility with old + // Windows versions. + "advapi32.def.in", + "kernel32.def.in", + "msvcrt.def.in", + } + } else { + // Use the modernized UCRT on new systems. + // 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. + libs = []string{ + "kernel32.def.in", + "api-ms-win-crt-conio-l1-1-0.def", + "api-ms-win-crt-convert-l1-1-0.def.in", + "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", + } + } + for _, name := range libs { outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib") inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name) job := &compileJob{ diff --git a/compileopts/config.go b/compileopts/config.go index 3d8a73627..d368503ee 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -391,15 +391,25 @@ func (c *Config) LibcCFlags() []string { case "mingw-w64": root := goenv.Get("TINYGOROOT") path, _ := c.LibcPath("mingw-w64") - return []string{ + cflags := []string{ "-nostdlibinc", "-isystem", filepath.Join(path, "include"), "-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"), "-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "include"), "-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"), - "-D_UCRT", - "-D_WIN32_WINNT=0x0a00", // target Windows 10 } + if c.GOARCH() == "386" { + cflags = append(cflags, + "-D__MSVCRT_VERSION__=0x700", // Microsoft Visual C++ .NET 2002 + "-D_WIN32_WINNT=0x0501", // target Windows XP + ) + } else { + cflags = append(cflags, + "-D_UCRT", + "-D_WIN32_WINNT=0x0a00", // target Windows 10 + ) + } + return cflags case "": // No libc specified, nothing to add. return nil diff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go index 16266198f..1426db1f4 100644 --- a/src/crypto/rand/rand_windows.go +++ b/src/crypto/rand/rand_windows.go @@ -21,6 +21,7 @@ func (r *reader) Read(b []byte) (n int, err error) { // Call rand_s every four bytes because it's a C int (always 32-bit in // Windows). if i%4 == 0 { + // TODO: use RtlGenRandom on GOARCH=386. errCode := libc_rand_s(&randomByte) if errCode != 0 { // According to the documentation, it can return an error. diff --git a/src/runtime/runtime_windows.go b/src/runtime/runtime_windows.go index 88857fc3a..b2fbdf339 100644 --- a/src/runtime/runtime_windows.go +++ b/src/runtime/runtime_windows.go @@ -239,11 +239,17 @@ 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 + if GOARCH == "386" { + // Use the old RtlGenRandom, introduced in Windows XP. + ok = RtlGenRandom(unsafe.Pointer(&n), 8) + } else { + // Use the newer secure rand_s function that's part of UCRT. + 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 } @@ -253,3 +259,10 @@ func hardwareRand() (n uint64, ok bool) { // //export rand_s func libc_rand_s(randomValue *uint32) int32 + +// This function is part of advapi32.dll, and is called SystemFunction036 for +// some reason. It's available on Windows XP and newer. +// See: https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom +// +//export SystemFunction036 +func RtlGenRandom(buf unsafe.Pointer, len int) bool