all: add support for windows/amd64
This uses Mingw-w64, which seems to be the de facto standard for porting Unixy programs to Windows.
Этот коммит содержится в:
родитель
41bcad9c19
коммит
869e917dc6
26 изменённых файлов: 841 добавлений и 161 удалений
2
.github/workflows/windows.yml
предоставленный
2
.github/workflows/windows.yml
предоставленный
|
@ -99,3 +99,5 @@ jobs:
|
||||||
- name: Smoke tests
|
- name: Smoke tests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: make smoketest TINYGO=build/tinygo AVR=0 XTENSA=0
|
run: make smoketest TINYGO=build/tinygo AVR=0 XTENSA=0
|
||||||
|
- name: Test stdlib packages
|
||||||
|
run: make tinygo-test
|
||||||
|
|
3
.gitmodules
предоставленный
3
.gitmodules
предоставленный
|
@ -29,3 +29,6 @@
|
||||||
[submodule "lib/binaryen"]
|
[submodule "lib/binaryen"]
|
||||||
path = lib/binaryen
|
path = lib/binaryen
|
||||||
url = https://github.com/WebAssembly/binaryen.git
|
url = https://github.com/WebAssembly/binaryen.git
|
||||||
|
[submodule "lib/mingw-w64"]
|
||||||
|
path = lib/mingw-w64
|
||||||
|
url = https://github.com/mingw-w64/mingw-w64.git
|
||||||
|
|
12
Makefile
12
Makefile
|
@ -38,7 +38,7 @@ endif
|
||||||
|
|
||||||
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
.PHONY: all tinygo test $(LLVM_BUILDDIR) llvm-source clean fmt gen-device gen-device-nrf gen-device-nxp gen-device-avr gen-device-rp
|
||||||
|
|
||||||
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf executionengine frontendopenmp instrumentation interpreter ipo irreader linker lto mc mcjit objcarcopts option profiledata scalaropts support target
|
LLVM_COMPONENTS = all-targets analysis asmparser asmprinter bitreader bitwriter codegen core coroutines coverage debuginfodwarf debuginfopdb executionengine frontendopenmp instrumentation interpreter ipo irreader libdriver linker lto mc mcjit objcarcopts option profiledata scalaropts support target windowsmanifest
|
||||||
|
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
EXE = .exe
|
EXE = .exe
|
||||||
|
@ -477,7 +477,10 @@ endif
|
||||||
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
$(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo
|
||||||
|
GOOS=windows GOARCH=amd64 $(TINYGO) build -o test.exe ./testdata/cgo
|
||||||
ifneq ($(OS),Windows_NT)
|
ifneq ($(OS),Windows_NT)
|
||||||
|
# TODO: this does not yet work on Windows. Somehow, unused functions are
|
||||||
|
# not garbage collected.
|
||||||
$(TINYGO) build -o test.elf -gc=leaking -scheduler=none examples/serial
|
$(TINYGO) build -o test.elf -gc=leaking -scheduler=none examples/serial
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -490,6 +493,8 @@ build/release: tinygo gen-device wasi-libc binaryen
|
||||||
@mkdir -p build/release/tinygo/lib/clang/include
|
@mkdir -p build/release/tinygo/lib/clang/include
|
||||||
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
|
||||||
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
|
||||||
|
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||||
|
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
|
||||||
@mkdir -p build/release/tinygo/lib/musl/arch
|
@mkdir -p build/release/tinygo/lib/musl/arch
|
||||||
@mkdir -p build/release/tinygo/lib/musl/crt
|
@mkdir -p build/release/tinygo/lib/musl/crt
|
||||||
@mkdir -p build/release/tinygo/lib/musl/src
|
@mkdir -p build/release/tinygo/lib/musl/src
|
||||||
|
@ -530,6 +535,11 @@ build/release: tinygo gen-device wasi-libc binaryen
|
||||||
@cp -rp lib/musl/src/thread build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/thread build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/time build/release/tinygo/lib/musl/src
|
||||||
@cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src
|
@cp -rp lib/musl/src/unistd build/release/tinygo/lib/musl/src
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
|
||||||
|
@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
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-headers/crt/ build/release/tinygo/lib/mingw-w64/mingw-w64-headers
|
||||||
|
@cp -rp lib/mingw-w64/mingw-w64-headers/defaults/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
|
||||||
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
|
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
|
||||||
@cp -rp lib/picolibc/newlib/libc/ctype build/release/tinygo/lib/picolibc/newlib/libc
|
@cp -rp lib/picolibc/newlib/libc/ctype build/release/tinygo/lib/picolibc/newlib/libc
|
||||||
@cp -rp lib/picolibc/newlib/libc/include build/release/tinygo/lib/picolibc/newlib/libc
|
@cp -rp lib/picolibc/newlib/libc/include build/release/tinygo/lib/picolibc/newlib/libc
|
||||||
|
|
|
@ -3,6 +3,7 @@ package builder
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
|
"debug/pe"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -39,10 +40,7 @@ func makeArchive(arfile *os.File, objs []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the symbols and add them to the symbol table.
|
// Read the symbols and add them to the symbol table.
|
||||||
dbg, err := elf.NewFile(objfile)
|
if dbg, err := elf.NewFile(objfile); err == nil {
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to open file %s: %w", objpath, err)
|
|
||||||
}
|
|
||||||
symbols, err := dbg.Symbols()
|
symbols, err := dbg.Symbols()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -63,6 +61,22 @@ func makeArchive(arfile *os.File, objs []string) error {
|
||||||
fileIndex int
|
fileIndex int
|
||||||
}{symbol.Name, i})
|
}{symbol.Name, i})
|
||||||
}
|
}
|
||||||
|
} else if dbg, err := pe.NewFile(objfile); err == nil {
|
||||||
|
for _, symbol := range dbg.Symbols {
|
||||||
|
if symbol.StorageClass != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if symbol.SectionNumber == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
symbolTable = append(symbolTable, struct {
|
||||||
|
name string
|
||||||
|
fileIndex int
|
||||||
|
}{symbol.Name, i})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("failed to open file %s as ELF or PE/COFF: %w", objpath, err)
|
||||||
|
}
|
||||||
|
|
||||||
// Close file, to avoid issues with too many open files (especially on
|
// Close file, to avoid issues with too many open files (especially on
|
||||||
// MacOS X).
|
// MacOS X).
|
||||||
|
@ -120,11 +134,13 @@ func makeArchive(arfile *os.File, objs []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add all object files to the archive.
|
// Add all object files to the archive.
|
||||||
|
var copyBuf bytes.Buffer
|
||||||
for i, objpath := range objs {
|
for i, objpath := range objs {
|
||||||
objfile, err := os.Open(objpath)
|
objfile, err := os.Open(objpath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer objfile.Close()
|
||||||
|
|
||||||
// Store the start index, for when we'll update the symbol table with
|
// Store the start index, for when we'll update the symbol table with
|
||||||
// the correct file start indices.
|
// the correct file start indices.
|
||||||
|
@ -155,13 +171,21 @@ func makeArchive(arfile *os.File, objs []string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy the file contents into the archive.
|
// Copy the file contents into the archive.
|
||||||
n, err := io.Copy(arwriter, objfile)
|
// First load all contents into a buffer, then write it all in one go to
|
||||||
|
// the archive file. This is a bit complicated, but is necessary because
|
||||||
|
// io.Copy can't deal with files that are of an odd size.
|
||||||
|
copyBuf.Reset()
|
||||||
|
n, err := io.Copy(©Buf, objfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("could not copy object file into ar file: %w", err)
|
||||||
}
|
}
|
||||||
if n != st.Size() {
|
if n != st.Size() {
|
||||||
return errors.New("file modified during ar creation: " + arfile.Name())
|
return errors.New("file modified during ar creation: " + arfile.Name())
|
||||||
}
|
}
|
||||||
|
_, err = arwriter.Write(copyBuf.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not copy object file into ar file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// File is not needed anymore.
|
// File is not needed anymore.
|
||||||
objfile.Close()
|
objfile.Close()
|
||||||
|
|
|
@ -115,6 +115,12 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||||
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
return errors.New("could not find wasi-libc, perhaps you need to run `make wasi-libc`?")
|
||||||
}
|
}
|
||||||
libcDependencies = append(libcDependencies, dummyCompileJob(path))
|
libcDependencies = append(libcDependencies, dummyCompileJob(path))
|
||||||
|
case "mingw-w64":
|
||||||
|
_, err := MinGW.load(config, dir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
libcDependencies = append(libcDependencies, makeMinGWExtraLibs(dir)...)
|
||||||
case "":
|
case "":
|
||||||
// no library specified, so nothing to do
|
// no library specified, so nothing to do
|
||||||
default:
|
default:
|
||||||
|
@ -518,6 +524,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil
|
||||||
// Prepare link command.
|
// Prepare link command.
|
||||||
linkerDependencies := []*compileJob{outputObjectFileJob}
|
linkerDependencies := []*compileJob{outputObjectFileJob}
|
||||||
executable := filepath.Join(dir, "main")
|
executable := filepath.Join(dir, "main")
|
||||||
|
if config.GOOS() == "windows" {
|
||||||
|
executable += ".exe"
|
||||||
|
}
|
||||||
tmppath := executable // final file
|
tmppath := executable // final file
|
||||||
ldflags := append(config.LDFlags(), "-o", executable)
|
ldflags := append(config.LDFlags(), "-o", executable)
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ func TestClangAttributes(t *testing.T) {
|
||||||
{GOOS: "linux", GOARCH: "arm64"},
|
{GOOS: "linux", GOARCH: "arm64"},
|
||||||
{GOOS: "darwin", GOARCH: "amd64"},
|
{GOOS: "darwin", GOARCH: "amd64"},
|
||||||
{GOOS: "darwin", GOARCH: "arm64"},
|
{GOOS: "darwin", GOARCH: "arm64"},
|
||||||
|
{GOOS: "windows", GOARCH: "amd64"},
|
||||||
} {
|
} {
|
||||||
t.Run("GOOS="+options.GOOS+",GOARCH="+options.GOARCH, func(t *testing.T) {
|
t.Run("GOOS="+options.GOOS+",GOARCH="+options.GOARCH, func(t *testing.T) {
|
||||||
testClangAttributes(t, options)
|
testClangAttributes(t, options)
|
||||||
|
|
|
@ -11,6 +11,11 @@ bool tinygo_link_elf(int argc, char **argv) {
|
||||||
return lld::elf::link(args, false, llvm::outs(), llvm::errs());
|
return lld::elf::link(args, false, llvm::outs(), llvm::errs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool tinygo_link_mingw(int argc, char **argv) {
|
||||||
|
std::vector<const char*> args(argv, argv + argc);
|
||||||
|
return lld::mingw::link(args, false, llvm::outs(), llvm::errs());
|
||||||
|
}
|
||||||
|
|
||||||
bool tinygo_link_wasm(int argc, char **argv) {
|
bool tinygo_link_wasm(int argc, char **argv) {
|
||||||
std::vector<const char*> args(argv, argv + argc);
|
std::vector<const char*> args(argv, argv + argc);
|
||||||
return lld::wasm::link(args, false, llvm::outs(), llvm::errs());
|
return lld::wasm::link(args, false, llvm::outs(), llvm::errs());
|
||||||
|
|
91
builder/mingw-w64.go
Обычный файл
91
builder/mingw-w64.go
Обычный файл
|
@ -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
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import (
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
bool tinygo_clang_driver(int argc, char **argv);
|
bool tinygo_clang_driver(int argc, char **argv);
|
||||||
bool tinygo_link_elf(int argc, char **argv);
|
bool tinygo_link_elf(int argc, char **argv);
|
||||||
|
bool tinygo_link_mingw(int argc, char **argv);
|
||||||
bool tinygo_link_wasm(int argc, char **argv);
|
bool tinygo_link_wasm(int argc, char **argv);
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
@ -24,6 +25,10 @@ const hasBuiltinTools = true
|
||||||
// This version actually runs the tools because TinyGo was compiled while
|
// This version actually runs the tools because TinyGo was compiled while
|
||||||
// linking statically with LLVM (with the byollvm build tag).
|
// linking statically with LLVM (with the byollvm build tag).
|
||||||
func RunTool(tool string, args ...string) error {
|
func RunTool(tool string, args ...string) error {
|
||||||
|
linker := "elf"
|
||||||
|
if tool == "ld.lld" && len(args) >= 2 && args[0] == "-m" && args[1] == "i386pep" {
|
||||||
|
linker = "mingw"
|
||||||
|
}
|
||||||
args = append([]string{"tinygo:" + tool}, args...)
|
args = append([]string{"tinygo:" + tool}, args...)
|
||||||
|
|
||||||
var cflag *C.char
|
var cflag *C.char
|
||||||
|
@ -41,7 +46,14 @@ func RunTool(tool string, args ...string) error {
|
||||||
case "clang":
|
case "clang":
|
||||||
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf))
|
||||||
case "ld.lld":
|
case "ld.lld":
|
||||||
|
switch linker {
|
||||||
|
case "elf":
|
||||||
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
|
ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf))
|
||||||
|
case "mingw":
|
||||||
|
ok = C.tinygo_link_mingw(C.int(len(args)), (**C.char)(buf))
|
||||||
|
default:
|
||||||
|
return errors.New("unknown linker: " + linker)
|
||||||
|
}
|
||||||
case "wasm-ld":
|
case "wasm-ld":
|
||||||
ok = C.tinygo_link_wasm(C.int(len(args)), (**C.char)(buf))
|
ok = C.tinygo_link_wasm(C.int(len(args)), (**C.char)(buf))
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -272,6 +272,15 @@ func (c *Config) CFlags() []string {
|
||||||
case "wasi-libc":
|
case "wasi-libc":
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
cflags = append(cflags, "--sysroot="+root+"/lib/wasi-libc/sysroot")
|
cflags = append(cflags, "--sysroot="+root+"/lib/wasi-libc/sysroot")
|
||||||
|
case "mingw-w64":
|
||||||
|
root := goenv.Get("TINYGOROOT")
|
||||||
|
path, _ := c.LibcPath("mingw-w64")
|
||||||
|
cflags = append(cflags,
|
||||||
|
"--sysroot="+path,
|
||||||
|
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "crt"),
|
||||||
|
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "mingw-w64", "mingw-w64-headers", "defaults", "include"),
|
||||||
|
"-D_UCRT",
|
||||||
|
)
|
||||||
case "":
|
case "":
|
||||||
// No libc specified, nothing to add.
|
// No libc specified, nothing to add.
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -206,6 +206,9 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||||
if options.GOARCH == "arm" {
|
if options.GOARCH == "arm" {
|
||||||
target += "-gnueabihf"
|
target += "-gnueabihf"
|
||||||
}
|
}
|
||||||
|
if options.GOOS == "windows" {
|
||||||
|
target += "-gnu"
|
||||||
|
}
|
||||||
return defaultTarget(options.GOOS, options.GOARCH, target)
|
return defaultTarget(options.GOOS, options.GOARCH, target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,13 +233,7 @@ func LoadTarget(options *Options) (*TargetSpec, error) {
|
||||||
return spec, nil
|
return spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WindowsBuildNotSupportedErr is being thrown, when goos is windows and no target has been specified.
|
|
||||||
var WindowsBuildNotSupportedErr = errors.New("Building Windows binaries is currently not supported. Try specifying a different target")
|
|
||||||
|
|
||||||
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||||
if goos == "windows" {
|
|
||||||
return nil, WindowsBuildNotSupportedErr
|
|
||||||
}
|
|
||||||
// No target spec available. Use the default one, useful on most systems
|
// No target spec available. Use the default one, useful on most systems
|
||||||
// with a regular OS.
|
// with a regular OS.
|
||||||
spec := TargetSpec{
|
spec := TargetSpec{
|
||||||
|
@ -279,12 +276,28 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||||
spec.RTLib = "compiler-rt"
|
spec.RTLib = "compiler-rt"
|
||||||
spec.Libc = "musl"
|
spec.Libc = "musl"
|
||||||
spec.LDFlags = append(spec.LDFlags, "--gc-sections")
|
spec.LDFlags = append(spec.LDFlags, "--gc-sections")
|
||||||
|
} else if goos == "windows" {
|
||||||
|
spec.Linker = "ld.lld"
|
||||||
|
spec.Libc = "mingw-w64"
|
||||||
|
spec.LDFlags = append(spec.LDFlags,
|
||||||
|
"-m", "i386pep",
|
||||||
|
"-Bdynamic",
|
||||||
|
"--image-base", "0x400000",
|
||||||
|
"--gc-sections",
|
||||||
|
"--no-insert-timestamp",
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie
|
||||||
}
|
}
|
||||||
if goarch != "wasm" {
|
if goarch != "wasm" {
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+".S")
|
suffix := ""
|
||||||
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+".S")
|
if goos == "windows" {
|
||||||
|
// Windows uses a different calling convention from other operating
|
||||||
|
// systems so we need separate assembly files.
|
||||||
|
suffix = "_windows"
|
||||||
|
}
|
||||||
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/runtime/gc_"+goarch+suffix+".S")
|
||||||
|
spec.ExtraFiles = append(spec.ExtraFiles, "src/internal/task/task_stack_"+goarch+suffix+".S")
|
||||||
}
|
}
|
||||||
if goarch != runtime.GOARCH {
|
if goarch != runtime.GOARCH {
|
||||||
// Some educated guesses as to how to invoke helper programs.
|
// Some educated guesses as to how to invoke helper programs.
|
||||||
|
@ -296,6 +309,11 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) {
|
||||||
spec.Emulator = []string{"qemu-aarch64"}
|
spec.Emulator = []string{"qemu-aarch64"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if goos != runtime.GOOS {
|
||||||
|
if goos == "windows" {
|
||||||
|
spec.Emulator = []string{"wine"}
|
||||||
|
}
|
||||||
|
}
|
||||||
return &spec, nil
|
return &spec, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,12 +156,12 @@ func (b *builder) createRawSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
// createSyscall emits instructions for the syscall.Syscall* family of
|
// createSyscall emits instructions for the syscall.Syscall* family of
|
||||||
// functions, depending on the target OS/arch.
|
// functions, depending on the target OS/arch.
|
||||||
func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
|
switch b.GOOS {
|
||||||
|
case "linux", "freebsd":
|
||||||
syscallResult, err := b.createRawSyscall(call)
|
syscallResult, err := b.createRawSyscall(call)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return syscallResult, err
|
return syscallResult, err
|
||||||
}
|
}
|
||||||
switch b.GOOS {
|
|
||||||
case "linux", "freebsd":
|
|
||||||
// Return values: r0, r1 uintptr, err Errno
|
// Return values: r0, r1 uintptr, err Errno
|
||||||
// Pseudocode:
|
// Pseudocode:
|
||||||
// var err uintptr
|
// var err uintptr
|
||||||
|
@ -180,6 +180,10 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||||
return retval, nil
|
return retval, nil
|
||||||
case "darwin":
|
case "darwin":
|
||||||
|
syscallResult, err := b.createRawSyscall(call)
|
||||||
|
if err != nil {
|
||||||
|
return syscallResult, err
|
||||||
|
}
|
||||||
// Return values: r0, r1 uintptr, err Errno
|
// Return values: r0, r1 uintptr, err Errno
|
||||||
// Pseudocode:
|
// Pseudocode:
|
||||||
// var err uintptr
|
// var err uintptr
|
||||||
|
@ -195,6 +199,57 @@ func (b *builder) createSyscall(call *ssa.CallCommon) (llvm.Value, error) {
|
||||||
retval = b.CreateInsertValue(retval, zero, 1, "")
|
retval = b.CreateInsertValue(retval, zero, 1, "")
|
||||||
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||||
return retval, nil
|
return retval, nil
|
||||||
|
case "windows":
|
||||||
|
// On Windows, syscall.Syscall* is basically just a function pointer
|
||||||
|
// call. This is complicated in gc because of stack switching and the
|
||||||
|
// different ABI, but easy in TinyGo: just call the function pointer.
|
||||||
|
// The signature looks like this:
|
||||||
|
// func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||||
|
|
||||||
|
// Prepare input values.
|
||||||
|
var paramTypes []llvm.Type
|
||||||
|
var params []llvm.Value
|
||||||
|
for _, val := range call.Args[2:] {
|
||||||
|
param := b.getValue(val)
|
||||||
|
params = append(params, param)
|
||||||
|
paramTypes = append(paramTypes, param.Type())
|
||||||
|
}
|
||||||
|
llvmType := llvm.FunctionType(b.uintptrType, paramTypes, false)
|
||||||
|
fn := b.getValue(call.Args[0])
|
||||||
|
fnPtr := b.CreateIntToPtr(fn, llvm.PointerType(llvmType, 0), "")
|
||||||
|
|
||||||
|
// Prepare some functions that will be called later.
|
||||||
|
setLastError := b.mod.NamedFunction("SetLastError")
|
||||||
|
if setLastError.IsNil() {
|
||||||
|
llvmType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{b.ctx.Int32Type()}, false)
|
||||||
|
setLastError = llvm.AddFunction(b.mod, "SetLastError", llvmType)
|
||||||
|
}
|
||||||
|
getLastError := b.mod.NamedFunction("GetLastError")
|
||||||
|
if getLastError.IsNil() {
|
||||||
|
llvmType := llvm.FunctionType(b.ctx.Int32Type(), nil, false)
|
||||||
|
getLastError = llvm.AddFunction(b.mod, "GetLastError", llvmType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now do the actual call. Pseudocode:
|
||||||
|
// SetLastError(0)
|
||||||
|
// r1 = trap(a1, a2, a3, ...)
|
||||||
|
// err = uintptr(GetLastError())
|
||||||
|
// return r1, 0, err
|
||||||
|
// Note that SetLastError/GetLastError could be replaced with direct
|
||||||
|
// access to the thread control block, which is probably smaller and
|
||||||
|
// faster. The Go runtime does this in assembly.
|
||||||
|
b.CreateCall(setLastError, []llvm.Value{llvm.ConstNull(b.ctx.Int32Type())}, "")
|
||||||
|
syscallResult := b.CreateCall(fnPtr, params, "")
|
||||||
|
errResult := b.CreateCall(getLastError, nil, "err")
|
||||||
|
if b.uintptrType != b.ctx.Int32Type() {
|
||||||
|
errResult = b.CreateZExt(errResult, b.uintptrType, "err.uintptr")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return r1, 0, err
|
||||||
|
retval := llvm.ConstNull(b.ctx.StructType([]llvm.Type{b.uintptrType, b.uintptrType, b.uintptrType}, false))
|
||||||
|
retval = b.CreateInsertValue(retval, syscallResult, 0, "")
|
||||||
|
retval = b.CreateInsertValue(retval, errResult, 2, "")
|
||||||
|
return retval, nil
|
||||||
default:
|
default:
|
||||||
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
return llvm.Value{}, b.makeError(call.Pos(), "unknown GOOS/GOARCH for syscall: "+b.GOOS+"/"+b.GOARCH)
|
||||||
}
|
}
|
||||||
|
|
1
lib/mingw-w64
Подмодуль
1
lib/mingw-w64
Подмодуль
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit acc9b9d9eb63a13d8122cbac4882eb5f4ee2f679
|
19
main_test.go
19
main_test.go
|
@ -71,11 +71,9 @@ func TestCompiler(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if runtime.GOOS != "windows" {
|
|
||||||
t.Run("Host", func(t *testing.T) {
|
t.Run("Host", func(t *testing.T) {
|
||||||
runPlatTests(optionsFromTarget(""), tests, t)
|
runPlatTests(optionsFromTarget(""), tests, t)
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
if testing.Short() {
|
if testing.Short() {
|
||||||
return
|
return
|
||||||
|
@ -113,10 +111,6 @@ func TestCompiler(t *testing.T) {
|
||||||
|
|
||||||
// Test a few build options.
|
// Test a few build options.
|
||||||
t.Run("build-options", func(t *testing.T) {
|
t.Run("build-options", func(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
// These tests assume a host that is supported by TinyGo.
|
|
||||||
t.Skip("can't test build options on Windows")
|
|
||||||
}
|
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Test with few optimizations enabled (no inlining, etc).
|
// Test with few optimizations enabled (no inlining, etc).
|
||||||
|
@ -298,6 +292,9 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
||||||
|
|
||||||
// Build the test binary.
|
// Build the test binary.
|
||||||
binary := filepath.Join(tmpdir, "test")
|
binary := filepath.Join(tmpdir, "test")
|
||||||
|
if spec.GOOS == "windows" {
|
||||||
|
binary += ".exe"
|
||||||
|
}
|
||||||
err = runBuild("./"+path, binary, &options)
|
err = runBuild("./"+path, binary, &options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
printCompilerError(t.Log, err)
|
printCompilerError(t.Log, err)
|
||||||
|
@ -339,7 +336,13 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
// Terminate the process if it runs too long.
|
// Terminate the process if it runs too long.
|
||||||
timer := time.NewTimer(10 * time.Second)
|
maxDuration := 10 * time.Second
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
// For some reason, tests on Windows can take around
|
||||||
|
// 30s to complete. TODO: investigate why and fix this.
|
||||||
|
maxDuration = 40 * time.Second
|
||||||
|
}
|
||||||
|
timer := time.NewTimer(maxDuration)
|
||||||
select {
|
select {
|
||||||
case <-runComplete:
|
case <-runComplete:
|
||||||
timer.Stop()
|
timer.Stop()
|
||||||
|
@ -369,7 +372,7 @@ func runTestWithConfig(name string, t *testing.T, options compileopts.Options, c
|
||||||
t.Log("failed to run:", err)
|
t.Log("failed to run:", err)
|
||||||
fail = true
|
fail = true
|
||||||
} else if !bytes.Equal(expected, actual) {
|
} else if !bytes.Equal(expected, actual) {
|
||||||
t.Log("output did not match")
|
t.Logf("output did not match (expected %d bytes, got %d bytes):", len(expected), len(actual))
|
||||||
fail = true
|
fail = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
42
src/crypto/rand/rand_windows.go
Обычный файл
42
src/crypto/rand/rand_windows.go
Обычный файл
|
@ -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
|
|
@ -1,4 +1,4 @@
|
||||||
// +build scheduler.tasks,amd64
|
// +build scheduler.tasks,amd64,!windows
|
||||||
|
|
||||||
package task
|
package task
|
||||||
|
|
||||||
|
|
57
src/internal/task/task_stack_amd64_windows.S
Обычный файл
57
src/internal/task/task_stack_amd64_windows.S
Обычный файл
|
@ -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
|
66
src/internal/task/task_stack_amd64_windows.go
Обычный файл
66
src/internal/task/task_stack_amd64_windows.go
Обычный файл
|
@ -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
|
||||||
|
}
|
114
src/os/file_anyos.go
Обычный файл
114
src/os/file_anyos.go
Обычный файл
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,113 +2,4 @@
|
||||||
|
|
||||||
package os
|
package os
|
||||||
|
|
||||||
import (
|
type syscallFd = int
|
||||||
"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(0), "/dev/stdin"}
|
|
||||||
Stdout = &File{unixFileHandle(1), "/dev/stdout"}
|
|
||||||
Stderr = &File{unixFileHandle(2), "/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(int(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(int(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(int(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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
7
src/os/file_windows.go
Обычный файл
7
src/os/file_windows.go
Обычный файл
|
@ -0,0 +1,7 @@
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package os
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
type syscallFd = syscall.Handle
|
22
src/runtime/gc_amd64_windows.S
Обычный файл
22
src/runtime/gc_amd64_windows.S
Обычный файл
|
@ -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
|
3
src/runtime/os_windows.go
Обычный файл
3
src/runtime/os_windows.go
Обычный файл
|
@ -0,0 +1,3 @@
|
||||||
|
package runtime
|
||||||
|
|
||||||
|
const GOOS = "windows"
|
230
src/runtime/runtime_windows.go
Обычный файл
230
src/runtime/runtime_windows.go
Обычный файл
|
@ -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() {
|
||||||
|
}
|
|
@ -62,6 +62,12 @@ const (
|
||||||
SIGTERM Signal = 0xf
|
SIGTERM Signal = 0xf
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Stdin = 0
|
||||||
|
Stdout = 1
|
||||||
|
Stderr = 2
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
O_RDONLY = 0x0
|
O_RDONLY = 0x0
|
||||||
O_WRONLY = 0x1
|
O_WRONLY = 0x1
|
||||||
|
|
3
testdata/filesystem.go
предоставленный
3
testdata/filesystem.go
предоставленный
|
@ -33,6 +33,5 @@ func main() {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
print(string(data))
|
os.Stdout.Write(data)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче