Browse Source

cgo: add support for stdio in picolibc and wasi-libc

This adds support for stdio in picolibc and fixes wasm_exec.js so that
it can also support C puts. With this, C stdout works on all supported
platforms.
pull/2206/head
Ayke van Laethem 3 years ago
committed by Ron Evans
parent
commit
14bb90c3c0
  1. 2
      Dockerfile
  2. 3
      Makefile
  3. 6
      builder/ar.go
  4. 112
      builder/picolibc.go
  5. 7
      compileopts/config.go
  6. 2
      lib/picolibc
  7. 18
      lib/picolibc-stdio.c
  8. 5
      src/runtime/baremetal.go
  9. 4
      targets/wasm_exec.js
  10. 5
      testdata/cgo/main.go
  11. 1
      testdata/cgo/out.txt

2
Dockerfile

@ -15,7 +15,7 @@ RUN cd /tinygo/ && \
git submodule sync && \
git submodule update --init --recursive --force
COPY ./lib/picolibc-include/* /tinygo/lib/picolibc-include/
COPY ./lib/picolibc-* /tinygo/lib/
RUN cd /tinygo/ && \
go install /tinygo/

3
Makefile

@ -480,6 +480,7 @@ build/release: tinygo gen-device wasi-libc
@mkdir -p build/release/tinygo/lib/compiler-rt/lib
@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
@mkdir -p build/release/tinygo/lib/wasi-libc
@mkdir -p build/release/tinygo/pkg/armv6m-unknown-unknown-eabi
@mkdir -p build/release/tinygo/pkg/armv7m-unknown-unknown-eabi
@ -498,7 +499,9 @@ build/release: tinygo gen-device wasi-libc
@cp -rp lib/picolibc/newlib/libc/locale build/release/tinygo/lib/picolibc/newlib/libc
@cp -rp lib/picolibc/newlib/libc/string build/release/tinygo/lib/picolibc/newlib/libc
@cp -rp lib/picolibc/newlib/libc/tinystdio build/release/tinygo/lib/picolibc/newlib/libc
@cp -rp lib/picolibc/newlib/libm/common build/release/tinygo/lib/picolibc/newlib/libm
@cp -rp lib/picolibc-include build/release/tinygo/lib
@cp -rp lib/picolibc-stdio.c build/release/tinygo/lib
@cp -rp lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
@cp -rp src build/release/tinygo/src
@cp -rp targets build/release/tinygo/targets

6
builder/ar.go

@ -5,6 +5,7 @@ import (
"debug/elf"
"encoding/binary"
"errors"
"fmt"
"io"
"os"
"path/filepath"
@ -49,7 +50,7 @@ func makeArchive(archivePath string, objs []string) error {
// Read the symbols and add them to the symbol table.
dbg, err := elf.NewFile(objfile)
if err != nil {
return err
return fmt.Errorf("failed to open file %s: %w", objpath, err)
}
symbols, err := dbg.Symbols()
if err != nil {
@ -61,9 +62,8 @@ func makeArchive(archivePath string, objs []string) error {
// Don't include local symbols (STB_LOCAL).
continue
}
if elf.ST_TYPE(symbol.Info) != elf.STT_FUNC {
if elf.ST_TYPE(symbol.Info) != elf.STT_FUNC && elf.ST_TYPE(symbol.Info) != elf.STT_OBJECT {
// Not a function.
// TODO: perhaps globals variables should also be included?
continue
}
// Include in archive.

112
builder/picolibc.go

@ -12,7 +12,18 @@ var Picolibc = Library{
name: "picolibc",
cflags: func() []string {
picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc")
return []string{"-Werror", "-Wall", "-std=gnu11", "-D_COMPILING_NEWLIB", "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", picolibcDir + "/include", "-I" + picolibcDir + "/tinystdio", "-I" + goenv.Get("TINYGOROOT") + "/lib/picolibc-include"}
return []string{
"-Werror",
"-Wall",
"-std=gnu11",
"-D_COMPILING_NEWLIB",
"-DHAVE_ALIAS_ATTRIBUTE",
"-DTINY_STDIO",
"-nostdlibinc",
"-Xclang", "-internal-isystem", "-Xclang", picolibcDir + "/include",
"-I" + picolibcDir + "/tinystdio",
"-I" + goenv.Get("TINYGOROOT") + "/lib/picolibc-include",
}
},
sourceDir: "lib/picolibc/newlib/libc",
sources: func(target string) []string {
@ -21,6 +32,105 @@ var Picolibc = Library{
}
var picolibcSources = []string{
"../../../picolibc-stdio.c",
"tinystdio/asprintf.c",
"tinystdio/atod_engine.c",
"tinystdio/atod_ryu.c",
"tinystdio/atof_engine.c",
"tinystdio/atof_ryu.c",
//"tinystdio/atold_engine.c", // have_long_double and not long_double_equals_double
"tinystdio/clearerr.c",
"tinystdio/compare_exchange.c",
"tinystdio/dtoa_data.c",
"tinystdio/dtoa_engine.c",
"tinystdio/dtoa_ryu.c",
"tinystdio/ecvtbuf.c",
"tinystdio/ecvt.c",
"tinystdio/ecvt_data.c",
"tinystdio/ecvtfbuf.c",
"tinystdio/ecvtf.c",
"tinystdio/ecvtf_data.c",
"tinystdio/exchange.c",
//"tinystdio/fclose.c", // posix-io
"tinystdio/fcvtbuf.c",
"tinystdio/fcvt.c",
"tinystdio/fcvtfbuf.c",
"tinystdio/fcvtf.c",
"tinystdio/fdevopen.c",
//"tinystdio/fdopen.c", // posix-io
"tinystdio/feof.c",
"tinystdio/ferror.c",
"tinystdio/fflush.c",
"tinystdio/fgetc.c",
"tinystdio/fgets.c",
"tinystdio/fileno.c",
"tinystdio/filestrget.c",
"tinystdio/filestrputalloc.c",
"tinystdio/filestrput.c",
//"tinystdio/fopen.c", // posix-io
"tinystdio/fprintf.c",
"tinystdio/fputc.c",
"tinystdio/fputs.c",
"tinystdio/fread.c",
"tinystdio/fscanf.c",
"tinystdio/fseek.c",
"tinystdio/ftell.c",
"tinystdio/ftoa_data.c",
"tinystdio/ftoa_engine.c",
"tinystdio/ftoa_ryu.c",
"tinystdio/fwrite.c",
"tinystdio/gcvtbuf.c",
"tinystdio/gcvt.c",
"tinystdio/gcvtfbuf.c",
"tinystdio/gcvtf.c",
"tinystdio/getchar.c",
"tinystdio/gets.c",
"tinystdio/matchcaseprefix.c",
"tinystdio/perror.c",
//"tinystdio/posixiob.c", // posix-io
//"tinystdio/posixio.c", // posix-io
"tinystdio/printf.c",
"tinystdio/putchar.c",
"tinystdio/puts.c",
"tinystdio/ryu_divpow2.c",
"tinystdio/ryu_log10.c",
"tinystdio/ryu_log2pow5.c",
"tinystdio/ryu_pow5bits.c",
"tinystdio/ryu_table.c",
"tinystdio/ryu_umul128.c",
"tinystdio/scanf.c",
"tinystdio/setbuf.c",
"tinystdio/setvbuf.c",
//"tinystdio/sflags.c", // posix-io
"tinystdio/snprintf.c",
"tinystdio/snprintfd.c",
"tinystdio/snprintff.c",
"tinystdio/sprintf.c",
"tinystdio/sprintfd.c",
"tinystdio/sprintff.c",
"tinystdio/sscanf.c",
"tinystdio/strfromd.c",
"tinystdio/strfromf.c",
"tinystdio/strtod.c",
"tinystdio/strtod_l.c",
"tinystdio/strtof.c",
//"tinystdio/strtold.c", // have_long_double and not long_double_equals_double
//"tinystdio/strtold_l.c", // have_long_double and not long_double_equals_double
"tinystdio/ungetc.c",
"tinystdio/vasprintf.c",
"tinystdio/vfiprintf.c",
"tinystdio/vfiscanf.c",
"tinystdio/vfprintf.c",
"tinystdio/vfprintff.c",
"tinystdio/vfscanf.c",
"tinystdio/vfscanff.c",
"tinystdio/vprintf.c",
"tinystdio/vscanf.c",
"tinystdio/vsnprintf.c",
"tinystdio/vsprintf.c",
"tinystdio/vsscanf.c",
"string/bcmp.c",
"string/bcopy.c",
"string/bzero.c",

7
compileopts/config.go

@ -206,7 +206,12 @@ func (c *Config) CFlags() []string {
}
if c.Target.Libc == "picolibc" {
root := goenv.Get("TINYGOROOT")
cflags = append(cflags, "-nostdlibinc", "-Xclang", "-internal-isystem", "-Xclang", filepath.Join(root, "lib", "picolibc", "newlib", "libc", "include"))
picolibcDir := filepath.Join(root, "lib", "picolibc", "newlib", "libc")
cflags = append(cflags,
"-nostdlibinc",
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(picolibcDir, "include"),
"-Xclang", "-internal-isystem", "-Xclang", filepath.Join(picolibcDir, "tinystdio"),
)
cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include"))
}
// Always emit debug information. It is optionally stripped at link time.

2
lib/picolibc

@ -1 +1 @@
Subproject commit 80528c684b10aaee977397e7eb40c4784e6dc433
Subproject commit f68b8204f797d6b3bfbc7c4da4d257961fbc8770

18
lib/picolibc-stdio.c

@ -0,0 +1,18 @@
// This file is included in the picolibc build.
// It makes stdio functions available to the C library.
#include <stdio.h>
#include <sys/cdefs.h>
// Defined in the runtime package. Writes to the default console (usually, the
// first UART or an USB-CDC device).
int runtime_putchar(char, FILE*);
// Define stdin, stdout, and stderr as a single object.
// This object must not reside in ROM.
static FILE __stdio = FDEV_SETUP_STREAM(runtime_putchar, NULL, NULL, _FDEV_SETUP_WRITE);
// Define the underlying structs for stdin, stdout, and stderr.
FILE *const stdin = &__stdio;
__strong_reference(stdin, stdout);
__strong_reference(stdin, stderr);

5
src/runtime/baremetal.go

@ -46,6 +46,11 @@ func libc_free(ptr unsafe.Pointer) {
free(ptr)
}
//export runtime_putchar
func runtime_putchar(c byte) {
putchar(c)
}
//go:linkname syscall_Exit syscall.Exit
func syscall_Exit(code int) {
exit(code)

4
targets/wasm_exec.js

@ -256,6 +256,7 @@
let iov_ptr = iovs_ptr+iovs_i*8; // assuming wasm32
let ptr = mem().getUint32(iov_ptr + 0, true);
let len = mem().getUint32(iov_ptr + 4, true);
nwritten += len;
for (let i=0; i<len; i++) {
let c = mem().getUint8(ptr+i);
if (c == 13) { // CR
@ -276,6 +277,9 @@
mem().setUint32(nwritten_ptr, nwritten, true);
return 0;
},
fd_close: () => 0, // dummy
fd_fdstat_get: () => 0, // dummy
fd_seek: () => 0, // dummy
"proc_exit": (code) => {
if (global.process) {
// Node.js

5
testdata/cgo/main.go

@ -1,6 +1,7 @@
package main
/*
#include <stdio.h>
int fortytwo(void);
#include "main.h"
int mul(int, int);
@ -139,6 +140,10 @@ func main() {
buf2 := make([]byte, len(buf1))
C.strcpy((*C.char)(unsafe.Pointer(&buf2[0])), (*C.char)(unsafe.Pointer(&buf1[0])))
println("copied string:", string(buf2[:C.strlen((*C.char)(unsafe.Pointer(&buf2[0])))]))
// libc: test basic stdio functionality
putsBuf := []byte("line written using C puts\x00")
C.puts((*C.char)(unsafe.Pointer(&putsBuf[0])))
}
func printUnion(union C.joined_t) C.joined_t {

1
testdata/cgo/out.txt

@ -62,3 +62,4 @@ option 3A: 21
enum width matches: true
CFLAGS value: 17
copied string: foobar
line written using C puts

Loading…
Cancel
Save