Skip to content

Commit 6d2fdbb

Browse files
committed
all: include picolibc for bare metal targets
This is necessary for better CGo support on bare metal. Existing libraries expect to be able to include parts of libc and expect to be able to link to those symbols. Because with this all targets have a working libc, it is now possible to add tests to check that a libc in fact works basically. Not all parts of picolibc are included, such as the math or stdio parts. These should be added later, when needed. This commit also avoids the need for the custom memcpy/memset/memcmp symbols that are sometimes emitted by LLVM. The C library will take care of that.
1 parent 8dba491 commit 6d2fdbb

18 files changed

+196
-62
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,6 @@
1717
[submodule "lib/wasi-libc"]
1818
path = lib/wasi-libc
1919
url = https://github.com/CraneStation/wasi-libc
20+
[submodule "lib/picolibc"]
21+
path = lib/picolibc
22+
url = https://github.com/keith-packard/picolibc.git

Makefile

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ release: tinygo gen-device wasi-libc
312312
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
313313
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
314314
@mkdir -p build/release/tinygo/lib/nrfx
315+
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libc
315316
@mkdir -p build/release/tinygo/lib/wasi-libc
316317
@mkdir -p build/release/tinygo/pkg/armv6m-none-eabi
317318
@mkdir -p build/release/tinygo/pkg/armv7m-none-eabi
@@ -325,10 +326,19 @@ release: tinygo gen-device wasi-libc
325326
@cp -rp lib/compiler-rt/LICENSE.TXT build/release/tinygo/lib/compiler-rt
326327
@cp -rp lib/compiler-rt/README.txt build/release/tinygo/lib/compiler-rt
327328
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
329+
@cp -rp lib/picolibc/newlib/libc/ctype build/release/tinygo/lib/picolibc/newlib/libc
330+
@cp -rp lib/picolibc/newlib/libc/include build/release/tinygo/lib/picolibc/newlib/libc
331+
@cp -rp lib/picolibc/newlib/libc/locale build/release/tinygo/lib/picolibc/newlib/libc
332+
@cp -rp lib/picolibc/newlib/libc/string build/release/tinygo/lib/picolibc/newlib/libc
333+
@cp -rp lib/picolibc/newlib/libc/tinystdio build/release/tinygo/lib/picolibc/newlib/libc
334+
@cp -rp lib/picolibc-include build/release/tinygo/lib
328335
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
329336
@cp -rp src build/release/tinygo/src
330337
@cp -rp targets build/release/tinygo/targets
331-
./build/tinygo build-builtins -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/compiler-rt.a
332-
./build/tinygo build-builtins -target=armv7m-none-eabi -o build/release/tinygo/pkg/armv7m-none-eabi/compiler-rt.a
333-
./build/tinygo build-builtins -target=armv7em-none-eabi -o build/release/tinygo/pkg/armv7em-none-eabi/compiler-rt.a
338+
./build/tinygo build-library -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/compiler-rt.a compiler-rt
339+
./build/tinygo build-library -target=armv7m-none-eabi -o build/release/tinygo/pkg/armv7m-none-eabi/compiler-rt.a compiler-rt
340+
./build/tinygo build-library -target=armv7em-none-eabi -o build/release/tinygo/pkg/armv7em-none-eabi/compiler-rt.a compiler-rt
341+
./build/tinygo build-library -target=armv6m-none-eabi -o build/release/tinygo/pkg/armv6m-none-eabi/picolibc.a picolibc
342+
./build/tinygo build-library -target=armv7m-none-eabi -o build/release/tinygo/pkg/armv7m-none-eabi/picolibc.a picolibc
343+
./build/tinygo build-library -target=armv7em-none-eabi -o build/release/tinygo/pkg/armv7em-none-eabi/picolibc.a picolibc
334344
tar -czf build/release.tar.gz -C build/release tinygo

builder/build.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,15 @@ func Build(pkgName, outpath string, config *compileopts.Config, action func(stri
145145
ldflags = append(ldflags, librt)
146146
}
147147

148+
// Add libc.
149+
if config.Target.Libc == "picolibc" {
150+
libc, err := Picolibc.Load(config.Triple())
151+
if err != nil {
152+
return err
153+
}
154+
ldflags = append(ldflags, libc)
155+
}
156+
148157
// Compile extra files.
149158
root := goenv.Get("TINYGOROOT")
150159
for i, path := range config.ExtraFiles() {

builder/picolibc.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package builder
2+
3+
import (
4+
"path/filepath"
5+
6+
"github.com/tinygo-org/tinygo/goenv"
7+
)
8+
9+
// Picolibc is a C library for bare metal embedded devices. It was originally
10+
// based on newlib.
11+
var Picolibc = Library{
12+
name: "picolibc",
13+
cflags: func() []string {
14+
picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc")
15+
return []string{"-Werror", "-Wall", "-std=gnu11", "-D_COMPILING_NEWLIB", "-fshort-enums", "--sysroot=" + picolibcDir, "-I" + picolibcDir + "/tinystdio", "-I" + goenv.Get("TINYGOROOT") + "/lib/picolibc-include"}
16+
},
17+
sourceDir: "lib/picolibc/newlib/libc",
18+
sources: func(target string) []string {
19+
return picolibcSources
20+
},
21+
}
22+
23+
var picolibcSources = []string{
24+
"string/bcmp.c",
25+
"string/bcopy.c",
26+
"string/bzero.c",
27+
"string/explicit_bzero.c",
28+
"string/ffsl.c",
29+
"string/ffsll.c",
30+
"string/fls.c",
31+
"string/flsl.c",
32+
"string/flsll.c",
33+
"string/gnu_basename.c",
34+
"string/index.c",
35+
"string/memccpy.c",
36+
"string/memchr.c",
37+
"string/memcmp.c",
38+
"string/memcpy.c",
39+
"string/memmem.c",
40+
"string/memmove.c",
41+
"string/mempcpy.c",
42+
"string/memrchr.c",
43+
"string/memset.c",
44+
"string/rawmemchr.c",
45+
"string/rindex.c",
46+
"string/stpcpy.c",
47+
"string/stpncpy.c",
48+
"string/strcasecmp.c",
49+
"string/strcasecmp_l.c",
50+
"string/strcasestr.c",
51+
"string/strcat.c",
52+
"string/strchr.c",
53+
"string/strchrnul.c",
54+
"string/strcmp.c",
55+
"string/strcoll.c",
56+
"string/strcoll_l.c",
57+
"string/strcpy.c",
58+
"string/strcspn.c",
59+
"string/strdup.c",
60+
"string/strerror.c",
61+
"string/strerror_r.c",
62+
"string/strlcat.c",
63+
"string/strlcpy.c",
64+
"string/strlen.c",
65+
"string/strlwr.c",
66+
"string/strncasecmp.c",
67+
"string/strncasecmp_l.c",
68+
"string/strncat.c",
69+
"string/strncmp.c",
70+
"string/strncpy.c",
71+
"string/strndup.c",
72+
"string/strnlen.c",
73+
"string/strnstr.c",
74+
"string/strpbrk.c",
75+
"string/strrchr.c",
76+
"string/strsep.c",
77+
"string/strsignal.c",
78+
"string/strspn.c",
79+
"string/strstr.c",
80+
"string/strtok.c",
81+
"string/strtok_r.c",
82+
"string/strupr.c",
83+
"string/strverscmp.c",
84+
"string/strxfrm.c",
85+
"string/strxfrm_l.c",
86+
"string/swab.c",
87+
"string/timingsafe_bcmp.c",
88+
"string/timingsafe_memcmp.c",
89+
"string/u_strerr.c",
90+
"string/wcpcpy.c",
91+
"string/wcpncpy.c",
92+
"string/wcscasecmp.c",
93+
"string/wcscasecmp_l.c",
94+
"string/wcscat.c",
95+
"string/wcschr.c",
96+
"string/wcscmp.c",
97+
"string/wcscoll.c",
98+
"string/wcscoll_l.c",
99+
"string/wcscpy.c",
100+
"string/wcscspn.c",
101+
"string/wcsdup.c",
102+
"string/wcslcat.c",
103+
"string/wcslcpy.c",
104+
"string/wcslen.c",
105+
"string/wcsncasecmp.c",
106+
"string/wcsncasecmp_l.c",
107+
"string/wcsncat.c",
108+
"string/wcsncmp.c",
109+
"string/wcsncpy.c",
110+
"string/wcsnlen.c",
111+
"string/wcspbrk.c",
112+
"string/wcsrchr.c",
113+
"string/wcsspn.c",
114+
"string/wcsstr.c",
115+
"string/wcstok.c",
116+
"string/wcswidth.c",
117+
"string/wcsxfrm.c",
118+
"string/wcsxfrm_l.c",
119+
"string/wcwidth.c",
120+
"string/wmemchr.c",
121+
"string/wmemcmp.c",
122+
"string/wmemcpy.c",
123+
"string/wmemmove.c",
124+
"string/wmempcpy.c",
125+
"string/wmemset.c",
126+
"string/xpg_strerror_r.c",
127+
}

cgo/cgo.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,11 @@ func Process(files []*ast.File, dir string, fset *token.FileSet, cflags []string
170170
enums: map[string]enumInfo{},
171171
}
172172

173+
// Disable _FORTIFY_SOURCE as it causes problems on macOS.
174+
// Note that it is only disabled for memcpy (etc) calls made from Go, which
175+
// have better alternatives anyway.
176+
cflags = append(cflags, "-D_FORTIFY_SOURCE=0")
177+
173178
// Add a new location for the following file.
174179
generatedTokenPos := p.fset.AddFile(dir+"/!cgo.go", -1, 0)
175180
generatedTokenPos.SetLines([]int{0})

compileopts/config.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package compileopts
55
import (
66
"errors"
77
"fmt"
8+
"path/filepath"
89
"regexp"
910
"strconv"
1011
"strings"
@@ -163,6 +164,11 @@ func (c *Config) CFlags() []string {
163164
for _, flag := range c.Target.CFlags {
164165
cflags = append(cflags, strings.Replace(flag, "{root}", goenv.Get("TINYGOROOT"), -1))
165166
}
167+
if c.Target.Libc == "picolibc" {
168+
root := goenv.Get("TINYGOROOT")
169+
cflags = append(cflags, "--sysroot="+filepath.Join(root, "lib", "picolibc", "newlib", "libc"))
170+
cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include"))
171+
}
166172
return cflags
167173
}
168174

compileopts/target.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type TargetSpec struct {
3232
Compiler string `json:"compiler"`
3333
Linker string `json:"linker"`
3434
RTLib string `json:"rtlib"` // compiler runtime library (libgcc, compiler-rt)
35+
Libc string `json:"libc"`
3536
CFlags []string `json:"cflags"`
3637
LDFlags []string `json:"ldflags"`
3738
LinkerScript string `json:"linkerscript"`
@@ -84,6 +85,9 @@ func (spec *TargetSpec) copyProperties(spec2 *TargetSpec) {
8485
if spec2.RTLib != "" {
8586
spec.RTLib = spec2.RTLib
8687
}
88+
if spec2.Libc != "" {
89+
spec.Libc = spec2.Libc
90+
}
8791
spec.CFlags = append(spec.CFlags, spec2.CFlags...)
8892
spec.LDFlags = append(spec.LDFlags, spec2.LDFlags...)
8993
if spec2.LinkerScript != "" {

lib/picolibc

Submodule picolibc added at 80528c6

lib/picolibc-include/picolibc.h

Whitespace-only changes.

main.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ func main() {
786786
}
787787
err := Build(pkgName, *outpath, options)
788788
handleCompilerError(err)
789-
case "build-builtins":
789+
case "build-library":
790790
// Note: this command is only meant to be used while making a release!
791791
if *outpath == "" {
792792
fmt.Fprintln(os.Stderr, "No output filename supplied (-o).")
@@ -796,7 +796,22 @@ func main() {
796796
if *target == "" {
797797
fmt.Fprintln(os.Stderr, "No target (-target).")
798798
}
799-
path, err := builder.CompilerRT.Load(*target)
799+
if flag.NArg() != 1 {
800+
fmt.Fprintf(os.Stderr, "Build-library only accepts exactly one library name as argument, %d given\n", flag.NArg())
801+
usage()
802+
os.Exit(1)
803+
}
804+
var lib *builder.Library
805+
switch name := flag.Arg(0); name {
806+
case "compiler-rt":
807+
lib = &builder.CompilerRT
808+
case "picolibc":
809+
lib = &builder.Picolibc
810+
default:
811+
fmt.Fprintf(os.Stderr, "Unknown library: %s\n", name)
812+
os.Exit(1)
813+
}
814+
path, err := lib.Load(*target)
800815
handleCompilerError(err)
801816
copyFile(path, *outpath)
802817
case "flash", "gdb":

src/runtime/runtime_arm7tdmi.go

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,3 @@ func abort() {
7676
for {
7777
}
7878
}
79-
80-
// Implement memset for LLVM and compiler-rt.
81-
//go:export memset
82-
func libc_memset(ptr unsafe.Pointer, c byte, size uintptr) {
83-
for i := uintptr(0); i < size; i++ {
84-
*(*byte)(unsafe.Pointer(uintptr(ptr) + i)) = c
85-
}
86-
}
87-
88-
// Implement memmove for LLVM and compiler-rt.
89-
//go:export memmove
90-
func libc_memmove(dst, src unsafe.Pointer, size uintptr) {
91-
memmove(dst, src, size)
92-
}

src/runtime/runtime_cortexm.go

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -95,23 +95,3 @@ func handleHardFault(sp *interruptStack) {
9595
println()
9696
abort()
9797
}
98-
99-
// Implement memset for LLVM and compiler-rt.
100-
//go:export memset
101-
func libc_memset(ptr unsafe.Pointer, c byte, size uintptr) {
102-
for i := uintptr(0); i < size; i++ {
103-
*(*byte)(unsafe.Pointer(uintptr(ptr) + i)) = c
104-
}
105-
}
106-
107-
// Implement memmove for LLVM and compiler-rt.
108-
//go:export memmove
109-
func libc_memmove(dst, src unsafe.Pointer, size uintptr) {
110-
memmove(dst, src, size)
111-
}
112-
113-
// Implement memcpy for LLVM and compiler-rt.
114-
//go:export memcpy
115-
func libc_memcpy(dst, src unsafe.Pointer, size uintptr) {
116-
memcpy(dst, src, size)
117-
}

src/runtime/runtime_tinygoriscv.go

Lines changed: 0 additions & 19 deletions
This file was deleted.

targets/cortex-m.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77
"scheduler": "tasks",
88
"linker": "ld.lld",
99
"rtlib": "compiler-rt",
10+
"libc": "picolibc",
1011
"cflags": [
1112
"-Oz",
1213
"-mthumb",
1314
"-Werror",
1415
"-fshort-enums",
15-
"-nostdlibinc",
16-
"-Wno-macro-redefined",
1716
"-fno-exceptions", "-fno-unwind-tables",
1817
"-ffunction-sections", "-fdata-sections"
1918
],

targets/gameboy-advance.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
"compiler": "clang",
88
"linker": "ld.lld",
99
"rtlib": "compiler-rt",
10+
"libc": "picolibc",
1011
"cflags": [
1112
"-g",
1213
"--target=thumb4-none-eabi",
1314
"-mcpu=arm7tdmi",
1415
"-Oz",
1516
"-Werror",
1617
"-fshort-enums",
17-
"-Wno-macro-redefined",
1818
"-Qunused-arguments",
1919
"-fno-exceptions", "-fno-unwind-tables",
2020
"-ffunction-sections", "-fdata-sections"

targets/riscv.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
"compiler": "clang",
88
"linker": "ld.lld",
99
"rtlib": "compiler-rt",
10+
"libc": "picolibc",
1011
"cflags": [
1112
"--target=riscv32--none",
1213
"-march=rv32imac",
1314
"-mabi=ilp32",
1415
"-Os",
1516
"-Werror",
16-
"-nostdinc",
1717
"-fno-exceptions", "-fno-unwind-tables",
1818
"-ffunction-sections", "-fdata-sections"
1919
],

testdata/cgo/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package main
44
int fortytwo(void);
55
#include "main.h"
66
int mul(int, int);
7+
#include <string.h>
78
*/
89
import "C"
910

@@ -109,6 +110,12 @@ func main() {
109110
var _ C.option3_t = C.option3A
110111
println("option 2A:", C.option2A)
111112
println("option 3A:", C.option3A)
113+
114+
// libc: test whether C functions work at all.
115+
buf1 := []byte("foobar\x00")
116+
buf2 := make([]byte, len(buf1))
117+
C.strcpy((*C.char)(unsafe.Pointer(&buf2[0])), (*C.char)(unsafe.Pointer(&buf1[0])))
118+
println("copied string:", string(buf2[:C.strlen((*C.char)(unsafe.Pointer(&buf2[0])))]))
112119
}
113120

114121
func printUnion(union C.joined_t) C.joined_t {

0 commit comments

Comments
 (0)