Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
9450ee8
initial
hectorchu Apr 14, 2026
02e150a
fix esp32
hectorchu Apr 14, 2026
0491538
fix ar exec
hectorchu Apr 15, 2026
98e3f5b
use builtin ar
hectorchu Apr 15, 2026
f9d09ee
xtensa: write each function to its own section
hectorchu Apr 15, 2026
21dce58
fix multiple definitions
hectorchu Apr 15, 2026
cc16c7d
link obj files together
hectorchu Apr 15, 2026
f4fb346
only link for xtensa
hectorchu Apr 15, 2026
ab5ef7e
remove call_start_cpu0
hectorchu Apr 15, 2026
f87b75d
allow c side to set gc vars
hectorchu Apr 16, 2026
6f33edd
Merge remote-tracking branch 'origin/dev' into c-archive
hectorchu Apr 16, 2026
ead5efa
fix error
hectorchu Apr 16, 2026
1728ce0
remove malloc and friends
hectorchu Apr 17, 2026
ccb331e
remove unnecessary line
hectorchu Apr 17, 2026
575886c
simplify
hectorchu Apr 17, 2026
0ec59df
simplify
hectorchu Apr 17, 2026
27e364f
revert unneeded macos change
hectorchu Apr 17, 2026
da24005
Merge branch 'dev' into c-archive
hectorchu Apr 17, 2026
fb26e5d
apply fixup to riscv as well
hectorchu Apr 17, 2026
bfa8cd7
remove nonnull attribute from malloc
hectorchu Apr 18, 2026
ad2cb23
add buildmode to tags
hectorchu Apr 19, 2026
f012bdf
Merge branch 'dev' into c-archive
hectorchu Apr 19, 2026
73291c8
doesn't seem necessary anymore
hectorchu Apr 19, 2026
4126133
better c-archive separation
hectorchu Apr 19, 2026
3c2feba
complete overhaul
hectorchu Apr 19, 2026
88f2911
for consistency
hectorchu Apr 19, 2026
cafc515
add CGO_CFLAGS
hectorchu Apr 19, 2026
85c4b74
slightly tidier
hectorchu Apr 19, 2026
604a482
simply rename the clashing symbols
hectorchu Apr 20, 2026
0bb44a8
interrupt handling done by OS
hectorchu Apr 20, 2026
6d0d99f
restrict to esp32c3
hectorchu Apr 20, 2026
b4a7075
tinygo.riscv not esp32c3
hectorchu Apr 20, 2026
823e602
place tinygo_scanCurrentStack in same section as tinygo_scanstack to …
hectorchu Apr 20, 2026
9927d6d
Merge branch 'dev' into c-archive
hectorchu Apr 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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")
}
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions compileopts/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down Expand Up @@ -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 ""
}
Expand Down
2 changes: 1 addition & 1 deletion compileopts/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"}
Expand Down
2 changes: 2 additions & 0 deletions goenv/goenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions src/device/esp/esp32.S
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
14 changes: 7 additions & 7 deletions src/device/esp/esp32c3.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -63,5 +63,5 @@ _vector_table:

.option pop

.size _vector_table, .-_vector_table
.size __vector_table, .-__vector_table

6 changes: 3 additions & 3 deletions src/device/esp/esp32s3.S
Original file line number Diff line number Diff line change
Expand Up @@ -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 ----

Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/atomics_critical.go
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
46 changes: 1 addition & 45 deletions src/runtime/baremetal.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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)
Expand Down
23 changes: 23 additions & 0 deletions src/runtime/baremetal_c_archive.go
Original file line number Diff line number Diff line change
@@ -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()
}
46 changes: 46 additions & 0 deletions src/runtime/baremetal_no_c_archive.go
Original file line number Diff line number Diff line change
@@ -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)
}
2 changes: 1 addition & 1 deletion src/runtime/interrupt/interrupt_none.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build !baremetal || tkey
//go:build !baremetal || tkey || (tinygo.riscv && buildmode.c_archive)

package interrupt

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/interrupt/interrupt_tinygoriscv.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build tinygo.riscv && !tkey
//go:build tinygo.riscv && !tkey && !buildmode.c_archive

package interrupt

Expand Down
2 changes: 1 addition & 1 deletion src/runtime/runtime_esp32c3.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,5 +173,5 @@ func sleepTicks(d timeUnit) {
}
}

//go:extern _vector_table
//go:extern __vector_table
var _vector_table [0]uintptr
2 changes: 1 addition & 1 deletion targets/esp32.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
2 changes: 1 addition & 1 deletion targets/esp32c3.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
2 changes: 1 addition & 1 deletion targets/esp32s3.ld
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
2 changes: 1 addition & 1 deletion tools/gen-critical-atomics/gen-critical-atomics.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down