Skip to content

Commit f1d1c59

Browse files
committed
windows: use MSVCRT.DLL instead of UCRT on i386
This allows the binaries to run on Windows XP, without needing any extra DLLs. Tested in an x86 Windows XP SP3 virtual machine.
1 parent 7252c3a commit f1d1c59

File tree

8 files changed

+196
-75
lines changed

8 files changed

+196
-75
lines changed

Diff for: GNUmakefile

+10-2
Original file line numberDiff line numberDiff line change
@@ -940,8 +940,9 @@ build/release: tinygo gen-device $(if $(filter 1,$(USE_SYSTEM_BINARYEN)),,binary
940940
@mkdir -p build/release/tinygo/lib/clang/include
941941
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
942942
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk
943+
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/crt
944+
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/math
943945
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
944-
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/stdio
945946
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
946947
@mkdir -p build/release/tinygo/lib/musl/arch
947948
@mkdir -p build/release/tinygo/lib/musl/crt
@@ -996,10 +997,17 @@ endif
996997
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
997998
@cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src
998999
@cp -rp lib/musl/src/process build/release/tinygo/lib/musl/src
1000+
@cp -rp lib/mingw-w64/mingw-w64-crt/crt/pseudo-reloc.c build/release/tinygo/lib/mingw-w64/mingw-w64-crt/crt
9991001
@cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
1002+
@cp -rp lib/mingw-w64/mingw-w64-crt/gdtoa build/release/tinygo/lib/mingw-w64/mingw-w64-crt
1003+
@cp -rp lib/mingw-w64/mingw-w64-crt/include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
10001004
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/api-ms-win-crt-* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
1005+
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/advapi32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
10011006
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
1002-
@cp -rp lib/mingw-w64/mingw-w64-crt/stdio/ucrt_* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/stdio
1007+
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/msvcrt.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
1008+
@cp -rp lib/mingw-w64/mingw-w64-crt/math/x86 build/release/tinygo/lib/mingw-w64/mingw-w64-crt/math
1009+
@cp -rp lib/mingw-w64/mingw-w64-crt/misc build/release/tinygo/lib/mingw-w64/mingw-w64-crt
1010+
@cp -rp lib/mingw-w64/mingw-w64-crt/stdio build/release/tinygo/lib/mingw-w64/mingw-w64-crt
10031011
@cp -rp lib/mingw-w64/mingw-w64-headers/crt/ build/release/tinygo/lib/mingw-w64/mingw-w64-headers
10041012
@cp -rp lib/mingw-w64/mingw-w64-headers/defaults/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
10051013
@cp -rp lib/mingw-w64/mingw-w64-headers/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers

Diff for: builder/builtins.go

+10
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package builder
33
import (
44
"os"
55
"path/filepath"
6+
"strings"
67

78
"github.com/tinygo-org/tinygo/compileopts"
89
"github.com/tinygo-org/tinygo/goenv"
@@ -201,6 +202,11 @@ var avrBuiltins = []string{
201202
"avr/udivmodqi4.S",
202203
}
203204

205+
// Builtins needed specifically for windows/386.
206+
var windowsI386Builtins = []string{
207+
"i386/chkstk.S", // also _alloca
208+
}
209+
204210
// libCompilerRT is a library with symbols required by programs compiled with
205211
// LLVM. These symbols are for operations that cannot be emitted with a single
206212
// instruction or a short sequence of instructions for that target.
@@ -229,6 +235,10 @@ var libCompilerRT = Library{
229235
builtins = append(builtins, avrBuiltins...)
230236
case "x86_64", "aarch64", "riscv64": // any 64-bit arch
231237
builtins = append(builtins, genericBuiltins128...)
238+
case "i386":
239+
if strings.Split(target, "-")[2] == "windows" {
240+
builtins = append(builtins, windowsI386Builtins...)
241+
}
232242
}
233243
return builtins, nil
234244
},

Diff for: builder/mingw-w64.go

+83-32
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,62 @@ var libMinGW = Library{
3030
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64") },
3131
cflags: func(target, headerPath string) []string {
3232
mingwDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64")
33-
return []string{
33+
flags := []string{
3434
"-nostdlibinc",
35+
"-isystem", mingwDir + "/mingw-w64-crt/include",
3536
"-isystem", mingwDir + "/mingw-w64-headers/crt",
37+
"-isystem", mingwDir + "/mingw-w64-headers/include",
3638
"-I", mingwDir + "/mingw-w64-headers/defaults/include",
3739
"-I" + headerPath,
3840
}
41+
if strings.Split(target, "-")[0] == "i386" {
42+
flags = append(flags,
43+
"-D__MSVCRT_VERSION__=0x700", // Microsoft Visual C++ .NET 2002
44+
"-D_WIN32_WINNT=0x0501", // target Windows XP
45+
"-D_CRTBLD",
46+
"-Wno-pragma-pack",
47+
)
48+
}
49+
return flags
3950
},
4051
librarySources: func(target string) ([]string, error) {
4152
// These files are needed so that printf and the like are supported.
42-
sources := []string{
43-
"mingw-w64-crt/stdio/ucrt_fprintf.c",
44-
"mingw-w64-crt/stdio/ucrt_fwprintf.c",
45-
"mingw-w64-crt/stdio/ucrt_printf.c",
46-
"mingw-w64-crt/stdio/ucrt_snprintf.c",
47-
"mingw-w64-crt/stdio/ucrt_sprintf.c",
48-
"mingw-w64-crt/stdio/ucrt_vfprintf.c",
49-
"mingw-w64-crt/stdio/ucrt_vprintf.c",
50-
"mingw-w64-crt/stdio/ucrt_vsnprintf.c",
51-
"mingw-w64-crt/stdio/ucrt_vsprintf.c",
53+
var sources []string
54+
if strings.Split(target, "-")[0] == "i386" {
55+
// Old 32-bit x86 systems use msvcrt.dll.
56+
sources = []string{
57+
"mingw-w64-crt/crt/pseudo-reloc.c",
58+
"mingw-w64-crt/gdtoa/dmisc.c",
59+
"mingw-w64-crt/gdtoa/gdtoa.c",
60+
"mingw-w64-crt/gdtoa/gmisc.c",
61+
"mingw-w64-crt/gdtoa/misc.c",
62+
"mingw-w64-crt/math/x86/exp2.S",
63+
"mingw-w64-crt/math/x86/trunc.S",
64+
"mingw-w64-crt/misc/___mb_cur_max_func.c",
65+
"mingw-w64-crt/misc/lc_locale_func.c",
66+
"mingw-w64-crt/misc/mbrtowc.c",
67+
"mingw-w64-crt/misc/strnlen.c",
68+
"mingw-w64-crt/misc/wcrtomb.c",
69+
"mingw-w64-crt/misc/wcsnlen.c",
70+
"mingw-w64-crt/stdio/acrt_iob_func.c",
71+
"mingw-w64-crt/stdio/mingw_lock.c",
72+
"mingw-w64-crt/stdio/mingw_pformat.c",
73+
"mingw-w64-crt/stdio/mingw_vfprintf.c",
74+
"mingw-w64-crt/stdio/mingw_vsnprintf.c",
75+
}
76+
} else {
77+
// Anything somewhat modern (amd64, arm64) uses UCRT.
78+
sources = []string{
79+
"mingw-w64-crt/stdio/ucrt_fprintf.c",
80+
"mingw-w64-crt/stdio/ucrt_fwprintf.c",
81+
"mingw-w64-crt/stdio/ucrt_printf.c",
82+
"mingw-w64-crt/stdio/ucrt_snprintf.c",
83+
"mingw-w64-crt/stdio/ucrt_sprintf.c",
84+
"mingw-w64-crt/stdio/ucrt_vfprintf.c",
85+
"mingw-w64-crt/stdio/ucrt_vprintf.c",
86+
"mingw-w64-crt/stdio/ucrt_vsnprintf.c",
87+
"mingw-w64-crt/stdio/ucrt_vsprintf.c",
88+
}
5289
}
5390
return sources, nil
5491
},
@@ -63,27 +100,41 @@ var libMinGW = Library{
63100
func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
64101
var jobs []*compileJob
65102
root := goenv.Get("TINYGOROOT")
66-
// Normally all the api-ms-win-crt-*.def files are all compiled to a single
67-
// .lib file. But to simplify things, we're going to leave them as separate
68-
// files.
69-
for _, name := range []string{
70-
"kernel32.def.in",
71-
"api-ms-win-crt-conio-l1-1-0.def",
72-
"api-ms-win-crt-convert-l1-1-0.def.in",
73-
"api-ms-win-crt-environment-l1-1-0.def",
74-
"api-ms-win-crt-filesystem-l1-1-0.def",
75-
"api-ms-win-crt-heap-l1-1-0.def",
76-
"api-ms-win-crt-locale-l1-1-0.def",
77-
"api-ms-win-crt-math-l1-1-0.def.in",
78-
"api-ms-win-crt-multibyte-l1-1-0.def",
79-
"api-ms-win-crt-private-l1-1-0.def.in",
80-
"api-ms-win-crt-process-l1-1-0.def",
81-
"api-ms-win-crt-runtime-l1-1-0.def.in",
82-
"api-ms-win-crt-stdio-l1-1-0.def",
83-
"api-ms-win-crt-string-l1-1-0.def",
84-
"api-ms-win-crt-time-l1-1-0.def",
85-
"api-ms-win-crt-utility-l1-1-0.def",
86-
} {
103+
var libs []string
104+
if goarch == "386" {
105+
libs = []string{
106+
// x86 uses msvcrt.dll instead of UCRT for compatibility with old
107+
// Windows versions.
108+
"advapi32.def.in",
109+
"kernel32.def.in",
110+
"msvcrt.def.in",
111+
}
112+
} else {
113+
// Use the modernized UCRT on new systems.
114+
// Normally all the api-ms-win-crt-*.def files are all compiled to a
115+
// single .lib file. But to simplify things, we're going to leave them
116+
// as separate files.
117+
libs = []string{
118+
"advapi32.def.in",
119+
"kernel32.def.in",
120+
"api-ms-win-crt-conio-l1-1-0.def",
121+
"api-ms-win-crt-convert-l1-1-0.def.in",
122+
"api-ms-win-crt-environment-l1-1-0.def",
123+
"api-ms-win-crt-filesystem-l1-1-0.def",
124+
"api-ms-win-crt-heap-l1-1-0.def",
125+
"api-ms-win-crt-locale-l1-1-0.def",
126+
"api-ms-win-crt-math-l1-1-0.def.in",
127+
"api-ms-win-crt-multibyte-l1-1-0.def",
128+
"api-ms-win-crt-private-l1-1-0.def.in",
129+
"api-ms-win-crt-process-l1-1-0.def",
130+
"api-ms-win-crt-runtime-l1-1-0.def.in",
131+
"api-ms-win-crt-stdio-l1-1-0.def",
132+
"api-ms-win-crt-string-l1-1-0.def",
133+
"api-ms-win-crt-time-l1-1-0.def",
134+
"api-ms-win-crt-utility-l1-1-0.def",
135+
}
136+
}
137+
for _, name := range libs {
87138
outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib")
88139
inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name)
89140
job := &compileJob{

Diff for: compileopts/config.go

+13-3
Original file line numberDiff line numberDiff line change
@@ -382,15 +382,25 @@ func (c *Config) LibcCFlags() []string {
382382
case "mingw-w64":
383383
root := goenv.Get("TINYGOROOT")
384384
path := c.LibcPath("mingw-w64")
385-
return []string{
385+
cflags := []string{
386386
"-nostdlibinc",
387387
"-isystem", filepath.Join(path, "include"),
388388
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
389389
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "include"),
390390
"-isystem", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
391-
"-D_UCRT",
392-
"-D_WIN32_WINNT=0x0a00", // target Windows 10
393391
}
392+
if c.GOARCH() == "386" {
393+
cflags = append(cflags,
394+
"-D__MSVCRT_VERSION__=0x700", // Microsoft Visual C++ .NET 2002
395+
"-D_WIN32_WINNT=0x0501", // target Windows XP
396+
)
397+
} else {
398+
cflags = append(cflags,
399+
"-D_UCRT",
400+
"-D_WIN32_WINNT=0x0a00", // target Windows 10
401+
)
402+
}
403+
return cflags
394404
case "":
395405
// No libc specified, nothing to add.
396406
return nil

Diff for: compileopts/target.go

+2
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,8 @@ func defaultTarget(options *Options) (*TargetSpec, error) {
434434
case "386":
435435
spec.LDFlags = append(spec.LDFlags,
436436
"-m", "i386pe",
437+
"--major-os-version", "4",
438+
"--major-subsystem-version", "4",
437439
)
438440
// __udivdi3 is not present in ucrt it seems.
439441
spec.RTLib = "compiler-rt"

Diff for: compiler/symbol.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ func (c *compilerContext) getFunction(fn *ssa.Function) (llvm.Type, llvm.Value)
208208
// > circumstances, and should not be exposed to source languages.
209209
llvmutil.AppendToGlobal(c.mod, "llvm.compiler.used", llvmFn)
210210
}
211-
case "GetModuleHandleExA", "GetProcAddress", "GetSystemInfo", "GetSystemTimeAsFileTime", "LoadLibraryExW", "QueryUnbiasedInterruptTime", "SetEnvironmentVariableA", "Sleep", "VirtualAlloc":
211+
case "GetModuleHandleExA", "GetProcAddress", "GetSystemInfo", "GetSystemTimeAsFileTime", "LoadLibraryExW", "QueryPerformanceCounter", "QueryPerformanceFrequency", "QueryUnbiasedInterruptTime", "SetEnvironmentVariableA", "Sleep", "SystemFunction036", "VirtualAlloc":
212212
// On Windows we need to use a special calling convention for some
213213
// external calls.
214214
if c.GOOS == "windows" && c.GOARCH == "386" {

Diff for: src/crypto/rand/rand_windows.go

+18-21
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package rand
22

3-
import "errors"
3+
import (
4+
"errors"
5+
"unsafe"
6+
)
47

58
func init() {
69
Reader = &reader{}
@@ -16,28 +19,22 @@ func (r *reader) Read(b []byte) (n int, err error) {
1619
return
1720
}
1821

19-
var randomByte uint32
20-
for i := range b {
21-
// Call rand_s every four bytes because it's a C int (always 32-bit in
22-
// Windows).
23-
if i%4 == 0 {
24-
errCode := libc_rand_s(&randomByte)
25-
if errCode != 0 {
26-
// According to the documentation, it can return an error.
27-
return n, errRandom
28-
}
29-
} else {
30-
randomByte >>= 8
31-
}
32-
b[i] = byte(randomByte)
22+
// Use the old RtlGenRandom, introduced in Windows XP.
23+
// Even though the documentation says it is deprecated, it is widely used
24+
// and probably won't go away anytime soon.
25+
// See for example: https://github.com/golang/go/issues/33542
26+
// For Windows 7 and newer, we might switch to ProcessPrng in the future
27+
// (which is a documented function and might be a tiny bit faster).
28+
ok := libc_RtlGenRandom(unsafe.Pointer(&b[0]), len(b))
29+
if !ok {
30+
return 0, errRandom
3331
}
34-
3532
return len(b), nil
3633
}
3734

38-
// Cryptographically secure random number generator.
39-
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rand-s?view=msvc-170
40-
// errno_t rand_s(unsigned int* randomValue);
35+
// This function is part of advapi32.dll, and is called SystemFunction036 for
36+
// some reason. It's available on Windows XP and newer.
37+
// See: https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
4138
//
42-
//export rand_s
43-
func libc_rand_s(randomValue *uint32) int32
39+
//export SystemFunction036
40+
func libc_RtlGenRandom(buf unsafe.Pointer, len int) bool

0 commit comments

Comments
 (0)