Skip to content

Commit

Permalink
cgo: add support for printf
Browse files Browse the repository at this point in the history
The C printf function is sometimes needed for C files included using
CGo. This commit makes sure they're available on all systems where CGo
is fully supported (that is, everywhere except on AVR).

For baremetal systems using picolibc, I've picked the integer-only
version of printf to save on flash size. We might want to consider
providing a way to pick the floating point version instead, if needed.
  • Loading branch information
aykevl committed Jan 27, 2024
1 parent f8b1ae0 commit 5252e7a
Show file tree
Hide file tree
Showing 10 changed files with 53 additions and 7 deletions.
2 changes: 2 additions & 0 deletions GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -806,6 +806,7 @@ build/release: tinygo gen-device wasi-libc $(if $(filter 1,$(USE_SYSTEM_BINARYEN
@mkdir -p build/release/tinygo/lib/CMSIS/CMSIS
@mkdir -p build/release/tinygo/lib/macos-minimal-sdk
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-crt/stdio
@mkdir -p build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
@mkdir -p build/release/tinygo/lib/musl/arch
@mkdir -p build/release/tinygo/lib/musl/crt
Expand Down Expand Up @@ -853,6 +854,7 @@ endif
@cp -rp lib/mingw-w64/mingw-w64-crt/def-include build/release/tinygo/lib/mingw-w64/mingw-w64-crt
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/api-ms-win-crt-* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@cp -rp lib/mingw-w64/mingw-w64-crt/lib-common/kernel32.def.in build/release/tinygo/lib/mingw-w64/mingw-w64-crt/lib-common
@cp -rp lib/mingw-w64/mingw-w64-crt/stdio/ucrt_* build/release/tinygo/lib/mingw-w64/mingw-w64-crt/stdio
@cp -rp lib/mingw-w64/mingw-w64-headers/crt/ build/release/tinygo/lib/mingw-w64/mingw-w64-headers
@cp -rp lib/mingw-w64/mingw-w64-headers/defaults/include build/release/tinygo/lib/mingw-w64/mingw-w64-headers/defaults
@cp -rp lib/nrfx/* build/release/tinygo/lib/nrfx
Expand Down
3 changes: 2 additions & 1 deletion builder/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,12 @@ func Build(pkgName, outpath, tmpdir string, config *compileopts.Config) (BuildRe
}
libcDependencies = append(libcDependencies, dummyCompileJob(path))
case "mingw-w64":
_, unlock, err := MinGW.load(config, tmpdir)
job, unlock, err := MinGW.load(config, tmpdir)
if err != nil {
return BuildResult{}, err
}
unlock()
libcDependencies = append(libcDependencies, job)
libcDependencies = append(libcDependencies, makeMinGWExtraLibs(tmpdir, config.GOARCH())...)
case "":
// no library specified, so nothing to do
Expand Down
26 changes: 21 additions & 5 deletions builder/mingw-w64.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,30 @@ var MinGW = Library{
_, err = io.Copy(outf, inf)
return err
},
sourceDir: func() string { return "" }, // unused
sourceDir: func() string { return filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64") },
cflags: func(target, headerPath string) []string {
// No flags necessary because there are no files to compile.
return nil
mingwDir := filepath.Join(goenv.Get("TINYGOROOT"), "lib/mingw-w64")
return []string{
"-nostdlibinc",
"-isystem", mingwDir + "/mingw-w64-headers/crt",
"-I", mingwDir + "/mingw-w64-headers/defaults/include",
"-I" + headerPath,
}
},
librarySources: func(target string) ([]string, error) {
// We only use the UCRT DLL file. No source files necessary.
return nil, nil
// These files are needed so that printf and the like are supported.
sources := []string{
"mingw-w64-crt/stdio/ucrt_fprintf.c",
"mingw-w64-crt/stdio/ucrt_fwprintf.c",
"mingw-w64-crt/stdio/ucrt_printf.c",
"mingw-w64-crt/stdio/ucrt_snprintf.c",
"mingw-w64-crt/stdio/ucrt_sprintf.c",
"mingw-w64-crt/stdio/ucrt_vfprintf.c",
"mingw-w64-crt/stdio/ucrt_vprintf.c",
"mingw-w64-crt/stdio/ucrt_vsnprintf.c",
"mingw-w64-crt/stdio/ucrt_vsprintf.c",
}
return sources, nil
},
}

Expand Down
4 changes: 4 additions & 0 deletions builder/musl.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ var Musl = Library{
"-Wno-ignored-pragmas",
"-Wno-tautological-constant-out-of-range-compare",
"-Wno-deprecated-non-prototype",
"-Wno-format",
"-Wno-parentheses",
"-Qunused-arguments",
// Select include dirs. Don't include standard library includes
// (that would introduce host dependencies and other complications),
Expand Down Expand Up @@ -119,11 +121,13 @@ var Musl = Library{
"internal/syscall_ret.c",
"internal/vdso.c",
"legacy/*.c",
"locale/*.c",
"linux/*.c",
"malloc/*.c",
"malloc/mallocng/*.c",
"mman/*.c",
"math/*.c",
"multibyte/*.c",
"signal/*.c",
"stdio/*.c",
"string/*.c",
Expand Down
1 change: 1 addition & 0 deletions builder/picolibc.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var Picolibc = Library{
"-D_HAVE_ALIAS_ATTRIBUTE",
"-DTINY_STDIO",
"-DPOSIX_IO",
"-DFORMAT_DEFAULT_INTEGER", // use __i_vfprintf and __i_vfscanf by default
"-D_IEEE_LIBM",
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
"-D__OBSOLETE_MATH_DOUBLE=0",
Expand Down
12 changes: 11 additions & 1 deletion stacksize/stacksize.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,17 @@ func CallGraph(f *elf.File, callsIndirectFunction []string) (map[string][]*CallN
if n, ok := symbols[address]; ok {
// Existing symbol.
if n.Size != elfSymbol.Size {
return nil, fmt.Errorf("symbol at 0x%x has inconsistent size (%d for %s and %d for %s)", address, n.Size, n.Names[0], elfSymbol.Size, elfSymbol.Name)
if n.Size != 0 && elfSymbol.Size != 0 {
return nil, fmt.Errorf("symbol at 0x%x has inconsistent size (%d for %s and %d for %s)", address, n.Size, n.Names[0], elfSymbol.Size, elfSymbol.Name)
}
// In some cases (like with --defsym) one of the symbols has size 0.
// In that case, pick the one that's not zero.
if n.Size == 0 {
// elfSymbol.Size must be non-zero, so pick that size.
n.Size = elfSymbol.Size
}
// n.Size is non-zero while elfSymbol.Size is zero, so leave
// as-is.
}
node = n
node.Names = append(node.Names, elfSymbol.Name)
Expand Down
5 changes: 5 additions & 0 deletions testdata/cgo/main.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "main.h"
#include <stdio.h>

int global = 3;
bool globalBool = 1;
Expand Down Expand Up @@ -67,3 +68,7 @@ void unionSetData(short f0, short f1, short f2) {
void arraydecay(int buf1[5], int buf2[3][8], int buf3[4][7][2]) {
// Do nothing.
}

void printf_single_int(char *format, int arg) {
printf(format, arg);
}
4 changes: 4 additions & 0 deletions testdata/cgo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ func main() {
// libc: test basic stdio functionality
putsBuf := []byte("line written using C puts\x00")
C.puts((*C.char)(unsafe.Pointer(&putsBuf[0])))

// libc: test whether printf works in C.
printfBuf := []byte("line written using C printf with value=%d\n\x00")
C.printf_single_int((*C.char)(unsafe.Pointer(&printfBuf[0])), -21)
}

func printUnion(union C.joined_t) C.joined_t {
Expand Down
2 changes: 2 additions & 0 deletions testdata/cgo/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,5 @@ extern int global;
// Test array decaying into a pointer.
typedef int arraydecay_buf3[4][7][2];
void arraydecay(int buf1[5], int buf2[3][8], arraydecay_buf3 buf3);

void printf_single_int(char *format, int arg);
1 change: 1 addition & 0 deletions testdata/cgo/out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,4 @@ len(C.GoStringN(nil, 0)): 0
len(C.GoBytes(nil, 0)): 0
copied string: foobar
line written using C puts
line written using C printf with value=-21

0 comments on commit 5252e7a

Please sign in to comment.