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.
Этот коммит содержится в:
родитель
1645f45c1a
коммит
14bb90c3c0
11 изменённых файлов: 158 добавлений и 7 удалений
|
@ -15,7 +15,7 @@ RUN cd /tinygo/ && \
|
||||||
git submodule sync && \
|
git submodule sync && \
|
||||||
git submodule update --init --recursive --force
|
git submodule update --init --recursive --force
|
||||||
|
|
||||||
COPY ./lib/picolibc-include/* /tinygo/lib/picolibc-include/
|
COPY ./lib/picolibc-* /tinygo/lib/
|
||||||
|
|
||||||
RUN cd /tinygo/ && \
|
RUN cd /tinygo/ && \
|
||||||
go install /tinygo/
|
go install /tinygo/
|
||||||
|
|
3
Makefile
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/compiler-rt/lib
|
||||||
@mkdir -p build/release/tinygo/lib/nrfx
|
@mkdir -p build/release/tinygo/lib/nrfx
|
||||||
@mkdir -p build/release/tinygo/lib/picolibc/newlib/libc
|
@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/lib/wasi-libc
|
||||||
@mkdir -p build/release/tinygo/pkg/armv6m-unknown-unknown-eabi
|
@mkdir -p build/release/tinygo/pkg/armv6m-unknown-unknown-eabi
|
||||||
@mkdir -p build/release/tinygo/pkg/armv7m-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/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/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/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-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 lib/wasi-libc/sysroot build/release/tinygo/lib/wasi-libc/sysroot
|
||||||
@cp -rp src build/release/tinygo/src
|
@cp -rp src build/release/tinygo/src
|
||||||
@cp -rp targets build/release/tinygo/targets
|
@cp -rp targets build/release/tinygo/targets
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"debug/elf"
|
"debug/elf"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
@ -49,7 +50,7 @@ func makeArchive(archivePath string, objs []string) error {
|
||||||
// Read the symbols and add them to the symbol table.
|
// Read the symbols and add them to the symbol table.
|
||||||
dbg, err := elf.NewFile(objfile)
|
dbg, err := elf.NewFile(objfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("failed to open file %s: %w", objpath, err)
|
||||||
}
|
}
|
||||||
symbols, err := dbg.Symbols()
|
symbols, err := dbg.Symbols()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -61,9 +62,8 @@ func makeArchive(archivePath string, objs []string) error {
|
||||||
// Don't include local symbols (STB_LOCAL).
|
// Don't include local symbols (STB_LOCAL).
|
||||||
continue
|
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.
|
// Not a function.
|
||||||
// TODO: perhaps globals variables should also be included?
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Include in archive.
|
// Include in archive.
|
||||||
|
|
|
@ -12,7 +12,18 @@ var Picolibc = Library{
|
||||||
name: "picolibc",
|
name: "picolibc",
|
||||||
cflags: func() []string {
|
cflags: func() []string {
|
||||||
picolibcDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/picolibc/newlib/libc")
|
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",
|
sourceDir: "lib/picolibc/newlib/libc",
|
||||||
sources: func(target string) []string {
|
sources: func(target string) []string {
|
||||||
|
@ -21,6 +32,105 @@ var Picolibc = Library{
|
||||||
}
|
}
|
||||||
|
|
||||||
var picolibcSources = []string{
|
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/bcmp.c",
|
||||||
"string/bcopy.c",
|
"string/bcopy.c",
|
||||||
"string/bzero.c",
|
"string/bzero.c",
|
||||||
|
|
|
@ -206,7 +206,12 @@ func (c *Config) CFlags() []string {
|
||||||
}
|
}
|
||||||
if c.Target.Libc == "picolibc" {
|
if c.Target.Libc == "picolibc" {
|
||||||
root := goenv.Get("TINYGOROOT")
|
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"))
|
cflags = append(cflags, "-I"+filepath.Join(root, "lib/picolibc-include"))
|
||||||
}
|
}
|
||||||
// Always emit debug information. It is optionally stripped at link time.
|
// Always emit debug information. It is optionally stripped at link time.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 80528c684b10aaee977397e7eb40c4784e6dc433
|
Subproject commit f68b8204f797d6b3bfbc7c4da4d257961fbc8770
|
18
lib/picolibc-stdio.c
Обычный файл
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);
|
|
@ -46,6 +46,11 @@ func libc_free(ptr unsafe.Pointer) {
|
||||||
free(ptr)
|
free(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export runtime_putchar
|
||||||
|
func runtime_putchar(c byte) {
|
||||||
|
putchar(c)
|
||||||
|
}
|
||||||
|
|
||||||
//go:linkname syscall_Exit syscall.Exit
|
//go:linkname syscall_Exit syscall.Exit
|
||||||
func syscall_Exit(code int) {
|
func syscall_Exit(code int) {
|
||||||
exit(code)
|
exit(code)
|
||||||
|
|
|
@ -256,6 +256,7 @@
|
||||||
let iov_ptr = iovs_ptr+iovs_i*8; // assuming wasm32
|
let iov_ptr = iovs_ptr+iovs_i*8; // assuming wasm32
|
||||||
let ptr = mem().getUint32(iov_ptr + 0, true);
|
let ptr = mem().getUint32(iov_ptr + 0, true);
|
||||||
let len = mem().getUint32(iov_ptr + 4, true);
|
let len = mem().getUint32(iov_ptr + 4, true);
|
||||||
|
nwritten += len;
|
||||||
for (let i=0; i<len; i++) {
|
for (let i=0; i<len; i++) {
|
||||||
let c = mem().getUint8(ptr+i);
|
let c = mem().getUint8(ptr+i);
|
||||||
if (c == 13) { // CR
|
if (c == 13) { // CR
|
||||||
|
@ -276,6 +277,9 @@
|
||||||
mem().setUint32(nwritten_ptr, nwritten, true);
|
mem().setUint32(nwritten_ptr, nwritten, true);
|
||||||
return 0;
|
return 0;
|
||||||
},
|
},
|
||||||
|
fd_close: () => 0, // dummy
|
||||||
|
fd_fdstat_get: () => 0, // dummy
|
||||||
|
fd_seek: () => 0, // dummy
|
||||||
"proc_exit": (code) => {
|
"proc_exit": (code) => {
|
||||||
if (global.process) {
|
if (global.process) {
|
||||||
// Node.js
|
// Node.js
|
||||||
|
|
5
testdata/cgo/main.go
предоставленный
5
testdata/cgo/main.go
предоставленный
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
#include <stdio.h>
|
||||||
int fortytwo(void);
|
int fortytwo(void);
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
int mul(int, int);
|
int mul(int, int);
|
||||||
|
@ -139,6 +140,10 @@ func main() {
|
||||||
buf2 := make([]byte, len(buf1))
|
buf2 := make([]byte, len(buf1))
|
||||||
C.strcpy((*C.char)(unsafe.Pointer(&buf2[0])), (*C.char)(unsafe.Pointer(&buf1[0])))
|
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])))]))
|
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 {
|
func printUnion(union C.joined_t) C.joined_t {
|
||||||
|
|
1
testdata/cgo/out.txt
предоставленный
1
testdata/cgo/out.txt
предоставленный
|
@ -62,3 +62,4 @@ option 3A: 21
|
||||||
enum width matches: true
|
enum width matches: true
|
||||||
CFLAGS value: 17
|
CFLAGS value: 17
|
||||||
copied string: foobar
|
copied string: foobar
|
||||||
|
line written using C puts
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче