diff options
author | Ayke van Laethem <[email protected]> | 2021-11-05 13:23:08 +0100 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-11-16 11:08:30 +0100 |
commit | 869e917dc6959445fac6ee4d93cd10c98ab445e5 (patch) | |
tree | 451d6c4b9a07b60a58f9db5d865720bf95498904 /builder | |
parent | 41bcad9c194d92ba90f6f42f680087efa4f0e998 (diff) | |
download | tinygo-869e917dc6959445fac6ee4d93cd10c98ab445e5.tar.gz tinygo-869e917dc6959445fac6ee4d93cd10c98ab445e5.zip |
all: add support for windows/amd64
This uses Mingw-w64, which seems to be the de facto standard for porting
Unixy programs to Windows.
Diffstat (limited to 'builder')
-rw-r--r-- | builder/ar.go | 70 | ||||
-rw-r--r-- | builder/build.go | 9 | ||||
-rw-r--r-- | builder/builder_test.go | 1 | ||||
-rw-r--r-- | builder/lld.cpp | 5 | ||||
-rw-r--r-- | builder/mingw-w64.go | 91 | ||||
-rw-r--r-- | builder/tools-builtin.go | 14 |
6 files changed, 166 insertions, 24 deletions
diff --git a/builder/ar.go b/builder/ar.go index dcdc48d4d..7e33e731b 100644 --- a/builder/ar.go +++ b/builder/ar.go @@ -3,6 +3,7 @@ package builder import ( "bytes" "debug/elf" + "debug/pe" "encoding/binary" "errors" "fmt" @@ -39,29 +40,42 @@ func makeArchive(arfile *os.File, objs []string) error { } // Read the symbols and add them to the symbol table. - dbg, err := elf.NewFile(objfile) - if err != nil { - return fmt.Errorf("failed to open file %s: %w", objpath, err) - } - symbols, err := dbg.Symbols() - if err != nil { - return err - } - for _, symbol := range symbols { - bind := elf.ST_BIND(symbol.Info) - if bind != elf.STB_GLOBAL && bind != elf.STB_WEAK { - // Don't include local symbols (STB_LOCAL). - continue + if dbg, err := elf.NewFile(objfile); err == nil { + symbols, err := dbg.Symbols() + if err != nil { + return err } - if elf.ST_TYPE(symbol.Info) != elf.STT_FUNC && elf.ST_TYPE(symbol.Info) != elf.STT_OBJECT { - // Not a function. - continue + for _, symbol := range symbols { + bind := elf.ST_BIND(symbol.Info) + if bind != elf.STB_GLOBAL && bind != elf.STB_WEAK { + // Don't include local symbols (STB_LOCAL). + continue + } + if elf.ST_TYPE(symbol.Info) != elf.STT_FUNC && elf.ST_TYPE(symbol.Info) != elf.STT_OBJECT { + // Not a function. + continue + } + // Include in archive. + symbolTable = append(symbolTable, struct { + name string + fileIndex int + }{symbol.Name, i}) } - // Include in archive. - symbolTable = append(symbolTable, struct { - name string - fileIndex int - }{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 @@ -120,11 +134,13 @@ func makeArchive(arfile *os.File, objs []string) error { } // Add all object files to the archive. + var copyBuf bytes.Buffer for i, objpath := range objs { objfile, err := os.Open(objpath) if err != nil { return err } + defer objfile.Close() // Store the start index, for when we'll update the symbol table with // the correct file start indices. @@ -155,13 +171,21 @@ func makeArchive(arfile *os.File, objs []string) error { } // 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 { - return err + return fmt.Errorf("could not copy object file into ar file: %w", err) } if n != st.Size() { 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. objfile.Close() diff --git a/builder/build.go b/builder/build.go index 252714964..3bb685a80 100644 --- a/builder/build.go +++ b/builder/build.go @@ -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`?") } libcDependencies = append(libcDependencies, dummyCompileJob(path)) + case "mingw-w64": + _, err := MinGW.load(config, dir) + if err != nil { + return err + } + libcDependencies = append(libcDependencies, makeMinGWExtraLibs(dir)...) case "": // no library specified, so nothing to do default: @@ -518,6 +524,9 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil // Prepare link command. linkerDependencies := []*compileJob{outputObjectFileJob} executable := filepath.Join(dir, "main") + if config.GOOS() == "windows" { + executable += ".exe" + } tmppath := executable // final file ldflags := append(config.LDFlags(), "-o", executable) diff --git a/builder/builder_test.go b/builder/builder_test.go index 6e08b9ac8..cff43db88 100644 --- a/builder/builder_test.go +++ b/builder/builder_test.go @@ -58,6 +58,7 @@ func TestClangAttributes(t *testing.T) { {GOOS: "linux", GOARCH: "arm64"}, {GOOS: "darwin", GOARCH: "amd64"}, {GOOS: "darwin", GOARCH: "arm64"}, + {GOOS: "windows", GOARCH: "amd64"}, } { t.Run("GOOS="+options.GOOS+",GOARCH="+options.GOARCH, func(t *testing.T) { testClangAttributes(t, options) diff --git a/builder/lld.cpp b/builder/lld.cpp index 9231df9cf..5a9ad8a20 100644 --- a/builder/lld.cpp +++ b/builder/lld.cpp @@ -11,6 +11,11 @@ bool tinygo_link_elf(int argc, char **argv) { 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) { std::vector<const char*> args(argv, argv + argc); return lld::wasm::link(args, false, llvm::outs(), llvm::errs()); diff --git a/builder/mingw-w64.go b/builder/mingw-w64.go new file mode 100644 index 000000000..bdab730fa --- /dev/null +++ b/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 +} diff --git a/builder/tools-builtin.go b/builder/tools-builtin.go index 61e7a325b..29dfa8613 100644 --- a/builder/tools-builtin.go +++ b/builder/tools-builtin.go @@ -13,6 +13,7 @@ import ( #include <stdlib.h> bool tinygo_clang_driver(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); */ import "C" @@ -24,6 +25,10 @@ const hasBuiltinTools = true // This version actually runs the tools because TinyGo was compiled while // linking statically with LLVM (with the byollvm build tag). 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...) var cflag *C.char @@ -41,7 +46,14 @@ func RunTool(tool string, args ...string) error { case "clang": ok = C.tinygo_clang_driver(C.int(len(args)), (**C.char)(buf)) case "ld.lld": - ok = C.tinygo_link_elf(C.int(len(args)), (**C.char)(buf)) + switch linker { + case "elf": + 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": ok = C.tinygo_link_wasm(C.int(len(args)), (**C.char)(buf)) default: |