Skip to content

Commit bb2bfe5

Browse files
committed
windows: use MSVCRT.DLL instead of UCRT on i386
This hopefully allows the binaries to run on Windows XP. I don't have a Windows XP system handy so can't test.
1 parent 7252c3a commit bb2bfe5

File tree

6 files changed

+153
-70
lines changed

6 files changed

+153
-70
lines changed

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
},

builder/mingw-w64.go

+82-32
Original file line numberDiff line numberDiff line change
@@ -30,25 +30,61 @@ 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/stdio/acrt_iob_func.c",
70+
"mingw-w64-crt/stdio/mingw_lock.c",
71+
"mingw-w64-crt/stdio/mingw_pformat.c",
72+
"mingw-w64-crt/stdio/mingw_vfprintf.c",
73+
"mingw-w64-crt/stdio/mingw_vsnprintf.c",
74+
}
75+
} else {
76+
// Anything somewhat modern (amd64, arm64) uses UCRT.
77+
sources = []string{
78+
"mingw-w64-crt/stdio/ucrt_fprintf.c",
79+
"mingw-w64-crt/stdio/ucrt_fwprintf.c",
80+
"mingw-w64-crt/stdio/ucrt_printf.c",
81+
"mingw-w64-crt/stdio/ucrt_snprintf.c",
82+
"mingw-w64-crt/stdio/ucrt_sprintf.c",
83+
"mingw-w64-crt/stdio/ucrt_vfprintf.c",
84+
"mingw-w64-crt/stdio/ucrt_vprintf.c",
85+
"mingw-w64-crt/stdio/ucrt_vsnprintf.c",
86+
"mingw-w64-crt/stdio/ucrt_vsprintf.c",
87+
}
5288
}
5389
return sources, nil
5490
},
@@ -63,27 +99,41 @@ var libMinGW = Library{
6399
func makeMinGWExtraLibs(tmpdir, goarch string) []*compileJob {
64100
var jobs []*compileJob
65101
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-
} {
102+
var libs []string
103+
if goarch == "386" {
104+
libs = []string{
105+
// x86 uses msvcrt.dll instead of UCRT for compatibility with old
106+
// Windows versions.
107+
"advapi32.def.in",
108+
"kernel32.def.in",
109+
"msvcrt.def.in",
110+
}
111+
} else {
112+
// Use the modernized UCRT on new systems.
113+
// Normally all the api-ms-win-crt-*.def files are all compiled to a
114+
// single .lib file. But to simplify things, we're going to leave them
115+
// as separate files.
116+
libs = []string{
117+
"advapi32.def.in",
118+
"kernel32.def.in",
119+
"api-ms-win-crt-conio-l1-1-0.def",
120+
"api-ms-win-crt-convert-l1-1-0.def.in",
121+
"api-ms-win-crt-environment-l1-1-0.def",
122+
"api-ms-win-crt-filesystem-l1-1-0.def",
123+
"api-ms-win-crt-heap-l1-1-0.def",
124+
"api-ms-win-crt-locale-l1-1-0.def",
125+
"api-ms-win-crt-math-l1-1-0.def.in",
126+
"api-ms-win-crt-multibyte-l1-1-0.def",
127+
"api-ms-win-crt-private-l1-1-0.def.in",
128+
"api-ms-win-crt-process-l1-1-0.def",
129+
"api-ms-win-crt-runtime-l1-1-0.def.in",
130+
"api-ms-win-crt-stdio-l1-1-0.def",
131+
"api-ms-win-crt-string-l1-1-0.def",
132+
"api-ms-win-crt-time-l1-1-0.def",
133+
"api-ms-win-crt-utility-l1-1-0.def",
134+
}
135+
}
136+
for _, name := range libs {
87137
outpath := filepath.Join(tmpdir, filepath.Base(name)+".lib")
88138
inpath := filepath.Join(root, "lib/mingw-w64/mingw-w64-crt/lib-common/"+name)
89139
job := &compileJob{

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

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", "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" {

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

src/runtime/runtime_windows.go

+29-13
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ func __p___argc() *int32
4242
//export __p___argv
4343
func __p___argv() **unsafe.Pointer
4444

45+
type startupInfo struct {
46+
newMode int32
47+
}
48+
49+
//export __getmainargs
50+
func __getmainargs(argc *int32, argv, env **unsafe.Pointer, doWildcard int, startInfo *startupInfo) int32
51+
4552
//export mainCRTStartup
4653
func mainCRTStartup() int {
4754
preinit()
@@ -78,9 +85,19 @@ var args []string
7885
func os_runtime_args() []string {
7986
if args == nil {
8087
// Obtain argc/argv from the environment.
81-
_configure_narrow_argv(2)
82-
argc := *__p___argc()
83-
argv := *__p___argv()
88+
var argc int32
89+
var argv *unsafe.Pointer
90+
if GOARCH == "386" {
91+
// MSVCRT.DLL
92+
var env *unsafe.Pointer
93+
startInfo := startupInfo{newMode: 0}
94+
__getmainargs(&argc, &argv, &env, 1, &startInfo)
95+
} else {
96+
// UCRT
97+
_configure_narrow_argv(2)
98+
argc = *__p___argc()
99+
argv = *__p___argv()
100+
}
84101

85102
// Make args slice big enough so that it can store all command line
86103
// arguments.
@@ -239,17 +256,16 @@ func procUnpin() {
239256
}
240257

241258
func hardwareRand() (n uint64, ok bool) {
242-
var n1, n2 uint32
243-
errCode1 := libc_rand_s(&n1)
244-
errCode2 := libc_rand_s(&n2)
245-
n = uint64(n1)<<32 | uint64(n2)
246-
ok = errCode1 == 0 && errCode2 == 0
259+
// Use the old RtlGenRandom, introduced in Windows XP.
260+
// See the rationale in src/crypto/rand/rand_windows.go for why we use this
261+
// one.
262+
ok = RtlGenRandom(unsafe.Pointer(&n), 8)
247263
return
248264
}
249265

250-
// Cryptographically secure random number generator.
251-
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rand-s?view=msvc-170
252-
// errno_t rand_s(unsigned int* randomValue);
266+
// This function is part of advapi32.dll, and is called SystemFunction036 for
267+
// some reason. It's available on Windows XP and newer.
268+
// See: https://learn.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom
253269
//
254-
//export rand_s
255-
func libc_rand_s(randomValue *uint32) int32
270+
//export SystemFunction036
271+
func RtlGenRandom(buf unsafe.Pointer, len int) bool

0 commit comments

Comments
 (0)