diff options
-rw-r--r-- | .circleci/config.yml | 19 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Makefile | 25 | ||||
-rw-r--r-- | builder/build.go | 12 | ||||
-rw-r--r-- | builder/builtins.go | 4 | ||||
-rw-r--r-- | builder/library.go | 74 | ||||
-rw-r--r-- | builder/musl.go | 162 | ||||
-rw-r--r-- | builder/picolibc.go | 6 | ||||
-rw-r--r-- | compileopts/config.go | 19 | ||||
-rw-r--r-- | compileopts/target.go | 17 | ||||
m--------- | lib/musl | 0 | ||||
-rw-r--r-- | src/runtime/runtime_unix.go | 40 |
12 files changed, 318 insertions, 63 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml index dbdb6b54b..de75739b7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,18 +17,15 @@ commands: echo 'deb https://apt.llvm.org/buster/ llvm-toolchain-buster-<<parameters.llvm>> main' | sudo tee /etc/apt/sources.list.d/llvm.list wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add - sudo apt-get update - sudo apt-get install \ + sudo apt-get install --no-install-recommends \ llvm-<<parameters.llvm>>-dev \ clang-<<parameters.llvm>> \ libclang-<<parameters.llvm>>-dev \ lld-<<parameters.llvm>> \ - gcc-arm-linux-gnueabihf \ - gcc-aarch64-linux-gnu \ qemu-system-arm \ qemu-user \ gcc-avr \ avr-libc - sudo apt-get install --no-install-recommends libc6-dev-i386 lib32gcc-8-dev install-node: steps: - run: @@ -139,17 +136,12 @@ commands: name: "Install apt dependencies" command: | sudo apt-get update - sudo apt-get install \ + sudo apt-get install --no-install-recommends \ libgnutls30 libssl1.0.2 \ - gcc-arm-linux-gnueabihf \ - libc6-dev-armel-cross \ - gcc-aarch64-linux-gnu \ - libc6-dev-arm64-cross \ qemu-system-arm \ qemu-user \ gcc-avr \ avr-libc - sudo apt-get install --no-install-recommends libc6-dev-i386 lib32gcc-6-dev - install-node - install-wasmtime - install-xtensa-toolchain: @@ -209,17 +201,12 @@ commands: name: "Install apt dependencies" command: | sudo apt-get update - sudo apt-get install \ + sudo apt-get install --no-install-recommends \ libgnutls30 libssl1.0.2 \ - gcc-arm-linux-gnueabihf \ - libc6-dev-armel-cross \ - gcc-aarch64-linux-gnu \ - libc6-dev-arm64-cross \ qemu-system-arm \ qemu-user \ gcc-avr \ avr-libc - sudo apt-get install --no-install-recommends libc6-dev-i386 lib32gcc-6-dev - install-node - install-wasmtime - install-xtensa-toolchain: diff --git a/.gitmodules b/.gitmodules index f4138be50..4df14b5ef 100644 --- a/.gitmodules +++ b/.gitmodules @@ -23,3 +23,6 @@ [submodule "lib/stm32-svd"] path = lib/stm32-svd url = https://github.com/tinygo-org/stm32-svd +[submodule "lib/musl"] + path = lib/musl + url = git://git.musl-libc.org/musl @@ -465,6 +465,7 @@ endif @$(MD5SUM) test.nro $(TINYGO) build -size short -o test.hex -target=pca10040 -opt=0 ./testdata/stdlib.go @$(MD5SUM) test.hex + GOOS=linux GOARCH=arm $(TINYGO) build -size short -o test.elf ./testdata/cgo ifneq ($(OS),Windows_NT) $(TINYGO) build -o test.elf -gc=leaking -scheduler=none examples/serial endif @@ -478,6 +479,9 @@ build/release: tinygo gen-device wasi-libc @mkdir -p build/release/tinygo/lib/clang/include @mkdir -p build/release/tinygo/lib/CMSIS/CMSIS @mkdir -p build/release/tinygo/lib/compiler-rt/lib + @mkdir -p build/release/tinygo/lib/musl/arch + @mkdir -p build/release/tinygo/lib/musl/crt + @mkdir -p build/release/tinygo/lib/musl/src @mkdir -p build/release/tinygo/lib/nrfx @mkdir -p build/release/tinygo/lib/picolibc/newlib/libc @mkdir -p build/release/tinygo/lib/picolibc/newlib/libm @@ -493,6 +497,27 @@ build/release: tinygo gen-device wasi-libc @cp -rp lib/compiler-rt/lib/builtins build/release/tinygo/lib/compiler-rt/lib @cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt @cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt + @cp -rp lib/musl/arch/aarch64 build/release/tinygo/lib/musl/arch + @cp -rp lib/musl/arch/arm build/release/tinygo/lib/musl/arch + @cp -rp lib/musl/arch/generic build/release/tinygo/lib/musl/arch + @cp -rp lib/musl/arch/i386 build/release/tinygo/lib/musl/arch + @cp -rp lib/musl/arch/x86_64 build/release/tinygo/lib/musl/arch + @cp -rp lib/musl/crt/crt1.c build/release/tinygo/lib/musl/crt + @cp -rp lib/musl/COPYRIGHT build/release/tinygo/lib/musl + @cp -rp lib/musl/include build/release/tinygo/lib/musl + @cp -rp lib/musl/src/env build/release/tinygo/lib/musl/src + @cp -rp lib/musl/src/errno build/release/tinygo/lib/musl/src + @cp -rp lib/musl/src/exit build/release/tinygo/lib/musl/src + @cp -rp lib/musl/src/include build/release/tinygo/lib/musl/src + @cp -rp lib/musl/src/internal build/release/tinygo/lib/musl/src + @cp -rp lib/musl/src/malloc build/release/tinygo/lib/musl/src + @cp -rp lib/musl/src/mman build/release/tinygo/lib/musl/src + @cp -rp lib/musl/src/signal build/release/tinygo/lib/musl/src + @cp -rp lib/musl/src/stdio build/release/tinygo/lib/musl/src + @cp -rp lib/musl/src/string 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/unistd build/release/tinygo/lib/musl/src @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/include build/release/tinygo/lib/picolibc/newlib/libc diff --git a/builder/build.go b/builder/build.go index 73a0f2574..e8ae7bc15 100644 --- a/builder/build.go +++ b/builder/build.go @@ -92,6 +92,13 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil root := goenv.Get("TINYGOROOT") var libcDependencies []*compileJob switch config.Target.Libc { + case "musl": + job, err := Musl.load(config, dir) + if err != nil { + return err + } + libcDependencies = append(libcDependencies, dummyCompileJob(filepath.Join(filepath.Dir(job.result), "crt1.o"))) + libcDependencies = append(libcDependencies, job) case "picolibc": libcJob, err := Picolibc.load(config, dir) if err != nil { @@ -578,12 +585,15 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(Buil // while we're at it. Relocations can only be compressed when debug // information is stripped. ldflags = append(ldflags, "--strip-debug", "--compress-relocations") + } else if config.Target.Linker == "ld.lld" { + // ld.lld is also used on Linux. + ldflags = append(ldflags, "--strip-debug") } else { switch config.GOOS() { case "linux": // Either real linux or an embedded system (like AVR) that // pretends to be Linux. It's a ELF linker wrapped by GCC in any - // case. + // case (not ld.lld - that case is handled above). ldflags = append(ldflags, "-Wl,--strip-debug") case "darwin": // MacOS (darwin) doesn't have a linker flag to strip debug diff --git a/builder/builtins.go b/builder/builtins.go index 33bcc99a3..76ef3975c 100644 --- a/builder/builtins.go +++ b/builder/builtins.go @@ -158,11 +158,11 @@ var aeabiBuiltins = []string{ // For more information, see: https://compiler-rt.llvm.org/ var CompilerRT = Library{ name: "compiler-rt", - cflags: func(headerPath string) []string { + cflags: func(target, headerPath string) []string { return []string{"-Werror", "-Wall", "-std=c11", "-nostdlibinc"} }, sourceDir: "lib/compiler-rt/lib/builtins", - sources: func(target string) []string { + librarySources: func(target string) []string { builtins := append([]string{}, genericBuiltins...) // copy genericBuiltins if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") { builtins = append(builtins, aeabiBuiltins...) diff --git a/builder/library.go b/builder/library.go index 511348c46..730797854 100644 --- a/builder/library.go +++ b/builder/library.go @@ -17,31 +17,19 @@ type Library struct { name string // makeHeaders creates a header include dir for the library - makeHeaders func(includeDir string) error + makeHeaders func(target, includeDir string) error // cflags returns the C flags specific to this library - cflags func(headerPath string) []string + cflags func(target, headerPath string) []string // The source directory, relative to TINYGOROOT. sourceDir string // The source files, relative to sourceDir. - sources func(target string) []string -} - -// fullPath returns the full path to the source directory. -func (l *Library) fullPath() string { - return filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir) -} + librarySources func(target string) []string -// sourcePaths returns a slice with the full paths to the source files. -func (l *Library) sourcePaths(target string) []string { - sources := l.sources(target) - paths := make([]string, len(sources)) - for i, name := range sources { - paths[i] = filepath.Join(l.fullPath(), name) - } - return paths + // The source code for the crt1.o file, relative to sourceDir. + crt1Source string } // Load the library archive, possibly generating and caching it if needed. @@ -90,6 +78,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ // Make headers if needed. headerPath := filepath.Join(outdir, "include") + target := config.Triple() if l.makeHeaders != nil { if _, err = os.Stat(headerPath); err != nil { temporaryHeaderPath, err := ioutil.TempDir(outdir, "include.tmp*") @@ -97,7 +86,7 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ return nil, err } defer os.RemoveAll(temporaryHeaderPath) - err = l.makeHeaders(temporaryHeaderPath) + err = l.makeHeaders(target, temporaryHeaderPath) if err != nil { return nil, err } @@ -119,11 +108,16 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ // Note: -fdebug-prefix-map is necessary to make the output archive // reproducible. Otherwise the temporary directory is stored in the archive // itself, which varies each run. - target := config.Triple() - args := append(l.cflags(headerPath), "-c", "-Oz", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir) + args := append(l.cflags(target, headerPath), "-c", "-Oz", "-g", "-ffunction-sections", "-fdata-sections", "-Wno-macro-redefined", "--target="+target, "-fdebug-prefix-map="+dir+"="+remapDir) cpu := config.CPU() if cpu != "" { - args = append(args, "-mcpu="+cpu) + // X86 has deprecated the -mcpu flag, so we need to use -march instead. + // However, ARM has not done this. + if strings.HasPrefix(target, "i386") || strings.HasPrefix(target, "x86_64") { + args = append(args, "-march="+cpu) + } else { + args = append(args, "-mcpu="+cpu) + } } if strings.HasPrefix(target, "arm") || strings.HasPrefix(target, "thumb") { args = append(args, "-fshort-enums", "-fomit-frame-pointer", "-mfloat-abi=soft") @@ -162,9 +156,15 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ // Create jobs to compile all sources. These jobs are depended upon by the // archive job above, so must be run first. - for _, srcpath := range l.sourcePaths(target) { - srcpath := srcpath // avoid concurrency issues by redefining inside the loop - objpath := filepath.Join(dir, filepath.Base(srcpath)+".o") + for _, path := range l.librarySources(target) { + // Strip leading "../" parts off the path. + cleanpath := path + for strings.HasPrefix(cleanpath, "../") { + cleanpath = cleanpath[3:] + } + srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, path) + objpath := filepath.Join(dir, cleanpath+".o") + os.MkdirAll(filepath.Dir(objpath), 0o777) objs = append(objs, objpath) job.dependencies = append(job.dependencies, &compileJob{ description: "compile " + srcpath, @@ -181,5 +181,31 @@ func (l *Library) load(config *compileopts.Config, tmpdir string) (job *compileJ }) } + // Create crt1.o job, if needed. + // Add this as a (fake) dependency to the ar file so it gets compiled. + // (It could be done in parallel with creating the ar file, but it probably + // won't make much of a difference in speed). + if l.crt1Source != "" { + srcpath := filepath.Join(goenv.Get("TINYGOROOT"), l.sourceDir, l.crt1Source) + job.dependencies = append(job.dependencies, &compileJob{ + description: "compile " + srcpath, + run: func(*compileJob) error { + var compileArgs []string + compileArgs = append(compileArgs, args...) + tmpfile, err := ioutil.TempFile(outdir, "crt1.o.tmp*") + if err != nil { + return err + } + tmpfile.Close() + compileArgs = append(compileArgs, "-o", tmpfile.Name(), srcpath) + err = runCCompiler(compileArgs...) + if err != nil { + return &commandError{"failed to build", srcpath, err} + } + return os.Rename(tmpfile.Name(), filepath.Join(outdir, "crt1.o")) + }, + }) + } + return job, nil } diff --git a/builder/musl.go b/builder/musl.go new file mode 100644 index 000000000..a54c092a3 --- /dev/null +++ b/builder/musl.go @@ -0,0 +1,162 @@ +package builder + +import ( + "bytes" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/tinygo-org/tinygo/compileopts" + "github.com/tinygo-org/tinygo/goenv" +) + +var Musl = Library{ + name: "musl", + makeHeaders: func(target, includeDir string) error { + bits := filepath.Join(includeDir, "bits") + err := os.Mkdir(bits, 0777) + if err != nil { + return err + } + + arch := compileopts.MuslArchitecture(target) + muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib", "musl") + + // Create the file alltypes.h. + f, err := os.Create(filepath.Join(bits, "alltypes.h")) + if err != nil { + return err + } + infiles := []string{ + filepath.Join(muslDir, "arch", arch, "bits", "alltypes.h.in"), + filepath.Join(muslDir, "include", "alltypes.h.in"), + } + for _, infile := range infiles { + data, err := ioutil.ReadFile(infile) + if err != nil { + return err + } + lines := strings.Split(string(data), "\n") + for _, line := range lines { + if strings.HasPrefix(line, "TYPEDEF ") { + matches := regexp.MustCompile(`TYPEDEF (.*) ([^ ]*);`).FindStringSubmatch(line) + value := matches[1] + name := matches[2] + line = fmt.Sprintf("#if defined(__NEED_%s) && !defined(__DEFINED_%s)\ntypedef %s %s;\n#define __DEFINED_%s\n#endif\n", name, name, value, name, name) + } + if strings.HasPrefix(line, "STRUCT ") { + matches := regexp.MustCompile(`STRUCT * ([^ ]*) (.*);`).FindStringSubmatch(line) + name := matches[1] + value := matches[2] + line = fmt.Sprintf("#if defined(__NEED_struct_%s) && !defined(__DEFINED_struct_%s)\nstruct %s %s;\n#define __DEFINED_struct_%s\n#endif\n", name, name, name, value, name) + } + f.WriteString(line + "\n") + } + } + f.Close() + + // Create the file syscall.h. + f, err = os.Create(filepath.Join(bits, "syscall.h")) + if err != nil { + return err + } + data, err := ioutil.ReadFile(filepath.Join(muslDir, "arch", arch, "bits", "syscall.h.in")) + if err != nil { + return err + } + _, err = f.Write(bytes.ReplaceAll(data, []byte("__NR_"), []byte("SYS_"))) + if err != nil { + return err + } + f.Close() + + return nil + }, + cflags: func(target, headerPath string) []string { + arch := compileopts.MuslArchitecture(target) + muslDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/musl") + return []string{ + "-std=c99", // same as in musl + "-D_XOPEN_SOURCE=700", // same as in musl + // Musl triggers some warnings and we don't want to show any + // warnings while compiling (only errors or silence), so disable + // specific warnings that are triggered in musl. + "-Werror", + "-Wno-logical-op-parentheses", + "-Wno-bitwise-op-parentheses", + "-Wno-shift-op-parentheses", + "-Wno-ignored-attributes", + "-Wno-string-plus-int", + "-Qunused-arguments", + // Select include dirs. Don't include standard library includes + // (that would introduce host dependencies and other complications), + // but do include all the include directories expected by musl. + "-nostdlibinc", + "-I" + muslDir + "/arch/" + arch, + "-I" + muslDir + "/arch/generic", + "-I" + muslDir + "/src/include", + "-I" + muslDir + "/src/internal", + "-I" + headerPath, + "-I" + muslDir + "/include", + } + }, + sourceDir: "lib/musl/src", + librarySources: func(target string) []string { + arch := compileopts.MuslArchitecture(target) + globs := []string{ + "env/*.c", + "errno/*.c", + "exit/*.c", + "internal/defsysinfo.c", + "internal/libc.c", + "internal/syscall_ret.c", + "internal/vdso.c", + "malloc/*.c", + "mman/*.c", + "signal/*.c", + "stdio/*.c", + "string/*.c", + "thread/" + arch + "/*.s", + "thread/" + arch + "/*.c", + "thread/*.c", + "time/*.c", + "unistd/*.c", + } + + var sources []string + seenSources := map[string]struct{}{} + basepath := goenv.Get("TINYGOROOT") + "/lib/musl/src/" + for _, pattern := range globs { + matches, err := filepath.Glob(basepath + pattern) + if err != nil { + // From the documentation: + // > Glob ignores file system errors such as I/O errors reading + // > directories. The only possible returned error is + // > ErrBadPattern, when pattern is malformed. + // So the only possible error is when the (statically defined) + // pattern is wrong. In other words, a programming bug. + panic("could not glob source dirs: " + err.Error()) + } + for _, match := range matches { + relpath, err := filepath.Rel(basepath, match) + if err != nil { + // Not sure if this is even possible. + panic(err) + } + // Make sure architecture specific files override generic files. + id := strings.ReplaceAll(relpath, "/"+arch+"/", "/") + if _, ok := seenSources[id]; ok { + // Already seen this file, skipping this (generic) file. + continue + } + seenSources[id] = struct{}{} + sources = append(sources, relpath) + } + } + return sources + }, + crt1Source: "../crt/crt1.c", // lib/musl/crt/crt1.c +} diff --git a/builder/picolibc.go b/builder/picolibc.go index 273c13609..75c93e91c 100644 --- a/builder/picolibc.go +++ b/builder/picolibc.go @@ -11,14 +11,14 @@ import ( // based on newlib. var Picolibc = Library{ name: "picolibc", - makeHeaders: func(includeDir string) error { + makeHeaders: func(target, includeDir string) error { f, err := os.Create(filepath.Join(includeDir, "picolibc.h")) if err != nil { return err } return f.Close() }, - cflags: func(headerPath string) []string { + cflags: func(target, headerPath string) []string { picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc") return []string{ "-Werror", @@ -34,7 +34,7 @@ var Picolibc = Library{ } }, sourceDir: "lib/picolibc/newlib/libc", - sources: func(target string) []string { + librarySources: func(target string) []string { return picolibcSources }, } diff --git a/compileopts/config.go b/compileopts/config.go index 9664d2770..bdbdd0d20 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -198,6 +198,16 @@ func (c *Config) RP2040BootPatch() bool { return false } +// MuslArchitecture returns the architecture name as used in musl libc. It is +// usually the same as the first part of the LLVM triple, but not always. +func MuslArchitecture(triple string) string { + arch := strings.Split(triple, "-")[0] + if strings.HasPrefix(arch, "arm") || strings.HasPrefix(arch, "thumb") { + arch = "arm" + } + return arch +} + // LibcPath returns the path to the libc directory. The libc path will be either // a precompiled libc shipped with a TinyGo build, or a libc path in the cache // directory (which might not yet be built). @@ -238,6 +248,15 @@ func (c *Config) CFlags() []string { "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(picolibcDir, "include"), "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(picolibcDir, "tinystdio"), ) + case "musl": + root := goenv.Get("TINYGOROOT") + path, _ := c.LibcPath("musl") + arch := MuslArchitecture(c.Triple()) + cflags = append(cflags, + "--sysroot="+path, + "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "musl", "arch", arch), + "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "musl", "include"), + ) case "wasi-libc": root := goenv.Get("TINYGOROOT") cflags = append(cflags, "--sysroot="+root+"/lib/wasi-libc/sysroot") diff --git a/compileopts/target.go b/compileopts/target.go index 88beb7a0a..0293f2b4f 100644 --- a/compileopts/target.go +++ b/compileopts/target.go @@ -234,6 +234,11 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) { if goos == "darwin" { spec.CFlags = append(spec.CFlags, "-isysroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk") spec.LDFlags = append(spec.LDFlags, "-Wl,-dead_strip") + } else if goos == "linux" { + spec.Linker = "ld.lld" + spec.RTLib = "compiler-rt" + spec.Libc = "musl" + spec.LDFlags = append(spec.LDFlags, "--gc-sections") } else { spec.LDFlags = append(spec.LDFlags, "-no-pie", "-Wl,--gc-sections") // WARNING: clang < 5.0 requires -nopie } @@ -245,18 +250,10 @@ func defaultTarget(goos, goarch, triple string) (*TargetSpec, error) { // Some educated guesses as to how to invoke helper programs. spec.GDB = []string{"gdb-multiarch"} if goarch == "arm" && goos == "linux" { - spec.CFlags = append(spec.CFlags, "--sysroot=/usr/arm-linux-gnueabihf") - spec.Linker = "arm-linux-gnueabihf-gcc" - spec.Emulator = []string{"qemu-arm", "-L", "/usr/arm-linux-gnueabihf"} + spec.Emulator = []string{"qemu-arm"} } if goarch == "arm64" && goos == "linux" { - spec.CFlags = append(spec.CFlags, "--sysroot=/usr/aarch64-linux-gnu") - spec.Linker = "aarch64-linux-gnu-gcc" - spec.Emulator = []string{"qemu-aarch64", "-L", "/usr/aarch64-linux-gnu"} - } - if goarch == "386" && runtime.GOARCH == "amd64" { - spec.CFlags = append(spec.CFlags, "-m32") - spec.LDFlags = append(spec.LDFlags, "-m32") + spec.Emulator = []string{"qemu-aarch64"} } } return &spec, nil diff --git a/lib/musl b/lib/musl new file mode 160000 +Subproject 040c1d16b468c50c04fc94edff521f163770832 diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index aa3515f1a..e53ff3573 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -17,8 +17,11 @@ func usleep(usec uint) int func malloc(size uintptr) unsafe.Pointer // void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); +// Note: off_t is defined as int64 because: +// - musl (used on Linux) always defines it as int64 +// - darwin is practically always 64-bit anyway //export mmap -func mmap(addr unsafe.Pointer, length uintptr, prot, flags, fd int, offset int) unsafe.Pointer +func mmap(addr unsafe.Pointer, length uintptr, prot, flags, fd int, offset int64) unsafe.Pointer //export abort func abort() @@ -27,16 +30,39 @@ func abort() func exit(code int) //export clock_gettime -func clock_gettime(clk_id int32, ts *timespec) +func libc_clock_gettime(clk_id int32, ts *timespec) + +//export __clock_gettime64 +func libc_clock_gettime64(clk_id int32, ts *timespec) + +// Portable (64-bit) variant of clock_gettime. +func clock_gettime(clk_id int32, ts *timespec) { + if TargetBits == 32 { + // This is a 32-bit architecture (386, arm, etc). + // We would like to use the 64-bit version of this function so that + // binaries will continue to run after Y2038. + // For more information: + // - https://musl.libc.org/time64.html + // - https://sourceware.org/glibc/wiki/Y2038ProofnessDesign + libc_clock_gettime64(clk_id, ts) + } else { + // This is a 64-bit architecture (amd64, arm64, etc). + // Use the regular variant, because it already fixes the Y2038 problem + // by using 64-bit integer types. + libc_clock_gettime(clk_id, ts) + } +} type timeUnit int64 -// Note: tv_sec and tv_nsec vary in size by platform. They are 32-bit on 32-bit -// systems and 64-bit on 64-bit systems (at least on macOS/Linux), so we can -// simply use the 'int' type which does the same. +// Note: tv_sec and tv_nsec normally vary in size by platform. However, we're +// using the time64 variant (see clock_gettime above), so the formats are the +// same between 32-bit and 64-bit architectures. +// There is one issue though: on big-endian systems, tv_nsec would be incorrect. +// But we don't support big-endian systems yet (as of 2021) so this is fine. type timespec struct { - tv_sec int // time_t: follows the platform bitness - tv_nsec int // long: on Linux and macOS, follows the platform bitness + tv_sec int64 // time_t with time64 support (always 64-bit) + tv_nsec int64 // unsigned 64-bit integer on all time64 platforms } var stackTop uintptr |