aboutsummaryrefslogtreecommitdiffhomepage
path: root/builder
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-11-05 13:23:08 +0100
committerRon Evans <[email protected]>2021-11-16 11:08:30 +0100
commit869e917dc6959445fac6ee4d93cd10c98ab445e5 (patch)
tree451d6c4b9a07b60a58f9db5d865720bf95498904 /builder
parent41bcad9c194d92ba90f6f42f680087efa4f0e998 (diff)
downloadtinygo-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.go70
-rw-r--r--builder/build.go9
-rw-r--r--builder/builder_test.go1
-rw-r--r--builder/lld.cpp5
-rw-r--r--builder/mingw-w64.go91
-rw-r--r--builder/tools-builtin.go14
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(&copyBuf, 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: