diff --git a/builder/build.go b/builder/build.go index 714a331a39..466df7ed3c 100644 --- a/builder/build.go +++ b/builder/build.go @@ -142,6 +142,11 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe } } + if config.BuildMode() == "c-archive" { + config.Target.Libc = "" + config.Target.RTLib = "" + } + // Check for a libc dependency. // As a side effect, this also creates the headers for the given libc, if // the libc needs them. @@ -705,14 +710,18 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe result.Binary = result.Executable // final file ldflags := append(config.LDFlags(), "-o", result.Executable) - if config.Options.BuildMode == "c-shared" { + if config.BuildMode() == "c-archive" { + ldflags = []string{"-r", "-o", result.Executable} + } + + if config.BuildMode() == "c-shared" { if !strings.HasPrefix(config.Triple(), "wasm32-") { return result, fmt.Errorf("buildmode c-shared is only supported on wasm at the moment") } ldflags = append(ldflags, "--no-entry") } - if config.Options.BuildMode == "wasi-legacy" { + if config.BuildMode() == "wasi-legacy" { if !strings.HasPrefix(config.Triple(), "wasm32-") { return result, fmt.Errorf("buildmode wasi-legacy is only supported on wasm") } @@ -865,6 +874,15 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe if err != nil { return err } + if config.BuildMode() == "c-archive" { + result.Binary = result.Executable + ".a" + f, err := os.Create(result.Binary) + if err != nil { + return err + } + defer f.Close() + return makeArchive(f, []string{result.Executable}) + } var calculatedStacks []string var stackSizes map[string]functionStackSize diff --git a/compileopts/config.go b/compileopts/config.go index 1920d2b9cb..bb7935f065 100644 --- a/compileopts/config.go +++ b/compileopts/config.go @@ -110,6 +110,7 @@ func (c *Config) BuildTags() []string { "osusergo", // to get os/user to work "math_big_pure_go", // to get math/big to work "gc." + c.GC(), "scheduler." + c.Scheduler(), // used inside the runtime package + "buildmode." + strings.ReplaceAll(c.BuildMode(), "-", "_"), "serial." + c.Serial()}...) // used inside the machine package switch c.Scheduler() { case "threads", "cores": @@ -325,6 +326,9 @@ func (c *Config) DefaultBinaryExtension() string { // I think it's a good tradition, so let's keep it. return ".elf" } + if c.BuildMode() == "c-archive" { + return ".a" + } // Linux, MacOS, etc, don't use a file extension. Use it as a fallback. return "" } diff --git a/compileopts/options.go b/compileopts/options.go index e543dca459..469a4fb45b 100644 --- a/compileopts/options.go +++ b/compileopts/options.go @@ -8,7 +8,7 @@ import ( ) var ( - validBuildModeOptions = []string{"default", "c-shared", "wasi-legacy"} + validBuildModeOptions = []string{"default", "c-archive", "c-shared", "wasi-legacy"} validGCOptions = []string{"none", "leaking", "conservative", "custom", "precise", "boehm"} validSchedulerOptions = []string{"none", "tasks", "asyncify", "threads", "cores"} validSerialOptions = []string{"none", "uart", "usb", "rtt"} diff --git a/goenv/goenv.go b/goenv/goenv.go index fe4c8bf63e..cecb5a85dd 100644 --- a/goenv/goenv.go +++ b/goenv/goenv.go @@ -151,6 +151,8 @@ func Get(name string) string { panic("could not find cache dir: " + err.Error()) } return filepath.Join(dir, "tinygo") + case "CGO_CFLAGS": + return os.Getenv("CGO_CFLAGS") case "CGO_ENABLED": // Always enable CGo. It is required by a number of targets, including // macOS and the rp2040. diff --git a/loader/loader.go b/loader/loader.go index 1ca1b6679d..c323fb5193 100644 --- a/loader/loader.go +++ b/loader/loader.go @@ -485,6 +485,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) { var initialCFlags []string initialCFlags = append(initialCFlags, p.program.config.CFlags(true)...) initialCFlags = append(initialCFlags, "-I"+p.Dir) + initialCFlags = append(initialCFlags, goenv.Get("CGO_CFLAGS")) generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags, p.program.config.GOOS()) p.CFlags = append(initialCFlags, cflags...) p.CGoHeaders = headerCode diff --git a/src/device/esp/esp32.S b/src/device/esp/esp32.S index 1179a2daa1..8ea996a9d5 100644 --- a/src/device/esp/esp32.S +++ b/src/device/esp/esp32.S @@ -10,8 +10,8 @@ .section .text.call_start_cpu0 1: .long _stack_top -.global call_start_cpu0 -call_start_cpu0: +.global _call_start_cpu0 +_call_start_cpu0: // We need to set the stack pointer to a different value. This is somewhat // complicated in the Xtensa architecture. The code below is a modified // version of the following code: @@ -50,7 +50,7 @@ call_start_cpu0: // Jump to the runtime start function written in Go. call4 main -.section .text.tinygo_scanCurrentStack +.section .text.tinygo_scanstack .global tinygo_scanCurrentStack tinygo_scanCurrentStack: // TODO: save callee saved registers on the stack diff --git a/src/device/esp/esp32c3.S b/src/device/esp/esp32c3.S index 0395d73bcf..ec9e6d72dd 100644 --- a/src/device/esp/esp32c3.S +++ b/src/device/esp/esp32c3.S @@ -9,9 +9,9 @@ // https://www.imperialviolet.org/2016/12/31/riscv.html .section .init -.global call_start_cpu0 -.type call_start_cpu0,@function -call_start_cpu0: +.global _call_start_cpu0 +.type _call_start_cpu0,@function +_call_start_cpu0: // At this point: // - The ROM bootloader is finished and has jumped to here. // - We're running from IRAM: both IRAM and DRAM segments have been loaded @@ -49,10 +49,10 @@ call_start_cpu0: j _start .section .text.exception_vectors -.global _vector_table -.type _vector_table,@function +.global __vector_table +.type __vector_table,@function -_vector_table: +__vector_table: .option push .option norvc @@ -63,5 +63,5 @@ _vector_table: .option pop -.size _vector_table, .-_vector_table +.size __vector_table, .-__vector_table diff --git a/src/device/esp/esp32s3.S b/src/device/esp/esp32s3.S index 6566e3f342..2c4f46dc38 100644 --- a/src/device/esp/esp32s3.S +++ b/src/device/esp/esp32s3.S @@ -97,8 +97,8 @@ .Ldrom_base: .long 0x3C000000 -.global call_start_cpu0 -call_start_cpu0: +.global _call_start_cpu0 +_call_start_cpu0: // ---- 1. Windowed-ABI register file setup ---- @@ -355,7 +355,7 @@ call_start_cpu0: // pointed to by the pane's a1 (sp). After all panes are flushed, a // scan from the current sp to stackTop covers every live value. // ----------------------------------------------------------------------- -.section .text.tinygo_scanCurrentStack +.section .text.tinygo_scanstack .global tinygo_scanCurrentStack tinygo_scanCurrentStack: diff --git a/src/runtime/atomics_critical.go b/src/runtime/atomics_critical.go index 74ce321f10..d62cc562a5 100644 --- a/src/runtime/atomics_critical.go +++ b/src/runtime/atomics_critical.go @@ -1,4 +1,4 @@ -//go:build baremetal && !tinygo.wasm +//go:build baremetal && !tinygo.wasm && !buildmode.c_archive // Automatically generated file. DO NOT EDIT. // This file implements standins for non-native atomics using critical sections. diff --git a/src/runtime/baremetal.go b/src/runtime/baremetal.go index 9915f191b2..96d6721329 100644 --- a/src/runtime/baremetal.go +++ b/src/runtime/baremetal.go @@ -2,33 +2,7 @@ package runtime -import ( - "sync/atomic" - "unsafe" -) - -//go:extern _heap_start -var heapStartSymbol [0]byte - -//go:extern _heap_end -var heapEndSymbol [0]byte - -//go:extern _globals_start -var globalsStartSymbol [0]byte - -//go:extern _globals_end -var globalsEndSymbol [0]byte - -//go:extern _stack_top -var stackTopSymbol [0]byte - -var ( - heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) - heapEnd = uintptr(unsafe.Pointer(&heapEndSymbol)) - globalsStart = uintptr(unsafe.Pointer(&globalsStartSymbol)) - globalsEnd = uintptr(unsafe.Pointer(&globalsEndSymbol)) - stackTop = uintptr(unsafe.Pointer(&stackTopSymbol)) -) +import "sync/atomic" // growHeap tries to grow the heap size. It returns true if it succeeds, false // otherwise. @@ -37,24 +11,6 @@ func growHeap() bool { return false } -//export malloc -func libc_malloc(size uintptr) unsafe.Pointer { - // Note: this zeroes the returned buffer which is not necessary. - // The same goes for bytealg.MakeNoZero. - return alloc(size, nil) -} - -//export calloc -func libc_calloc(nmemb, size uintptr) unsafe.Pointer { - // No difference between calloc and malloc. - return libc_malloc(nmemb * size) -} - -//export free -func libc_free(ptr unsafe.Pointer) { - free(ptr) -} - //export runtime_putchar func runtime_putchar(c byte) { putchar(c) diff --git a/src/runtime/baremetal_c_archive.go b/src/runtime/baremetal_c_archive.go new file mode 100644 index 0000000000..d3d668f009 --- /dev/null +++ b/src/runtime/baremetal_c_archive.go @@ -0,0 +1,23 @@ +//go:build baremetal && buildmode.c_archive + +package runtime + +var ( + heapStart uintptr + heapEnd uintptr + globalsStart uintptr + globalsEnd uintptr + stackTop uintptr +) + +// Allows C consumers of the library to set the GC variables. +// +//export tinygo_init +func tinygo_init(heap, heapSize, glob, globEnd, stack uintptr) { + heapStart, heapEnd = heap, heap+heapSize + globalsStart, globalsEnd = glob, globEnd + stackTop = stack + initRand() + initHeap() + initAll() +} diff --git a/src/runtime/baremetal_no_c_archive.go b/src/runtime/baremetal_no_c_archive.go new file mode 100644 index 0000000000..41d012da11 --- /dev/null +++ b/src/runtime/baremetal_no_c_archive.go @@ -0,0 +1,46 @@ +//go:build baremetal && !buildmode.c_archive + +package runtime + +import "unsafe" + +//go:extern _heap_start +var heapStartSymbol [0]byte + +//go:extern _heap_end +var heapEndSymbol [0]byte + +//go:extern _globals_start +var globalsStartSymbol [0]byte + +//go:extern _globals_end +var globalsEndSymbol [0]byte + +//go:extern _stack_top +var stackTopSymbol [0]byte + +var ( + heapStart = uintptr(unsafe.Pointer(&heapStartSymbol)) + heapEnd = uintptr(unsafe.Pointer(&heapEndSymbol)) + globalsStart = uintptr(unsafe.Pointer(&globalsStartSymbol)) + globalsEnd = uintptr(unsafe.Pointer(&globalsEndSymbol)) + stackTop = uintptr(unsafe.Pointer(&stackTopSymbol)) +) + +//export malloc +func libc_malloc(size uintptr) unsafe.Pointer { + // Note: this zeroes the returned buffer which is not necessary. + // The same goes for bytealg.MakeNoZero. + return alloc(size, nil) +} + +//export calloc +func libc_calloc(nmemb, size uintptr) unsafe.Pointer { + // No difference between calloc and malloc. + return libc_malloc(nmemb * size) +} + +//export free +func libc_free(ptr unsafe.Pointer) { + free(ptr) +} diff --git a/src/runtime/interrupt/interrupt_none.go b/src/runtime/interrupt/interrupt_none.go index ea8bdb68c6..d1f62c1ec3 100644 --- a/src/runtime/interrupt/interrupt_none.go +++ b/src/runtime/interrupt/interrupt_none.go @@ -1,4 +1,4 @@ -//go:build !baremetal || tkey +//go:build !baremetal || tkey || (tinygo.riscv && buildmode.c_archive) package interrupt diff --git a/src/runtime/interrupt/interrupt_tinygoriscv.go b/src/runtime/interrupt/interrupt_tinygoriscv.go index 558e67150c..8c61d42b13 100644 --- a/src/runtime/interrupt/interrupt_tinygoriscv.go +++ b/src/runtime/interrupt/interrupt_tinygoriscv.go @@ -1,4 +1,4 @@ -//go:build tinygo.riscv && !tkey +//go:build tinygo.riscv && !tkey && !buildmode.c_archive package interrupt diff --git a/src/runtime/runtime_esp32c3.go b/src/runtime/runtime_esp32c3.go index 761a14d1cd..36d1189204 100644 --- a/src/runtime/runtime_esp32c3.go +++ b/src/runtime/runtime_esp32c3.go @@ -173,5 +173,5 @@ func sleepTicks(d timeUnit) { } } -//go:extern _vector_table +//go:extern __vector_table var _vector_table [0]uintptr diff --git a/targets/esp32.ld b/targets/esp32.ld index 6818ce3190..7dab4a3024 100644 --- a/targets/esp32.ld +++ b/targets/esp32.ld @@ -19,7 +19,7 @@ MEMORY /* The entry point. It is set in the image flashed to the chip, so must be * defined. */ -ENTRY(call_start_cpu0) +ENTRY(_call_start_cpu0) SECTIONS { diff --git a/targets/esp32c3.ld b/targets/esp32c3.ld index 5fe8cc89d2..ec104abf4b 100644 --- a/targets/esp32c3.ld +++ b/targets/esp32c3.ld @@ -44,7 +44,7 @@ MEMORY /* The entry point. It is set in the image flashed to the chip, so must be * defined. */ -ENTRY(call_start_cpu0) +ENTRY(_call_start_cpu0) SECTIONS { diff --git a/targets/esp32s3.ld b/targets/esp32s3.ld index 6a08f29983..d90e81b1ba 100644 --- a/targets/esp32s3.ld +++ b/targets/esp32s3.ld @@ -25,7 +25,7 @@ MEMORY /* The entry point. It is set in the image flashed to the chip, so must be * defined. */ -ENTRY(call_start_cpu0) +ENTRY(_call_start_cpu0) SECTIONS { diff --git a/tools/gen-critical-atomics/gen-critical-atomics.go b/tools/gen-critical-atomics/gen-critical-atomics.go index 98ceebb020..3bdf2eda8f 100644 --- a/tools/gen-critical-atomics/gen-critical-atomics.go +++ b/tools/gen-critical-atomics/gen-critical-atomics.go @@ -17,7 +17,7 @@ var tmpl = template.Must(template.New("go").Funcs(template.FuncMap{ return v }, "title": strings.Title, -}).Parse(`//go:build baremetal && !tinygo.wasm +}).Parse(`//go:build baremetal && !tinygo.wasm && !buildmode.c_archive // Automatically generated file. DO NOT EDIT. // This file implements standins for non-native atomics using critical sections.